VAPIC writable ROM alias can escape the option-ROM window and expose locked SMRAM

Automated-tool disclosure: AI-assisted source analysis helped identify this issue. I manually reviewed the affected source, validated the trigger and resulting mapping, and practically demonstrated that it can supply a capability in a guest-to-host escape chain. The attached reproducer is human-reviewed source code and does not exercise the full escape chain.

Classification note: I am not sure whether this standalone issue qualifies as a security issue under QEMU's published guidelines. It crosses the guest-kernel-to-SMM isolation boundary, and Q35 with a virtualization accelerator is a supported configuration, but the guidelines also treat some virtual-device misbehavior requiring guest kernel or root privileges as a hardening bug. I have therefore prepared this as a confidential report so the QEMU security team can make that determination. The confidential marking is not intended to assert that the standalone bug qualifies for security handling.

Host environment

  • Operating system: Debian GNU/Linux 12 (bookworm)
  • OS/kernel version: Linux 6.1.0-51-amd64, Debian 6.1.177-1 (2026-07-16)
  • Architecture: x86_64
  • QEMU flavor: qemu-system-x86_64
  • QEMU version: 11.0.92 (v11.1.0-rc2-9-gb428fe0362), commit b428fe03
  • QEMU command line:
    /home/artem/qemuctf/patched-stack/qemu/bin/qemu-system-x86_64 \
      -name debian-12-qemu \
      -machine q35,accel=kvm:tcg \
      -cpu max \
      -smp 4 \
      -m 8G \
      -drive file=/home/artem/vm/debian-12-qemu/disk.qcow2,if=virtio,format=qcow2 \
      -drive file=/home/artem/vm/debian-12-qemu/seed.iso,if=virtio,format=raw,readonly=on \
      -nic user,model=virtio-net-pci,mac=52:54:00:12:34:56,hostfwd=tcp:127.0.0.1:2222-:22 \
      -object rng-random,filename=/dev/urandom,id=rng0 \
      -device virtio-rng-pci,rng=rng0 \
      -display none \
      -serial file:/home/artem/vm/debian-12-qemu/console.log \
      -monitor unix:/home/artem/vm/debian-12-qemu/monitor.sock,server=on,wait=off

Emulated/Virtualized environment

  • Operating system: Debian GNU/Linux 12 (bookworm)
  • OS/kernel version: Linux 6.1.0-51-cloud-amd64, Debian 6.1.177-1 (2026-07-16)
  • Architecture: x86_64

Description of problem

The 16-bit VAPIC setup hypercall lets a privileged guest choose both the base and size of a priority-1000 writable RAM alias. QEMU does not verify that the resulting interval belongs to the VAPIC option ROM. On Q35, a guest can therefore place the alias over 0xa0000..0xbffff, bypassing the chipset's locked-SMRAM view and modifying memory that will execute in System Management Mode.

Current qemu.git master, commit fa19879df1658f96ac07365fca8835b7decd6995, remains affected by source inspection and runtime validation. The relevant code is:

For a 16-bit write to I/O port 0x7e, vapic_write() derives rom_paddr from guest-controlled CS:EIP and adds the guest-supplied port value. vapic_map_rom_writable() then:

  1. aligns this guest-selected value to a 512-byte ROM block;
  2. calls memory_region_find(mr, 0, 1), selecting the full guest-RAM backing rather than validating the region at the requested address;
  3. reads the alias length from the guest-controlled byte ram[rom_paddr + 2]; and
  4. creates a kvmvapic-rom alias at overlap priority 1000 without checking that its rounded base and end remain inside the option-ROM window.

In the attached reproducer, the setup write executes at GPA 0x9fe10, selecting block 0x9fe00, and guest RAM byte 0x9fe02 is 0xff. The raw length is therefore 0xff * 512 = 0x1fe00 bytes. QEMU's page rounding changes this into [0x9f000, 0xc0000). The priority-1000 alias overrides the normal Q35 mappings for locked SMRAM at [0xa0000, 0xc0000).

The resume condition is practical and does not inherently require the attacker to control QEMU's management interface. Privileged guest code can install the suspend hook in advance. When the guest next enters S3, the hook arms the malicious firmware waking vector, which remains in place while the VM is suspended. The attacker can then wait for an ordinary resume initiated by a user, operator, or configured virtual wake source; the wake stub executes before the guest OS completes its normal resume. The system_wakeup HMP command in the reproducer is only a deterministic way to produce that ordinary wake event.

