qubes.vm.dispvm – Disposable VM

A disposable qube implementation

class qubes.vm.dispvm.DispVM(app, xml, *args, **kwargs)[source]

Bases: QubesVM

Disposable qube.

Disposable behavior

A disposable template is a qube which has the AppVM class and the template_for_dispvms property enabled, being a DVMTemplateMixin.

A disposable is a qube with the DispVM class and is based on a disposable template. Every disposable type has all of its volumes configured to disable save_on_stop, therefore no changes are saved on shutdown. Unnamed disposables enables the property auto_cleanup by default, thus automatically removes the qube upon shutdown.

Named disposables are useful for service qubes, as referencing static names is easier when the qube name is mentioned on Qrexec policies (qubes.UpdatesProxy target) or as a property of another qube, such as a disposable net qube which is referenced by downstream clients in the netvm property.

Unnamed disposables have their names in the format disp1234, where 1234 is derived from the dispid property, a random integer ranging from 0 to 9999 with a fail-safe mechanism to avoid reusing the same value in a short period.

The system and every qube can have the default_dispvm property. If the qube property is set to the default value, it will use the system’s property. This property can only have disposable template as value or an empty value. Qubes which have this property set are allowed to request the creation of a disposable from this property. An exception to the rule is the property of disposables, which always default to their disposables templates to avoid data leaks such as using unintended network paths.

There are some Qrexec services that which allows execution to disposables created from the default_dispvm property when the destination qube of the Qrexec field uses the @dispvm tag, most commonly used to open files and URLs, (qubes.OpenInVM and qubes.OpenURL, respectively).

Preload queue

Preloaded disposables are started in the background and kept hidden from the user when not in use. They are interrupted (paused or suspended, as appropriate) and resumed (transparently) when a disposable qube is requested by the user.

Goals:

  • Fast: Usage must be always instantaneous from user perspective when requesting the use of disposables. Pause/suspend must be skipped if qube is requested before the interrupt can be performed.

  • Easy-to-use: Preloading requires a single qube feature (preload-dispvm-max), and its use must be transparent, indistinguishable from working with normal (non-preloaded) unnamed disposable qubes.

  • Reliable:

    • Avoid race conditions: Marking a qube as preloaded or marking the preloaded as used must be synchronous.

    • Recovery from failed or incomplete preload: The system must attempt to preload qubes even if previous preloading attempts failed due to errors, qubesd restart or lack of available memory, regardless of whether preloaded disposable qubes have been requested on this instance. If current qube list is invalid, it must be cleaned up before being used.

    • Avoid copy of invalid attributes: Qube operation (in particular cloning, renaming or creating a standalone based on a template) must not result in properties that are invalid on the target.

    • Full start: Preloaded disposable must only be interrupted (paused/suspended) or used after all basic services in it have been started. Failure to complete this step must remove the qube from the preload list.

  • Prevents accidental tampering:

    • Preloaded qubes have the internal feature set when they are created. This feature hides the qube from GUI tools and discourages user tampering. It is unset when the qube is marked as used. Remember to validate if all GUI applications correctly react to setting and removing the internal feature (optionally, the is_preload property can be helpful). GUI applications may react to domain-add before the internal feature is set and the qube entry may briefly appear on some GUI applications, that is a bug because features cannot be set before that event.

    • Preloaded qubes must be marked as used after being unpaused/resumed, even if it was not requested. The goal of pause/suspend in case of preloaded disposables is mostly detecting whether a qube was used or not, not managing resource consumption; thus, even with abundant system resources, they should not be unpaused/resumed without being requested.

Features and properties relationship on stages:

  • Properties indicate the runtime stage of preloaded qubes and intentionally lost on qubesd restart.

  • Features indicate that a preloaded qube has reached certain stage at any qubesd cycle.

  • Comparing the value of certain features and properties can indicate that there were qubes being preloaded or requested but qubesd restarted between the stages, interrupting the process. The only stage that should conserve the preloaded qubes is a qubes that has completed preloading but has not been requested.

Stages:

  • Preload: The qube is created and marked as preloaded. Qube is not visible in GUI applications.

    • Startup: Begins qube startup, start basic services in it and attempt to interrupt (suspend/pause).

    • Request: The qube is removed from the preload list. If startup has not yet reached interrupt, the latter is skipped.

  • Used: The qube is marked as used and may be unpaused/resumed (if applicable). Only in this phase, GUI applications treat the qube as any other unnamed disposable and the qube object is returned to the caller if requested.

classmethod can_gen_disposable(appvm, preload=False) bool[source]

Check if app qube can be used to generate a disposable.

Return type:

bool

async classmethod from_appvm(appvm, preload=False, **kwargs) DispVM | None[source]

Use a preloaded disposable if available, else fallback to creating a new disposable instance from given app qube.

Parameters:
  • appvm (qubes.vm.appvm.AppVM) – template from which the qube should be created

  • preload (bool) – Whether to preload a disposable

Returns:

new disposable qube

