A x86 CPU fuzzer utilizing microcode coverage
Internal synonym: uFuzz
uFuzz is the first x86 CPU fuzzer that leverages microcode coverage information as feedback to guide the fuzzing campaign. For more details see the paper.
uFuzz consists of three different systems:
- The fuzzer agent: This is the target device that runs the device under test. We used the Gigabyte Brix (GB-BPCE-3350C-BWUP) with an Intel Apollo Lake (Celeron, Goldmont) N3350 processor (
CPUID[1].EAX=0x506ca) ; vulnerable to the Red-unlock vulnerability. - A fuzzer instrumentor: This is a device that emulates a USB storage (for serving the UEFI app) and USB keyboard for skipping the BIOS screen automatically, further controls the power supply of the fuzzer device. (Raspberry Pi 4)
- The fuzzer controller: The main fuzzing loop runs here, tasks are scheduled on the fuzzer device for execution. (Raspberry Pi 4)
The uFuzz project is structured as follows:
| Component | Description |
|---|---|
corpus-gen |
Generates the corpus for initial fuzzing inputs. See the evaluation section of the paper. |
coverage |
Collects microcode coverage from the CPU by deploying microcode updates |
custom_processing_unit |
Contains utility function derived from CustomProcessingUnit*. |
data_types |
Contains shared data types for writing custom microcode updates. |
fuzzer_data |
Contains shared data between the fuzzer instance and fuzzer master controller. |
fuzzer_device |
Contains the implementation of the fuzzing agent, which runs on the target device/CPU. |
fuzzer_master |
Contains the implementation of the fuzzer controler that controls a fuzzer agent. |
fuzzer_node |
Contains the implementation of the fuzzer device instrumentor - emulating USB devices for the fuzzer device. |
hypervisor |
Contains the implementation of the hypervisor environment. |
literature_search |
Contains a tool to search connected/related works by paper connections. |
nix |
Contains the definition of system of the fuzzer master and fuzzer instrumentor. |
performance_timing |
Contains the tools to collect timing information from the fuzzer device, |
performance_timing_macros |
Contains utility macros to automate timing collection from target functions. |
spec_fuzz |
Contains the implementation of the speculative micrcode fuzzer. |
speculation_x86 |
Contains some test scenarios to check speculative execution behavior. |
speculation_ucode |
Contains some test scenarios to check speculative execution behavior. |
ucode_compiler_bridge |
Contains a bridge implementation to interface with the microcode compiler from CustomProcessingUnit* and preprocessor macros for deriving multi file microcode updates. |
ucode_compiler_derive |
Contains utility macros to automate the generation of microcode updates. |
ucode_compiler_dynamic |
Contains runtime mircocode update compilation. |
ucode_dump |
Contains microcode dumps of the CPU. |
uefi_udp4 |
Contains a basic UEFI driver implementation of UDP. |
x86_perf_counter |
Contains the implementation to use x86 performance counters. |
xtask |
Contains build automation for this project. |
* CustomProcessingUnit: Licensed under the Apache License, Version 2.0
To build and run the uFuzz project, you will need the Rust compiler with the nightly toolchain and UEFI target support.
# Install python (ubuntu/debian); required for the CustomProcessingUnit microcode compiler
sudo apt install python3 python3-click
# Install compiler
sudo apt install gcc-aarch64-linux-gnu build-essential git
# Install rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y # installs rustup
rustup install nightly-2025-05-30 # verified to work with the project
rustup target add x86_64-unknown-uefi # to compile UEFI applications
rustup target add aarch64-unknown-linux-gnu # to compile the fuzzer instrumentor
rustup target add x86_64-unknown-linux-gnu # to compile documentation
rustup default nightly-2025-05-30 # set the default toolchain to nightlyDownload CustomProcessingUnit and 1. place it into the parent directory of this folder or 2. set the env var UASM to the
uasm.py file from CustomProcessingUnit. The uFuzz project uses the uasm.py script to compile microcode updates.
Then apply the following git-patch to uasm.py: uasm.py.patch.
To deploy the fuzzer instrumentor and fuzzer master, you will need nix installed (follow https://nixos.org/download/ to install the package manager).
Go into the nix directory, change the public ssh keys, IPs etc., to your likings,
and run the following commands to build the SD card images for the PIs (|& nom is optional):
nix build .#images.master |& nom
nix build .#images.node |& nomThis builds SD card images for the fuzzer master and fuzzer instrumentor. After initial setup,
further changes may be deployed using (deploy-rs) by running in the nix directory (|& nom is optional):
nix run |& nomTo built and deploy the fuzzer device UEFI app:
HOST_NODE="put IP of the instrumentor here" cargo xtask put-remote
--remote-ip {address of fuzzer controller}
--source-ip {address of agent}
--netmask {network mask}
--port {udp port}
--startup {app name here}Depending on the target fuzzing scenario, use spec_fuzz (speculative microcode fuzzing) or fuzzer_device (x86 instruction fuzzing)
instead of {app name here}.
Then boot the fuzzer master. Start the fuzzer_master app. Settings can be displayed by using --help in the CLI.
fuzzer_master --help
# Main fuzzing application. This app governs and controls the entire fuzzing process, issuing commands to a fuzzer agent (which e.g. executes fuzzing inputs on its CPU)
#
# Usage: fuzz_master [OPTIONS] <COMMAND>
#
# Commands:
# genetic Perform coverage fuzzing using (bad) genetic mutation algorithm, probably you would like to execute the `afl` command. == Requires the `fuzzer_device` app running on the agent ==
# instruction-mutation Under-construction
# init Bring up the fuzzer agent to a usable state
# reboot Reboot the fuzzer agent
# cap Report the capabilities of the fuzzer agent
# performance Extract performance values from the fuzzer agent
# spec Do speculative microcode fuzzing == Requires the `spec_fuzz` app running on the agent ==
# spec-manual Executes a given speculative fuzzing payload manually == Requires the `spec_fuzz` app running on the agent ==
# manual Executes a single fuzzing input manually == Requires the `fuzzer_device` app running on the agent ==
# bulk-manual Executes a corpus of fuzzing inputs; essentially runs the manual command using all files within the given directory == Requires the `fuzzer_device` app running on the agent ==
# afl Executes the main fuzzing loop with AFL mutations == Requires the `fuzzer_device` app running on the agent ==
# help Print this message or the help of the given subcommand(s)
#
# Options:
# -d, --database <DATABASE> The database file to save fuzzing progress and results to; a .gz extension enables gzip compression
# --compression <COMPRESSION> Compression level of database when using gzip compression (1-9) [default: 6]
# --dont-reset Dont reset address blacklist
# --instrumentor <INSTRUMENTOR> Address of the fuzzer instrumentor [default: http://10.83.3.198:8000]
# --agent <AGENT> Address of the fuzzer agent [default: 10.83.3.6:4444]
# -h, --help Print help
# -V, --version Print versionEach mentioned fuzz_master commands (experiments) includes built-in help documentation that allows you to specify parameters. For example:
- For running the fuzzing campaign with AFL mutations:
fuzz_master afl --help
# Executes the main fuzzing loop with AFL mutations == Requires the `fuzzer_device` app running on the agent ==
#
# Usage: fuzz_master afl [OPTIONS]
#
# Options:
# -s, --solutions <SOLUTIONS> Store findings to this path
# -c, --corpus <CORPUS> Use the provided corpus file to generate initial fuzzing inputs from
# -a, --afl-corpus <AFL_CORPUS> Store the fuzzing corpus to that path
# -t, --timeout-hours <TIMEOUT_HOURS> End fuzzing after that many hours automatically, if not set fuzzing does not terminate
# -d, --disable-feedback Disabled coverage fuzzing feedback; instead fuzzing feedback is randomized
# -p, --printable-input-generation When not using the `corpus` argument; initial fuzzing inputs are random byte sequences; enabling this flag these byte sequences are selected among printable ASCII characters
# -h, --help Print help- For running the fuzzing campaign with pure genetic mutations:
fuzz_master genetic --help
# Perform coverage fuzzing using (bad) genetic mutation algorithm, probably you would like to execute the `afl` command. == Requires the `fuzzer_device` app running on the agent ==
#
# Usage: fuzz_master genetic [OPTIONS]
#
# Options:
# -c, --corpus <CORPUS> A corpus file to generate the initial fuzzing inputs from
# -t, --timeout-hours <TIMEOUT_HOURS> After how many hours the fuzzing should be terminated. If not given fuzzing must be interrupted by CTRL+C
# -d, --disable-feedback Disable the feedback loop, fuzzing input ratings will become randomized
# -h, --help Print help- For running the fuzzing campaign with speculative microcode fuzzing:
fuzz_master spec --help
# Do speculative microcode fuzzing == Requires the `spec_fuzz` app running on the agent ==
#
# Usage: fuzz_master spec [OPTIONS] <REPORT>
#
# Arguments:
# <REPORT> Path to database; save the results to this file
#
# Options:
# -a, --all Execute fuzzing for all instructions extracted from MSROM
# -s, --skip Skip instruction that were already run; continue a stopped fuzzing execution
# -n, --no-crbus Skip all CRBUS related instructions
# -e, --exclude <EXCLUDE> Exclude a list of instructions from running through the fuzzer
# -f, --fuzzy-pmc Run all PMC variants through the fuzzer; takes a long time
# -h, --help Print helpThe fuzzing results will be stored to a database file (specifiable via the --database argument) (fuzzer master)
and can be viewed by running (fuzzer master):
fuzz_viewer database.json.gzThe following binaries are provided for the fuzzing master:
| Binary | Description |
|---|---|
fuzzer_master |
Main fuzzing controller, main fuzzing loop + manual execution, list all options using --help |
fuzz_viewer |
Tool for viewing and analyzing fuzzing results from the database |
fuzz_compare |
Compares execution results from different manual fuzzing input executions |
spec_compare |
Analyzes and compares results from speculative execution fuzzing campaigns |
spec_new_database |
Creates new speculative execution databases from templates (copies exclusions) |
fuzz_combine |
Combines multiple fuzzing databases into a single output database |
afl_convert |
Converts AFL fuzzing findings to inputs usable with the manual execution fuzzing tool |
Generate the rust doc documentation using the following command:
cargo xtask docThe results will be placed inside the build directory target/doc
The cargo xtask command contains utilities to, for example, test the hypervisor environment on a simulated
CPU using the bochs emulator; allowing easier debugging.
cargo xtask emulate hypervisor bochs-intelList all available xtask commands using:
cargo xtask --help
# A build and test assist program
#
# Usage: xtask <COMMAND>
#
# Commands:
# emulate Emulate a UEFI application using BOCHS CPU emulator
# put-remote Push an UEFI app onto the remote machine
# control-remote Control a remote machine
# update-node Update the node's software, systemd service, etc
# doc Generate documentation
# check Compile all examples and subprojects
# help Print this message or the help of the given subcommand(s)
#
# Options:
# -h, --help Print helpA up-to-date version of the source-code can be found at: https://github.com/0xCCF4/ufuzz
Our work has been published as a paper at Network and Distributed System Security Symposium 2026 (NDSS'26):
@inproceedings{TBD}