The virtio spec requires the device to leave the reply buffer
untouched and report a used length of zero for an unrecognised
request type, so the driver can tell the request was not handled.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
Domain::mappings only shrinks on UNMAP. Without a bound a guest can
issue MAP for arbitrarily many distinct virt_start values and drive
the VMM heap until the host runs out.
Reject MAP with VIRTIO_IOMMU_S_NOMEM at 1M entries per domain.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
A concurrent DETACH between the read-lock check and the write-lock
get_mut().unwrap() in MAP/UNMAP would panic the worker. Replace the
unwrap with a let-else.
A failure on a later endpoint in the per-endpoint MAP loop, or a
DETACH that races the missing-domain branch, must roll back the
external mappings already installed; otherwise domain.mappings
diverges from VFIO state.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
An UNMAP that would split an existing mapping must be rejected with
VIRTIO_IOMMU_S_RANGE without removing anything. The previous start-only
retain silently left mappings that started outside the unmap range but
overlapped it.
Walk bookkeeping under a read lock and reject before touching VFIO so a
rejection cannot leave VFIO and bookkeeping out of sync.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
The DmaRemapping translate_gva and translate_gpa entry points discarded
the size argument that AccessPlatform's signature already carries and
only checked the base address. A buffer beginning inside a mapping but
extending past it was treated as fully translated, allowing reads or
writes outside the IOMMU-authorized window.
Add `size` to the trait, validate the full span fits in a single
mapping, and propagate it through AccessPlatformMapping.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
The reply length was `hdr_len + size_of::<tail>()`, computed twice.
Make it explicit via checked_add and reuse the result.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
Verify that firing a config change interrupt with msix_config
vector beyond the table size returns Ok without panicking.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that a valid in bounds vector with MSI-X enabled
successfully triggers the interrupt source group.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that requesting a notifier with an out-of-bounds MSI-X
vector returns None instead of panicking.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that triggering an interrupt when the vector is set to
VIRTQ_MSI_NO_VECTOR short-circuits and returns Ok.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Verify that firing an interrupt with a queue vector beyond the
MSI-X table size returns Ok without panicking.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
A malicious or buggy guest can write an out-of-bounds value to
queue_msix_vector or msix_config. When the device later triggers
an interrupt, it indexes into table_entries with the unchecked
vector, causing a panic.
Validate the vector against the MSI-X table size in both trigger()
and notifier() paths, logging a warning and returning early when
the vector exceeds the table bounds.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Follow the same pattern as other virtio devices using a bool to check if
it needs notification and propagating its own Error enum.
Sadly this does still use `anyhow!()` but this does match with the
behaviour of the other devices in their implementations.
As a side effect we can now remove two errors from the top-level Error
enum in virtio-devices as these were only used by this module and those
errors had mangled descriptions.
Signed-off-by: Rob Bradford <rbradford@meta.com>
`get_host_address_range` used `check_range(addr, size)` as a guard then
unwrapped `get_slice(addr, size)`. This allowed a span across two
regions to hit the unwrap (get_slice limits to one range).
If `size` were zero, then the checks were all skipped. Causing a panic
later on for an invalid address.
Make get_slice the sole authority and reject size==0 explicitly.
Callers already handle None.
Signed-off-by: Dylan Reid <dgreid@fb.com>
The guest can cause submit and completion failures with malformed chains
or invalid addresses. However, this shouldn't permanently stall the
device and terminate the worker.
Genuine reset-worthy failures set needs_reset and return `Ok` anyways
and will more cleanly reset the worker.
Signed-off-by: Dylan Reid <dgreid@fb.com>
process_queue_submit's drain loop builds a fresh queue.iter() per
iteration, which re-reads the guest avail index on every call and has
no per-call cap (the per-iter gap check in virtio-queue only protects
against avail_idx jumping more than queue_size between two reads).
In theory, a malicous or buggy guest could keep adding descriptors and
cause this loop to overflow the iouring submit queue.
Cap a single drain at queue_size. A spec-compliant driver never
produces more than queue_size outstanding entries simultaneously, so
the cap is invisible to well-behaved guests.
Signed-off-by: Dylan Reid <dgreid@fb.com>
If querying the fd's MTU fails (because it was from a different network
namespace). Degrade gracefully by not advertising the VIRTIO_NET_F_MTU
feature and instead let the guest kernel use the default 1500 Ethernet
MTU.
Signed-off-by: Rob Bradford <rbradford@meta.com>
The TX path's inline-data branch didn't check the inline buffer length
against the guest-supplied pkt.len() field. The worker will later panic
when it tries to index the packet.
Add the missing check, mirroring the other TX branches.
Signed-off-by: Dylan Reid <dgreid@fb.com>
dma_unmap computed `iova + size - 1` unchecked while the sibling dma_map
already used checked_add/checked_sub. A guest reaching dma_unmap via
VIRTIO_IOMMU_T_UNMAP could cause a panic.
Signed-off-by: Dylan Reid <dgreid@fb.com>
Catch u64 overflows on map so that later translation requests from the
guest don't have a vector for causing a host panic.
Signed-off-by: Dylan Reid <dgreid@fb.com>
prepare_activator() called queue.is_valid() and only logged the failure,
then still pushed the queue to the activator. This would later panic.
Technically this is a fixup for:
a10508970 "virtio-devices: Support driver programming fewer queues"
But that's been in there since 2021. The intent was to allow a subset of
possible queues to be configured, but the invalid queues slipped through
too.
Signed-off-by: Dylan Reid <dgreid@fb.com>
Add support for backend that is connected via the vhost-user-generic
frontend to generate an interrupt into the guest when it has made a
change to the configuration. This is useful for devices that can change
the exposed configuration at runtime.
Assisted-by: Claude:Opus-4.7
Signed-off-by: Rob Bradford <rbradford@meta.com>
The VirtioInterrupt is now always created so the Option<..> can always
be removed.
As a side effect the interrupt_source_group can also be removed from the
struct.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Since this is always created there is no need to make it an Option type
simplifying the code. Historically it was an Option to support INTx
based virtio but that was removed long ago.
Signed-off-by: Rob Bradford <rbradford@meta.com>
msix_num is guaranteed to be at least 1 so this check (and the Option)
that it returns can be removed.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Rather than calculate in the DeviceManager and pass it through do it in
the device where it already has all the required information.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Previously the interrupt was created in VirtioPciDevice, moved via the
Option::take() to the VirtioPciDeviceActivator and then moved to the
VirtioDevice upon activation. On reset it would be moved back ready for
reactivation.
Since this already an Arc type remove the wrapping Option and instead
refcount it such that the VirtioPciDevice can continue to hold onto it
for later activations.
This significantly simplifies the reset() logic as there is no need to
hand back the interrupt.
A few devices used whether the interrupt was Some to make triggering an
interrupt a no-op. However the MSI-X interrupt routing already drops the
interrupt if the driver hasn't yet configured the vector so it is safe
to trigger the interrupt before device activation (e.g. balloon resize
request before driver loaded).
VirtioCommon still retains an Option<..> for the interrupt as the
interrupt is not known until activation time (after this has been
created). A helper VirtioCommon::trigger_interrupt() has been added to
handle this.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Per the virtio spec a device reset must return the device to its
power-on state. The reset path was only zeroing queue_select. Add
VirtioPciCommonConfig::reset() and call it from the transport's reset
path so the configuration is cleared. Also relax the condition to allow
the device to be reset at any time to match the spec.
Signed-off-by: Rob Bradford <rbradford@meta.com>
The virtio specification treats reset as the recovery operation and so
must take the device back to a fresh state, and the driver waits for the
status read-back to converge before continuing. There is no defined way
for the device to report a reset failure to the driver.
Previously the implementations of reset() would return early and not
complete all their cleanup leaving them in an inconsistent state. Now
log errors and continue through the execution.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
The four vhost-user device wrappers (blk, fs, generic_vhost_user, net)
each carried an identical reset() body that resumed the worker thread,
asked the backend to reset, signalled kill_evt and dropped interrupt_cb.
Move the shared body into VhostUserCommon::reset() so behaviour stays in
one place.
No behavioural change.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
release_memory_range took (range_base, range_len) verbatim from a
guest-controlled descriptor (free-page-reporting and inflate paths) and
called fallocate(PUNCH_HOLE | KEEP_SIZE) on the backing file before any
length check ran. find_region only validates that range_base lands
inside *some* region; range_len can extend past the region's end. When
the operator uses --memory-zone file=PATH against a host file larger
than the zone, the punch zeroes host file content past the guest's
memory extent.
Free-page reporting is advisory, so the VMM is allowed to act on a
subset of a free range. Clamp range_len to the bytes that actually fit
within the region returned by find_region. Going past the end of a
region is most likely a guest bug so log it.
Signed-off-by: Dylan Reid <dgreid@fb.com>
All disk format backends now implement AsyncFullDiskFile directly.
The DiskBackend enum that dispatched between Legacy and Next arms
is no longer needed since the factory returns trait objects and vmm
no longer constructs format types manually.
Replace DiskBackend with Box<dyn AsyncFullDiskFile> in the Block
struct and its constructor. Remove the DiskBackend::Next wrapping
in device_manager and the fuzz target.
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
There is no reason for most of the Request struct to be writable from
anywhere in the codebase. Encapsulate it.
Use getter functions for access outside the request module. Replace the
trivial setter for the writeback field with direct assignment.
No functional change intended.
Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
The new_ prefix in Rust conventionally denotes constructors that return
Self (e.g. Vec::new(), File::new()). AsyncDiskFile::new_async_io does
not return Self. It is a factory method that constructs and returns a
Box<dyn AsyncIo> worker bound to the disk file descriptor and
metadata. The create_ prefix communicates this: the caller receives
a freshly constructed object of a different type.
This rename touches every format backend in block plus two external
callers in virtio-devices and performance-metrics. Every change is a
mechanical s/new_async_io/create_async_io/ substitution. No functional
change.
Ref: #7877 (task 3.2.8)
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
The driver needs to be notified with the number of bytes written by the
device. Ensure that the correct number of bytes is reported.
Signed-off-by: Rob Bradford <rbradford@meta.com>
The virtio spec allows a chain of writable descriptors however the rng
device was assuming just a single writable descriptor. Instead fill in
all writable descriptors. There is no status byte (unlike e.g. block)
and instead 0 bytes used is used to indicate error.
Signed-off-by: Rob Bradford <rbradford@meta.com>
The virtio spec requires that a status response is always written on
error. This was missing from the path where we had a valid request but
not for one we support.
Signed-off-by: Rob Bradford <rbradford@meta.com>
The virtio spec allows the use of larger descriptors (for future
expansion). Relax the bounds check to only reject descriptors that are
too small.
Signed-off-by: Rob Bradford <rbradford@meta.com>
The virtio spec allows the use of larger descriptors (for future
expansion). Relax the bounds check to only reject descriptors that are
too small.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Following the pattern used by the existing virtio devices make the
balloon device work with confidential VMs (e.g. SEV-SNP). This requires
advertising the VIRTIO_F_ACCESS_PLATFORM feature. Do not expose this to
the user as a controllable option and instead only enable in on the
"force" case.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Rename from iommu to access_platform_enabled. The original name was
iommu as this feature was exposed for devices behind an IOMMU however
this feature is also now used for confidential VMs so adopt a more
general name.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Replace the stored AccessPlatform reference with one to the
VirtioDevice. By doing this not only does it allow the code to be
simplified but also now makes it virtio spec compliant by only
translating via the access platform if the feature is acknowledged.
Signed-off-by: Rob Bradford <rbradford@meta.com>
This forwards through to the VirtioCommon implementation and can be used
to simplify the virtio PCI access code.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Adding this method to the trait will allow the virtio PCI code to access
a feature conditional version of the access platform and simplify the
logic.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Use the new virtio feature gated accessor when creating the handlers for
the virtio devices. This now means that the translations via the
accessor will only be applied if the feature is acked in accordance with
the spec.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Add VirtioCommon::access_platform() method. The virtio spec requires
that only if the feature is acked should the accesses be transformed via
the access platform implementation. This will enable that filtering.
Signed-off-by: Rob Bradford <rbradford@meta.com>
Previously, calling pause() when already paused would wait on a barrier
for worker threads that were already parked, causing a deadlock.
This situation occurs when the VMM thread holds a device mutex while
calling an operation that triggers pause(), and a vCPU thread
simultaneously needs that same mutex for MMIO access. With slow I/O
backends (like RBD/Ceph), the timing window for this race is larger,
making the deadlock more likely to occur, see [0].
Make pause() idempotent by checking the paused state atomically and
returning early if already paused, avoiding the barrier wait.
[0] https://github.com/cloud-hypervisor/cloud-hypervisor/pull/7948#discussion_r305052509
Signed-off-by: Vincent Thomas <vincent@v-thomas.com>
Make it easier to chase down which vhost user socket failed and why in
systems that have many vhost user devices.
Signed-off-by: Dylan Reid <dgreid@fb.com>