How to Exploit the CXL GET_LOG Handler Vulnerability in QEMU: A Full Chain Guide

You can exploit the CXL GET_LOG handler vulnerability in QEMU by leveraging an out-of-bounds read in the Type-3 mailbox command handler to leak host pointers, then using an unchecked SET_FEATURE rank-sparing write to forge memory-management objects that ultimately trigger arbitrary code execution in the QEMU host process.

The CXL Type-3 mailbox implementation in QEMU contains critical validation flaws that allow guest-to-host escape. According to the bikini/exploitarium repository, these vulnerabilities reside in the mailbox command handlers where boundary checks are improperly implemented, enabling an attacker to first disclose sensitive memory addresses and subsequently achieve arbitrary write capabilities leading to full system compromise.

Understanding the GET_LOG Validation Flaw

The root cause lies in how the GET_LOG handler validates its arguments. As documented in the repository's analysis of qemu-cxl-type3-mailbox-escape-poc/README.md at lines 141-149, the handler verifies that offset + length fits within the internal log buffer size (sizeof(cci->cel_log)). However, it then uses offset directly as an array index without ensuring offset itself is within the buffer bounds.

This discrepancy creates an out-of-bounds read primitive. By providing a large offset value that passes the aggregate range check but indexes beyond the buffer boundary, a guest can leak arbitrary host memory contents, including QEMU's PIE base and the CXLType3Dev object address.

The Unchecked SET_FEATURE Write Primitive

Complementing the read vulnerability, the SET_FEATURE rank-sparing handler lacks proper bounds checking on its write operations. As noted in README.md at lines 151-156, this handler copies guest-controlled data into the rank_sparing_wr_attrs structure without validating the destination bounds. This provides an arbitrary write primitive into host memory relative to the CXLType3Dev object.

The Five-Stage Exploit Chain

The proof-of-concept implemented in stage2.c executes a complete chain that transforms these primitives into full code execution:

  1. Leak Host Pointers via GET_LOG: Issue the GET_LOG command with a crafted offset to read beyond the cel_log buffer, extracting a QEMU text pointer and the CXLType3Dev object address from host heap memory.

  2. Calculate Base Addresses: Derive the QEMU PIE base from the leaked text pointer, then calculate the exact host address of the CXLType3Dev structure using offsets determined from the leaked data.

  3. Forge Memory-Management Objects: Use the SET_FEATURE rank-sparing command to write a carefully constructed payload past the end of rank_sparing_wr_attrs. This forges a complete chain of QEMU objects including FlatView, AddressSpaceDispatch, MemoryRegionSection, MemoryRegion, and MemoryRegionOps structures.

  4. Trigger the Callback: Invoke the MEDIA_OPERATIONS mailbox command, which walks the forged address space and eventually calls the controlled MemoryRegionOps.write callback.

  5. Achieve Code Execution: The callback redirects execution to attacker-controlled code, typically invoking system("/bin/sh") to escape the guest and execute commands in the host environment.

Implementation Details from stage2.c

The 32-bit guest payload in stage2.c demonstrates the exploitation sequence. First, it issues the out-of-bounds GET_LOG command as shown at lines 24-36 and 250-254:

/* Issue GET_LOG to leak host pointers */
u32 off = GET_LOG_OOB_BASE_OFFSET + (rel >> 2);
mailbox_cmd(CMD_GET_LOG, 24);   // Sends GET_LOG request

/* Extract leaked data from the out-of-bounds scratch area */
qemu_base = handler - CMD_LOGS_GET_LOG_OFF;               // PIE base
ct3d_host  = qemu_base + GET_LOG_OOB_MEM_BASE_FROM_CT3D; // CXLType3Dev address

After calculating the base addresses at lines 392-402, the exploit crafts the payload and writes it via SET_FEATURE:

/* Forge the dynamic-capacity objects using SET_FEATURE */
memcpy((uint8_t *)&ct3d->rank_sparing_wr_attrs + hdr->offset,
       crafted_payload, payload_len);

/* Trigger the forged write callback */
mailbox_cmd(CMD_MEDIA_OPERATIONS, ...);

Key files in the bikini/exploitarium repository support this exploitation:

  • stage2.c: Contains the 32-bit protected-mode payload that drives PCI config space access and issues the mailbox commands.
  • boot.asm: Provides the 16-bit bootloader that initializes the system and loads the main stage.
  • run.sh: Automates QEMU launch with the correct CXL Type-3 configuration and validates successful exploitation.
  • README.md: Documents the vulnerability analysis and exploit flow with specific line references to the QEMU source behavior.

Building and Running the Exploit

To execute the proof-of-concept locally, build the guest stage and BIOS image, then launch QEMU with CXL Type-3 support enabled:


# Build the 32-bit stage and BIOS image

sh build.sh

# Launch QEMU with CXL Type-3 enabled (replace with your qemu binary)

sh run.sh /usr/bin/qemu-system-x86_64

# Verify successful exploitation

cat /tmp/qemu_cxl_escape_marker

The run.sh script handles the QEMU configuration and validates successful exploitation by checking for the marker file. Successful execution logs include entries showing the leaked handler address (handler=0x…), calculated QEMU base (qemu_base=0x…), and system function location (system=0x…), confirming each stage of the chain completed successfully.

Summary

  • The GET_LOG handler in QEMU's CXL Type-3 mailbox validates offset + length against buffer size but uses offset directly as an array index, enabling out-of-bounds reads of host memory.
  • The SET_FEATURE rank-sparing handler performs unchecked writes to rank_sparing_wr_attrs, allowing arbitrary data injection into host memory relative to the CXLType3Dev object.
  • The exploit chain implemented in bikini/exploitarium combines these primitives to forge QEMU memory-management structures and trigger arbitrary code execution through the MEDIA_OPERATIONS command handler.
  • Any guest capable of issuing CXL mailbox commands can exploit this vulnerability, making it critical for environments with CXL Type-3 support enabled.

Frequently Asked Questions

What QEMU versions are affected by the CXL GET_LOG vulnerability?

The vulnerability affects QEMU implementations with CXL Type-3 mailbox support where the GET_LOG and SET_FEATURE handlers lack proper bounds validation. You should check your specific QEMU version against the latest security advisories, but the underlying issue stems from improper offset validation in the mailbox command processing code referenced in the bikini/exploitarium analysis at lines 141-156.

Can this exploit work with a 16-bit guest or does it require a full operating system?

The exploit does not require a full operating system. According to the source analysis, any guest capable of issuing CXL mailbox commands can trigger the vulnerability, including a simple 16-bit bootloader. The provided PoC uses a minimal bootloader (boot.asm) that loads a 32-bit stage (stage2.c), demonstrating that complex guest environments are unnecessary for successful exploitation.

What specific host memory structures are leaked by the GET_LOG command?

The out-of-bounds GET_LOG read leaks QEMU text pointers (allowing calculation of the PIE base) and the heap address of the CXLType3Dev object. These pointers are located in memory adjacent to the cel_log buffer and are extracted by the guest in stage2.c using carefully calculated offsets based on the buffer overflow position.

How does the SET_FEATURE command achieve arbitrary code execution?

The SET_FEATURE rank-sparing handler writes guest-controlled data beyond the bounds of rank_sparing_wr_attrs without validation. By overwriting adjacent memory, an attacker can construct fake MemoryRegion and MemoryRegionOps structures. When the MEDIA_OPERATIONS command subsequently traverses these forged objects, it invokes a controlled function pointer, redirecting execution to attacker-specified code such as system("/bin/sh").

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 →