QEMU CXL Guest-to-Host Code Execution Vulnerability: Technical Analysis of the Type-3 Mailbox Escape

The QEMU CXL guest-to-host code execution vulnerability is a critical escape primitive in QEMU's CXL Type-3 implementation that allows unprivileged virtual machine guests to execute arbitrary commands on the host system by exploiting two flaws in mailbox command handlers.

This vulnerability, documented in the bikini/exploitarium repository, demonstrates how improper bounds checking in CXL (Compute Express Link) mailbox commands creates a complete guest-to-host escape chain. The exploit targets QEMU's emulation of CXL Type-3 devices, specifically abusing the GET_LOG and SET_FEATURE command handlers to leak memory addresses and forge internal QEMU object structures.

Root Cause: Twin Flaws in CXL Mailbox Handling

The vulnerability stems from two distinct memory safety issues in QEMU's CXL Type-3 mailbox implementation. Together, these flaws enable an attacker to first leak critical memory addresses and then overwrite vital structures to hijack control flow.

GET_LOG Off-by-One Out-of-Bounds Read

The GET_LOG mailbox command handler contains an off-by-one validation error in its bounds checking logic. According to the source analysis in the bikini/exploitarium repository, the handler validates that offset + length remains within the bounds of the internal log buffer size, but subsequently uses the raw offset value directly as an array index when copying data to the guest.

This validation gap allows a guest-controlled offset to read past the end of the cci->cel_log buffer. By carefully selecting the offset parameter, an attacker can leak a pointer to QEMU's text (PIE) segment and obtain the address of the host's CXLType3Dev object, defeating Address Space Layout Randomization (ASLR) and locating the target structure for the next phase of the attack.

SET_FEATURE Rank-Sparing Unbounded Write

The second flaw resides in the SET_FEATURE command handler for the rank-sparing feature. This handler copies guest-supplied data into rank_sparing_wr_attrs + hdr->offset without performing any bounds check on the destination object size or the offset value.

Because rank_sparing_wr_attrs resides within the CXLType3Dev structure, supplying a large hdr->offset value allows an attacker to write beyond the intended buffer and overwrite the tail of the CXLType3Dev object. This capability enables the forging of a complete hierarchy of QEMU internal objects including FlatView, AddressSpaceDispatch, MemoryRegionSection, MemoryRegion, and critically, MemoryRegionOps. By forging the MemoryRegionOps.write callback to point to the host's system() function, the attacker establishes a direct path to arbitrary code execution.

Exploitation Chain: From Guest to Host

The complete exploitation flow implemented in stage2.c and boot.asm proceeds through six distinct phases to achieve host code execution:

  1. Initial Setup: The guest bootloader (16-bit assembly from boot.asm) loads a protected-mode payload (stage2.c), which configures the PCI-CXL root port and Type-3 endpoint via PCI configuration I/O operations.

  2. Information Disclosure: The guest issues a GET_LOG request with a crafted offset to trigger the out-of-bounds read, leaking the QEMU PIE base address and a pointer to the CXLType3Dev object (ct3d).

  3. Symbol Resolution: Using the leaked PIE base, the payload computes the runtime address of memmove@plt and other libc symbols, ultimately locating the address of the system() function.

  4. Structure Forging: The guest sends a SET_FEATURE request that writes a forged dynamic-capacity state into the tail of the CXLType3Dev object. This injects a fake MemoryRegionOps structure with a write callback pointing to the resolved system() address.

  5. Trigger Execution: A subsequent CXL media operation invokes the forged MemoryRegionOps.write callback, which executes the attacker-controlled command on the host.

  6. Verification: Successful exploitation creates the file /tmp/qemu_cxl_escape_marker on the host filesystem, demonstrating that code execution occurred inside the QEMU process with the same privileges as the host user.

Reproducing the Vulnerability

The bikini/exploitarium repository provides a complete proof-of-concept that automates this exploit chain. To reproduce the vulnerability against a QEMU binary built with CXL Type-3 support:


# From the PoC directory, execute the run script with the path to your QEMU binary

sh run.sh /absolute/path/to/qemu-system-x86_64

Upon successful execution, the serial output will display leaked addresses and the final system call invocation:


stage2 start
pci done
handler=0x00005d41f6d636f0
qemu_base=0x00005d41f68b6000
ct3d=0x000077f609b3a010
rank=0x000077f60a1ffb7a
fake leak call
media-op sent
...
system=0x000077f60c858750
fake system call
stage2 done

The presence of /tmp/qemu_cxl_escape_marker on the host confirms successful code execution.

Building from Source

To recreate the BIOS floppy image from the assembly and C source:


# Rebuild poc.img from boot.asm and stage2.c

sh build.sh

The core exploit logic in stage2.c performs the critical mailbox operations:

/* Leak a QEMU pointer via crafted GET_LOG request */
w32(MBOX_PAYLOAD + 8, CXL_STATIC_VMEM_SIZE);   // length parameter
w32(MBOX_PAYLOAD + 16, CXL_CACHELINE_SIZE);   // malicious offset triggers OOB read

/* Forge fake MemoryRegionOps.write callback via SET_FEATURE */
memcpy((uint8_t *)&ct3d->rank_sparing_wr_attrs + hdr->offset,
       forged_memory_region_ops, sizeof(forged_memory_region_ops));

Summary

  • The QEMU CXL guest-to-host code execution vulnerability combines an off-by-one read in GET_LOG with an unbounded write in SET_FEATURE to escape virtualization boundaries.
  • The GET_LOG flaw leaks the QEMU PIE base and CXLType3Dev object location, bypassing ASLR protections.
  • The SET_FEATURE rank-sparing handler allows writing beyond rank_sparing_wr_attrs to forge MemoryRegionOps structures and hijack callback execution.
  • Exploitation requires no host privileges—only the ability to run a guest VM with CXL Type-3 device emulation enabled.
  • The bikini/exploitarium PoC demonstrates full exploitation via custom 16-bit bootloader and 32-bit stage2 payload.

Frequently Asked Questions

How does the GET_LOG vulnerability leak memory addresses?

The GET_LOG command handler validates that offset + length fits within the log buffer but then accesses cci->cel_log[offset] directly. By supplying an offset that passes the sum check but exceeds the buffer boundary, a guest can read memory contents beyond the allocated buffer. This leaks pointers to QEMU's code segment and the CXLType3Dev object instance, providing the necessary addresses to calculate the location of libc functions like system().

What QEMU components are affected by the SET_FEATURE vulnerability?

The vulnerability specifically affects the CXL Type-3 device emulation code in QEMU, particularly the mailbox command handler for the rank-sparing feature. The SET_FEATURE command writes to the rank_sparing_wr_attrs field within the CXLType3Dev structure. Because this field is embedded early in the structure, an unchecked offset allows overwriting adjacent fields including the dynamic capacity state and memory region structures.

Does this vulnerability require special guest privileges?

No. The exploit chain operates from a fully unprivileged guest context. The guest only needs to issue standard CXL mailbox commands through the emulated PCI device interface. Neither root access within the guest nor special QEMU monitor permissions are required, as the vulnerability exists in the device emulation layer that processes guest-initiated mailbox requests.

How can I verify if my QEMU installation is vulnerable?

Check if your QEMU build includes CXL Type-3 device support by verifying the presence of CXL-related device options. The vulnerability affects versions implementing the GET_LOG and SET_FEATURE mailbox commands without proper bounds validation on the offset parameter. Running the bikini/exploitarium PoC against your QEMU binary and observing the creation of /tmp/qemu_cxl_escape_marker confirms exploitability.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →