How to Build the QEMU CXL PoC with build.sh
Run sh build.sh inside the qemu-cxl-type3-mailbox-escape-poc directory to automate the compilation of the 16-bit bootloader, 32-bit stage-2 payload, and the creation of a bootable poc.img floppy image ready for QEMU.
The bikini/exploitarium repository hosts a proof-of-concept that demonstrates a CXL Type-3 mailbox escape vulnerability in QEMU. To build the QEMU CXL PoC with build.sh, you need only standard development toolchains and a single shell command that orchestrates assembly, compilation, linking, and image padding into one deterministic pipeline.
Prerequisites for Building the QEMU CXL PoC
Before executing the build script, ensure your system provides the following tools as specified in the repository’s README.md:
- NASM – Assembles the real-mode bootloader (
boot.asm) into raw machine code. - GCC with 32-bit support – Compiles
stage2.cas a freestanding 32-bit object using flags like-m32and-ffreestanding. - GNU ld – Links the stage-2 object using the custom linker script
stage2.ld. - Python 3 – Executes the inline padding script that concatenates binaries and pads the final image to a 1.44 MiB floppy size (
1474560bytes).
On Debian-based distributions, you can typically install these with:
sudo apt-get install nasm gcc-multilib python3
Architecture of the Build Process
The build.sh script (located at qemu-cxl-type3-mailbox-escape-poc/build.sh) transforms three source components into a single bootable disk image through four distinct stages.
Bootloader Assembly (boot.asm)
The 16-bit bootloader is assembled by NASM into a raw binary. This 512-byte sector loads the protected-mode stage from the floppy into memory and jumps to it.
nasm -f bin boot.asm -o boot.bin
Stage-2 Payload Compilation (stage2.c)
The freestanding 32-bit C program (stage2.c) contains the exploit logic that interacts with PCI configuration space and the CXL mailbox. It is compiled without standard libraries or position-independent code:
gcc -m32 -ffreestanding -nostdlib -fno-builtin -fno-pic -fno-pie -fno-stack-protector -Wall -Wextra -c stage2.c -o stage2.o
Linking with stage2.ld
The linker script stage2.ld defines the memory layout for the flat binary output. The linker produces a raw binary blob that can be concatenated directly after the bootloader:
ld -m elf_i386 -T stage2.ld --oformat binary stage2.o -o stage2.bin
Image Assembly and Padding
An embedded Python fragment pads stage2.bin to the correct alignment, concatenates it with boot.bin, and pads the entire image to exactly 1,474,560 bytes (standard 1.44 MiB floppy geometry). The result is written to poc.img.
Step-by-Step Build Instructions
Follow these steps to produce the bootable exploit image:
-
Navigate to the PoC directory
cd qemu-cxl-type3-mailbox-escape-poc -
Execute the build script
sh build.shThis command runs the full pipeline defined in lines 3–14 of
build.sh: assemblingboot.asm, compilingstage2.c, linking withstage2.ld, and invoking the Python padding logic. Finally,sha256sumprints checksums for the generated artifacts to verify integrity. -
Verify the output
Upon successful completion, the terminal displays size metrics confirming correct assembly:
boot=512 stage=4560 image=1474560You can also inspect the final image:
ls -lh poc.img # -rw-r--r-- 1 user user 1.4M Jul 12 10:23 poc.img
Build Verification and Artifacts
After the script finishes, the directory contains three key files:
boot.bin– The 512-byte master boot record.stage2.bin– The flat-binary payload (size varies by compilation, typically ~4–5 KB).poc.img– The final 1.44 MiB floppy image containing the concatenated bootloader and padded stage-2 binary.
The sha256sum output at the end of the build provides cryptographic hashes for these files, allowing you to verify reproducible builds across environments.
Running the PoC After Building
With poc.img generated, you can launch the exploit using the companion run.sh script. Supply the path to a QEMU binary that includes CXL Type-3 device emulation:
sh run.sh /usr/bin/qemu-system-x86_64
If the exploit succeeds, the script will display confirmation that the host-side marker file /tmp/qemu_cxl_escape_marker was created, proving the mailbox escape from guest to host.
Summary
- Single command build: Execute
sh build.shin theqemu-cxl-type3-mailbox-escape-pocdirectory to trigger the complete pipeline. - Multi-stage compilation: The script orchestrates NASM for assembly, GCC 32-bit freestanding compilation, custom LD linking, and Python image padding.
- Deterministic output: The process always produces a 1.44 MiB
poc.imgready for QEMU, with checksums provided for verification. - No manual intervention: All steps—from
boot.asmto the final floppy geometry—are automated inbuild.shlines 3–14.
Frequently Asked Questions
What does the build.sh script automate?
The build.sh script automates the entire toolchain invocation sequence. It assembles the 16-bit bootloader with NASM, compiles the 32-bit C payload with GCC freestanding flags, links it using stage2.ld, pads the binary to floppy geometry with Python, and generates SHA-256 checksums for the resulting artifacts.
Why does the build require 32-bit GCC support?
The guest payload in stage2.c runs as a freestanding 32-bit protected-mode program inside the QEMU guest. The -m32 flag ensures the compiler targets the i386 instruction set, while -ffreestanding -nostdlib prevents linkage against the host’s standard library, producing a bare-metal binary suitable for the custom bootloader.
How can I verify the build succeeded?
Check the terminal output for the size verification line (boot=512 stage=4560 image=1474560) and confirm that poc.img exists with a size of exactly 1,474,560 bytes. The sha256sum output printed by build.sh also allows you to compare hashes against known-good values to ensure the binaries were constructed correctly.
Where is the final bootable image located?
The script writes the completed floppy image to poc.img in the current working directory (qemu-cxl-type3-mailbox-escape-poc/). This file isReady to be mounted by QEMU as a floppy disk using the -fda parameter or the configuration provided in run.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →