Skip to content

Commit 2bddc7a

Browse files
committed
Announcement: Preloaded disposables
1 parent af325a6 commit 2bddc7a

1 file changed

Lines changed: 222 additions & 0 deletions

File tree

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
---
2+
layout: post
3+
title: "Qubes Architecture Next Steps: Preloaded diposables"
4+
categories: articles
5+
author: Benjamin Grande
6+
---
7+
8+
<!--
9+
TODO: reviewer: when posting, rename this file with correct date.
10+
TODO: reviewer: when posting, rewrite the paragraph below:
11+
12+
This is the second article in the "What's new in Qubes 4.3?" series. You
13+
can find the previous one (about GUI Domains)
14+
[here](/news/2020/03/18/gui-domain/). While the
15+
introduction of GUI domains is a big, singular feature, the changes to
16+
qrexec are more complex and varied --- but also very important.
17+
-->
18+
19+
## What are disposables?
20+
21+
A [disposable](/doc/how-to-use-disposables/) is a lightweight qube that can be
22+
created quickly and will self-destruct when closed. Disposables are usually
23+
created in order to host and execute untrusted code, be it on the software
24+
level of single application (like a viewer, editor or web browser) or the
25+
hardware level (e.g., for [PCI passtrough](/doc/how-to-use-pci-devices/)).
26+
27+
There are two kinds of disposables, unnamed and named. The difference between
28+
them (besides one having a fixed name) is that unnamed disposables are
29+
destroyed when closing the first application opened in them while the user
30+
must explicitly shut down named disposables.
31+
32+
## Fresh disposable startup time leads to fatigue
33+
34+
One of the most secure ways to segregate duties is to use a fresh disposable
35+
for every new task. Unnamed disposables are ideal for this use case. So, what
36+
is the problem with them? The caller has to wait for a complete qube startup
37+
before running the desired application. The delay might seem a minor annoyance
38+
at first, but over a prolonged period, fatigued users tend to reuse already
39+
tainted disposables or run applications in non-disposable qubes to avoid the
40+
waiting time.
41+
42+
The problem is not the user's lack of understanding or a lack of documentation
43+
but how the user perceives the system. If the system is slow, many users will
44+
circumvent that slowness even if it means compromising their security, because
45+
they just want to get things done.
46+
47+
The slowdown is aggravated when requesting a high number of disposables
48+
sequentially, which happens constantly when using the Qubes Executor to
49+
[build QubesOS](/doc/qubes-builder-v2/).
50+
51+
A secure workflow should not be a burden. Can Qubes OS do better?
52+
53+
## What are preloaded disposables?
54+
55+
Yes! It can do better.
56+
57+
Qubes OS project lead Marek Marczykowski-Górecki, in his
58+
[Qubes OS development status update](https://cfp.3mdeb.com/qubes-os-summit-2024/talk/AWCBJ8/)
59+
at Qubes OS Summit 2024, mentioned plans for faster disposables in Qubes OS
60+
4.3. Here is where preloaded disposables enters.
61+
62+
Preloaded disposables are unnamed disposables started in the background and
63+
kept hidden from the user when not in use. They are interrupted (paused or
64+
suspended, as appropriate) and resumed (transparently) when a disposable qube
65+
is requested by the user.
66+
67+
When the qube is preloaded, the qube application listing and the qube entry
68+
itself are hidden from GUI applications such as the app menu and the Qrexec
69+
Policy Ask prompt. This is by design to avoid contamination. A preload is not
70+
something intended to be used by finding them on the system and clicking to
71+
run applications on them, but indirectly, as will be explained in the next
72+
paragraph.
73+
74+
The use of preloaded disposables is transparent, indistinguishable from the
75+
use of unnamed disposables. Requesting the creation of a new unnamed
76+
disposable will instead mark a preload as used and reply with an
77+
already-running preloaded disposable, followed by the creation of a
78+
substitute. A preload that is marked as used ceases to be a preload. Its
79+
applications entries become visible in GUI applications the same way as
80+
standard unnamed disposables.
81+
82+
Usage of disposables that have completed the preloading stage is almost as
83+
fast as executing code on an already-running qube, just a bit slower because
84+
it has to unpause the qube as well as start the GUI agent. While unpause can
85+
take just a few milliseconds, waiting for the GUI agent can take a second. The
86+
remaining time is overhead of operations across [domains](/doc/qrexec/).
87+
88+
In case of a failure to preload for any reason, such as lack of available
89+
memory or the daemon responsible for managing qubes (`qubesd`) having stopped,
90+
no user interaction is needed. The system will fill in gaps of missing
91+
preloaded disposables while removing incomplete ones.
92+
93+
## How to use preloaded disposables
94+
95+
Preloading is a "set and forget" operation. Configure it once, setting the
96+
desired maximum number of preloaded qubes you would like to have preloaded,
97+
and the system takes care of the rest, including filling in the gaps of used
98+
preloaded qubes.
99+
100+
The only action required to preload a disposable is to set the feature
101+
`preload-dispvm-max` on the disposable template you most use for generating
102+
unnamed disposables. Consider a user that does a lot of management operations
103+
with `qvm-console-dispvm`. The default qube for those operations is
104+
`default-mgmt-dvm`:
105+
106+
[![VM Settings](/attachment/posts/preload-local.png)](/attachment/posts/preload-local.png)
107+
108+
Or use the equivalent command-line operation:
109+
110+
```shell
111+
qvm-features default-mgmt-dvm preload-dispvm-max 2
112+
```
113+
114+
If you use the global `default_dispvm` a lot, you can target the global
115+
preload setting by setting the feature on `dom0`:
116+
117+
[![Global settings](/attachment/posts/preload-global.png)](/attachment/posts/preload-global.png)
118+
119+
Or use the equivalent command-line operation:
120+
121+
```shell
122+
qvm-features dom0 preload-dispvm-max 2
123+
```
124+
125+
While the disposable template remains the global `default_dispvm`, it will
126+
respect the global feature and ignore the per-qube setting.
127+
128+
To remove all the preloaded qubes, set the feature value to `0`, `''` (empty)
129+
or delete the feature by unchecking the box. If the feature is set on `dom0`,
130+
it's better to delete the feature so that the preload setting to be read from
131+
the disposable template itself and not capped to `0`.
132+
133+
```shell
134+
qvm-features --delete dom0 preload-dispvm-max
135+
```
136+
137+
The limit on the number of preloaded disposables you can configure depends on
138+
how much your system can handle. If there is not enough available memory to
139+
preload a disposable, it is skipped until another distinct event triggers it.
140+
However, GUI applications that allow preload configuration have a hard limit
141+
of `50`, which should be more than enough for most use cases.
142+
143+
The second spin button refers to the amount of free memory that you don't want
144+
preloaded disposables to use. Setting a value of 1000 MiB won't preload
145+
disposables if your system only has that amount of free memory at that time.
146+
At a later time, it will retry and preload gap will be filled if there is
147+
enough memory. On a fresh install, this value is already set according to a
148+
percentage of your system's total memory. A value equal or over 1000 MiB is
149+
recommended.
150+
151+
## Performance improvements
152+
153+
The following tests were conducted on a system with:
154+
155+
- Qubes: R4.3-rc2
156+
- Xen: 4.19.3
157+
- Linux: 6.15.11-1.fc41
158+
- Template: Fedora 42 Xfce (build time: 2025-08-19 23:42:23)
159+
- Memory: 32GB RAM
160+
- CPU: Intel(R) Core(TM) Ultra 7 155H
161+
- Drive: PX700 NVMe SSD
162+
163+
### Stacked execution
164+
165+
The average execution time is very low, even considering a great amount of
166+
consecutive iterations. The GUI execution is unfortunately higher because the
167+
GUI daemon only starts the connection with the qube's GUI agent when the
168+
preload disposable is marked as used.
169+
170+
[![Stacked execution](/attachment/posts/preload-graph_01_stage_stack_0.png)](/attachment/posts/preload-graph_01_stack_stack_0.png)
171+
172+
### Stacked total
173+
174+
Notice that execution time is not all that counts, disposables are destroyed
175+
after use, this time has to be considered to measure the call performance.
176+
Notice that preloaded disposables cleanup are higher than their normal
177+
variants, this is due to a new preload being created while the current one was
178+
marked as used.
179+
180+
[![Stacked total](/attachment/posts/preload-graph_01_stage_stack_1.png)](/attachment/posts/preload-graph_01_stack_stack_1.png)
181+
182+
### Execution per iteration
183+
184+
When analyzing the execution time per iteration, the first request seems to be
185+
stable, at around `0.3` seconds (`300 milliseconds`). Some prominent spikes on
186+
iterations 9-11 happened because there were no preloaded disposables ready to
187+
fulfill the large amount of requests. Nevertheless, it was still faster than
188+
normal disposables. If your workflow doesn't require a large amount of
189+
preloaded disposables in a short period, 1-3 preloaded disposables might be
190+
enough (the graph doesn't show 1-2 preloaded because it would have too many
191+
overlapping lines).
192+
193+
[![Line execution](/attachment/posts/preload-graph_03_line_0_exec.png)](/attachment/posts/preload-graph_03_line_0_exec.png)
194+
195+
### Total per iteration
196+
197+
At last, there are no occurrences of preloaded disposables underperforming
198+
normal disposables.
199+
200+
[![Line total](/attachment/posts/preload-graph_03_line_2_total.png)](/attachment/posts/preload-graph_03_line_2_total.png)
201+
202+
## Known issues
203+
204+
Preloaded disposables are paused when they finish preloading and haven't been
205+
requested for use yet. Paused qubes still consume allocated memory, and this
206+
stale state does not allow for [memory redistribution](/doc/qmemman/).
207+
208+
Preloading multiple disposables works even on systems with as low as 8 GB of
209+
RAM, the experience however, may not be the best when there's little available
210+
memory. Users with machines that constantly have a lot of unused memory won't
211+
notice a difference.
212+
213+
The GUI connection to preloaded disposables is delayed until they are used for
214+
various reasons such as:
215+
216+
- Preloads can be started and paused before the GUI session starts;
217+
- Autostarted applications won't display as they would become unresponsive
218+
when the qube suddenly pauses.
219+
220+
Ideally, the GUI connection would not be delayed, but connected during the
221+
preloading phase with a mode to not display windows until the qube is marked
222+
as used. That is planned as a future improvement.

0 commit comments

Comments
 (0)