Return type:

qubes.vm.dispvm.DispVM

kwargs are passed to the newly created disposable.

>>> import qubes.vm.dispvm.DispVM
>>> dispvm = qubes.vm.dispvm.DispVM.from_appvm(appvm).start()
>>> dispvm.run_service('qubes.VMShell', input='firefox')
>>> dispvm.cleanup()

This method modifies qubes.xml file.

async classmethod gen_disposable(appvm, preload=False, **kwargs) DispVM[source]

Create a new disposable instance from a given app qube. If preload is truthy, the qube is started.

Parameters:
  • appvm (qubes.vm.appvm.AppVM) – template from which the qube should be created

  • preload (bool) – Whether to preload a disposable

Return type:

qubes.vm.dispvm.DispVM

async cleanup(force: bool = False) None[source]

Clean up after the disposable.

This stops the disposable qube and removes it from the store. This method modifies qubes.xml file.

Parameters:

force (bool) – Auto clean up if property is enabled and domain is not running, should be used in special circumstances only as the sole purpose of this option is because using it may not be reliable.

create_qdb_entries() None[source]

Create entries in Qubes DB.

async get_preload() bool[source]

Get preloaded disposable.

Return type:

bool

is_preload_outdated() dict[source]

Show properties that differ on disposable compared to its template.

Return type:

dict

mark_preload() None[source]

Mark disposable as a preload.

mark_preload_requested() None[source]

Mark preloaded disposable as requested.

on_domain_loaded(event) None[source]

When qube is loaded, assert that this qube has a template.

on_domain_paused(event, **kwargs)[source]

On pause, log if it is a preloaded disposable.

Parameters:

event (str) – Event which was fired.

async on_domain_pre_paused(event, **kwargs) None[source]

Before the qube is paused, if the qube is a preloaded disposable that has memory balancing enabled, attempt to set it’s memory to its preferred memory configuration, which is just enough to get the qube working at that time.

This helps preloaded disposables to be paused with just enough memory.

Parameters:

event (str) – Event which was fired.

on_domain_remove_from_disk(_event, **_kwargs) None[source]

On volume removal, remove preloaded disposable from preload-dispvm feature in disposable template. If the feature is still here, it means the domain-shutdown cleanup was bypassed, possibly by improper shutdown, which can happen when a disposable is running, qubesd stops and system reboots.

async on_domain_shutdown(_event, **_kwargs) None[source]

Do auto cleanup if enabled.

async on_domain_started_dispvm(event, **kwargs)[source]

When starting a qube, await for basic services to be started on preloaded disposables and interrupts the domain if the qube has not been requested yet.

Parameters:

event (str) – Event which was fired.

async on_domain_unpaused(event, **kwargs)[source]

When qube is unpaused, mark preloaded disposables as used.

Parameters:

event (str) – Event which was fired.

on_property_pre_reset_template(event, name, oldvalue=None) None[source]

Forbid deleting template of qube.

Parameters:
on_property_pre_set_template(event, name, newvalue, oldvalue=None)[source]

Forbid changing template of running qube.

Parameters:
on_property_set_template(event, name, newvalue, oldvalue=None) None[source]

Adjust root (and possibly other snap_on_start=True) volume on template change.

Parameters:
async start(**kwargs)[source]

Start disposable qube, but if it fails, make sure to clean it up.

async use_preload() None[source]

Marks preloaded disposable as used (tainted), delete the internal when appropriate, making GUI applications show the qube as any other disposable. Start the preload cycle to fill gaps.

async wait_operational_preload(service: str, timeout: int | float) None[source]

Await for preloaded disposable to become fully operational.

Parameters:
  • service (str) – Full command-line.

  • timeout (int|float) – Fail after timeout is reached.

auto_cleanup

automatically remove this qube upon shutdown

default_dispvm

Default disposable template to be used for spawning disposable qubes for service calls.

dispid

Internal, persistent identifier of particular disposable.

include_in_backups

If this domain is to be included in default backup.

is_preload

Check if qube is a preloaded disposable.

Return type:

bool

property preload_requested: bool

Check if preloaded disposable was requested and still is preloaded (not used yet).

This property exists because the qube may still be a preloaded disposable and not be on the preload-dispvm feature of the disposable template. This offload is done to avoid race conditions:

  • Decreasing preload-dispvm-max feature will not remove the qube;

  • Another request to this function will not return the same qube.

Return type:

bool

template

AppVM, on which this disposable is based.

qubes.vm.dispvm.get_preload_max(qube) int | None[source]

Get the preload-dispvm-max feature as an integer.

Parameters:

qube (qubes.vm.qubes.QubesVM) – Qube to query the feature from.

Return type:

int | None

qubes.vm.dispvm.get_preload_templates(app) list[source]

Get all disposable templates that have the preload-dispvm-max feature greater than 0, either directly or indirectly by being the global default_dispvm and dom0 has the feature enabled.

Parameters:

app (qubes.app.Qubes) – Qubes application.

Return type:

list