ANT-2026-PQ3CNCCK · firecracker-microvm/firecracker

oob-read high

CVE-2026-5747 GHSA-776c-mpj7-jm3r

Severity Claude high · Security research firm - · Maintainer -

Discovered by Claude Mythos Preview

REPORT

Anthropic's analysis of this finding, sealed at approval.

ANT-2026-PQ3CNCCK: Virtio-PCI queue size writable after activation enables host OOB

In Firecracker's virtio-PCI transport, a 2-byte BAR write to offset 0x18 reaches self.with_queue_mut(queues, |q| q.size = value) at common_config.rs:202 with no check on device status, unlike the MMIO transport which gates the same write behind DRIVER_OK (mmio.rs:130-142). Queue::initialize() validates size and caches raw host pointers (desc_table_ptr/avail_ring_ptr/used_ring_ptr) bounds-checked only for the validated size, but the hot path later indexes those cached pointers using the live self.size inside unsafe blocks (queue.rs:392-398, 417-426, 445-456, 541-546, 574-582). A malicious guest can enlarge size to 0xFFFF after DRIVER_OK, yielding guest-controlled-index 2-byte reads and 2/8-byte writes up to ~512 KiB past the validated guest-memory mapping into host VMM process memory. This is a sliding OOB write primitive usable as a building block for guest-to-host escape, or at minimum reliable host DoS.

Target

Project: firecracker-microvm/firecracker
Location: src/vmm/src/devices/virtio/transport/pci/common_config.rs:202
Discovery: static analysis — not yet dynamically reproduced

Technical Details

The PCI common-config write path assigns guest-supplied queue_size directly (q.size = value) without gating on activation/DRIVER_OK and without re-running Queue::initialize(), so the safety invariants of the cached raw pointers derived at activation time are broken. Subsequent unsafe pointer arithmetic such as used_ring_ptr.add(size_of::<u16>()*2 + size_of::<UsedElement>()*size) uses the new, unvalidated size against stale pointers whose backing slice was bounds-checked only for the original size, producing host-process OOB reads and writes. Queue ring addresses (offsets 0x20-0x34) and queue_enable (0x1c) are similarly ungated.

Reproduction

  1. Operator starts Firecracker with --enable-pci and a virtio-pci device (e.g. virtio-net).
  2. Guest negotiates the device with minimum queue size and places the used ring at the highest valid guest-physical page so the validated mapping ends at the top of the GuestMemoryMmap region.
  3. Guest sets DRIVER_OK; Queue::initialize() caches used_ring_ptr for the validated size.
  4. Guest performs a 2-byte write to BAR0 at COMMON_CONFIG_BAR_OFFSET+0x18 with value 0xFFFF, silently overwriting q.size.
  5. Guest kicks the queue / manipulates avail idx so the VMM runs used_ring_used_element_set() and used_ring_avail_event_set(), whose unsafe .add() offsets now land hundreds of KiB past the guest-memory mmap, writing an 8-byte UsedElement and a 2-byte avail_event at attacker-influenced offsets; repeat with varying next_used for a sliding OOB write primitive.

[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]

Suggested Fix

Make queue geometry (size, ring addresses, enable) immutable from the guest once the device is activated / DRIVER_OK is set; reject post-activation writes or force full queue re-validation and re-derivation of cached raw pointers before they are used again.

Acknowledgement

This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-PQ3CNCCK.


Reference: ANT-2026-PQ3CNCCK
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure

ADVISORY

https://github.com/firecracker-microvm/firecracker/security/advisories/GHSA-776c-mpj7-jm3r

TIMELINE

Dates from discovery through public reveal.

  1. 2026-03-31 Sent to maintainer
  2. 2026-03-31 Maintainer acknowledged
  3. 2026-04-06 Patch released
  4. 2026-04-15 Reported to tracker
  5. 2026-08-17 Publicly revealed
PROVENANCE

SHA-3-512 hash:

97cd06851823987565a3f40bce0a69f65bdf9fe68e2ab7303ee3b5d58780e4e5a65f21cd44d70f53e4d97898416ddc420f50c71d4bb4164bedef94b9e5f03077

Committed 2026-03-31 17:00 PT

Revealed 2026-08-17 16:50 PT

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-PQ3CNCCK",
  "bug_class": "Memory Safety / Out-of-Bounds Read+Write (unsafe Rust)",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-04-16T02:27:08+00:00",
  "description": "In Firecracker's virtio-PCI transport, a 2-byte BAR write to offset 0x18 reaches `self.with_queue_mut(queues, |q| q.size = value)` at common_config.rs:202 with no check on device status, unlike the MMIO transport which gates the same write behind DRIVER_OK (mmio.rs:130-142). Queue::initialize() validates size and caches raw host pointers (desc_table_ptr/avail_ring_ptr/used_ring_ptr) bounds-checked only for the validated size, but the hot path later indexes those cached pointers using the live `self.size` inside unsafe blocks (queue.rs:392-398, 417-426, 445-456, 541-546, 574-582). A malicious guest can enlarge size to 0xFFFF after DRIVER_OK, yielding guest-controlled-index 2-byte reads and 2/8-byte writes up to ~512 KiB past the validated guest-memory mapping into host VMM process memory. This is a sliding OOB write primitive usable as a building block for guest-to-host escape, or at minimum reliable host DoS.",
  "discovered_at": "2026-04-02T00:00:00+00:00",
  "location": "src/vmm/src/devices/virtio/transport/pci/common_config.rs:202",
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "firecracker-microvm/firecracker",
  "reproduction": [
    "1. Operator starts Firecracker with --enable-pci and a virtio-pci device (e.g. virtio-net).",
    "2. Guest negotiates the device with minimum queue size and places the used ring at the highest valid guest-physical page so the validated mapping ends at the top of the GuestMemoryMmap region.",
    "3. Guest sets DRIVER_OK; Queue::initialize() caches used_ring_ptr for the validated size.",
    "4. Guest performs a 2-byte write to BAR0 at COMMON_CONFIG_BAR_OFFSET+0x18 with value 0xFFFF, silently overwriting q.size.",
    "5. Guest kicks the queue / manipulates avail idx so the VMM runs used_ring_used_element_set() and used_ring_avail_event_set(), whose unsafe .add() offsets now land hundreds of KiB past the guest-memory mmap, writing an 8-byte UsedElement and a 2-byte avail_event at attacker-influenced offsets; repeat with varying next_used for a sliding OOB write primitive."
  ],
  "technical_details": "The PCI common-config write path assigns guest-supplied queue_size directly (`q.size = value`) without gating on activation/DRIVER_OK and without re-running Queue::initialize(), so the safety invariants of the cached raw pointers derived at activation time are broken. Subsequent unsafe pointer arithmetic such as `used_ring_ptr.add(size_of::<u16>()*2 + size_of::<UsedElement>()*size)` uses the new, unvalidated size against stale pointers whose backing slice was bounds-checked only for the original size, producing host-process OOB reads and writes. Queue ring addresses (offsets 0x20-0x34) and queue_enable (0x1c) are similarly ungated.",
  "title": "Virtio-PCI queue size writable after activation enables host OOB",
  "vendor_severity": null
}