Security relevance and scope uncertainty

I am not asserting that this bug independently crosses a security boundary that QEMU promises to protect. I have prepared it for confidential triage because I am unsure whether it qualifies as a security issue under QEMU's guidelines. The QEMU security process says that not every part of QEMU provides a security boundary and invites reports when the reporter thinks there may be a vulnerability or is unsure how a vulnerability affects QEMU. In particular, I do not know whether guest-kernel-to-SMM isolation on an accelerated Q35 machine is within the supported security boundary.

Regardless of that security classification, this is incorrect device behavior: a hypercall whose documented purpose is to make the VAPIC option ROM writable can instead replace unrelated Q35 memory mappings. QEMU neither rejects the malformed setup nor constrains its resulting range to the option-ROM window.

The immediate primitive crosses the guest kernel/SMM isolation boundary: a guest administrator can inject arbitrary code into locked SMRAM and have it execute in SMM. The Q35 D_LCK protection no longer prevents non-SMM guest software from modifying the SMM backing memory.

The project's published security model explicitly treats the guest as untrusted and lists Q35 with a virtualization accelerator as a supported guest-isolation configuration. On the other hand, its "degraded guest behaviour" guidance says that virtual-device misbehavior requiring guest kernel or root privileges will generally be treated as a hardening bug, and it does not explicitly identify guest-kernel-to-SMM isolation as a supported security boundary. I therefore defer classification to the QEMU security team.

I nevertheless used this primitive with other, separately reported vulnerabilities to obtain a complete guest-to-host escape and practically demonstrated that chain from a Debian 12 guest. Those other issues are independent and intentionally omitted here. I defer the standalone severity and CVE decision to the QEMU security team.

Suggested fix

QEMU should bind the writable alias to the VAPIC option ROM that QEMU actually loaded, rather than trusting an address and length reconstructed from guest state. At minimum, before creating the alias, it should reject any raw or page-rounded interval whose base or end lies outside the PC option-ROM window (0xc0000..0xdffff), perform overflow-safe end checks, and verify the expected VAPIC ROM identity. The strongest fix would retain the exact address and size assigned when kvmvapic.bin is loaded and require the setup hypercall to match that range.

The validation must occur before deleting an existing alias or calling memory_region_init_alias(). A regression test should attempt setup writes from below 0xc0000 and with a size byte that would cross 0xe0000, then confirm that no alias is created.

Steps to reproduce

  1. Start an x86 Q35/KVM guest with ACPI S3 and an HMP monitor. The complete command line used for practical validation is included above.
  2. Copy the attached qemu-vapic-smram-reproducer.tar.gz into the guest, extract it, and install the build prerequisites. On Debian:
    tar -xzf qemu-vapic-smram-reproducer.tar.gz
    cd qemu-vapic-smram-reproducer
    sudo apt-get install build-essential linux-headers-$(uname -r)
    make
    sudo insmod vapic_s3_repro.ko
  3. In the guest, run sudo systemctl suspend. In the HMP monitor, run system_wakeup to resume it.
  4. In HMP, run info mtree and inspect the system-memory region tree. A vulnerable build shows priority-1000 kvmvapic-rom covering 0x000000000009f000..0x00000000000bffff, overlapping smram-region at 0x00000000000a0000..0x00000000000bffff.
  5. In the guest, confirm the messages with sudo dmesg | tail -n 20, then unload the module with sudo rmmod vapic_s3_repro. Reset or stop the test VM to remove the QEMU-side alias.

The observable mapping is sufficient to demonstrate the bug. The reproducer deliberately does not write an SMM payload or invoke any other vulnerability.

Additional information

  • Runtime validation was performed on upstream-derived commit b428fe036233cbd15d37e3c027ab6ca4d3661a80 and repeated on current master fa19879df1658f96ac07365fca8835b7decd6995 (QEMU 11.1.50).
  • The exact attached archive builds against the listed Debian kernel's installed headers without compiler warnings. The resulting module loaded under that matching kernel for runtime testing.
  • The attached archive contains only the kernel-module C source, a Makefile, and detailed run instructions.
  • I found no public QEMU issue or CVE matching this VAPIC alias-range flaw. A confidential report may of course already exist.
  • Reporter: "Artem Dinaburg" artem@trailofbits.com

qemu-vapic-smram-reproducer.tar.gz