Firmware fuzzing in emulation works. Now you trust a peripheral model.

You can fuzz firmware without a board. That part is settled, and it has been settled since 2020.
What people skip is the second half. The fuzzer still needs something to answer its reads. Take the chip out and a model answers instead. Every result you get is a claim about that model, and the model is code someone wrote, inferred, or guessed.
The research stack that runs with no board
Three papers cover the ground, and each one deletes a different layer.
P2IM builds an abstract peripheral interface model automatically, by watching how the firmware itself touches registers, then drives QEMU with it. No hardware, no vendor spec, no manual per-peripheral work.1
HALucinator does not model the peripheral at all. It matches the HAL functions in the binary and swaps in host handlers. If you never execute the driver, you never need the register semantics under it.2
Fuzzware goes the other way and models MMIO precisely, at access-level granularity, so the fuzzer stops wasting mutations on input bits the firmware cannot act on. The evaluation covers 19 platforms and 77 firmware images, reports up to 3.25x the coverage of prior work, and found 15 new bugs that became 12 CVEs.3
Those are the numbers worth quoting, because they come with a paper you can read.

The boring, supported version is Renode plus AFL++
Antmicro shipped this in October 2023, as part of the aSSIsT project. It needs no
changes to the simulator. Renode's own hooks do the work: SetHookAtBlockBegin
fills AFL's coverage map in shared memory on every executed block, and a
quantum hook feeds the fuzzer's byte stream into the machine.4
The demo treats those bytes as UART input to a Zephyr echo sample on an
EFR32MG board, patched so the letter a jumps to address 0x0. A jump to 0 is the
crash signal. You are fuzzing the whole image, driver layer included, not a
harnessed subsystem. The a to 0x0 patch is only the crash oracle.
Antmicro calls the whole-image setup complementary rather than better.
The catch is one sentence in their post that most readers skate past. It is up to you to decide how to interpret and feed the byte stream. That decision is the harness, and the harness decides which bug class you are able to find at all. Bytes as UART characters finds parser bugs. Bytes as a length-prefixed packet finds different ones. Neither finds what you did not wire up.
GDBFuzz is the hardware-backed contrast
GDBFuzz keeps getting filed under hardware-free fuzzing. It is the opposite, and the config file says so.
Bosch Research's tool takes coverage feedback from hardware breakpoints on the
microcontroller, read through a GDB server attached to a debug probe.5 The
repo's setup instructions are a tour of debug hardware: st-util for an STM32
B-L4S5I-IOT01A, pyocd for a CY8CKIT-062, OpenOCD with a Segger J-Link for an
ESP32, mspdebug for an MSP430.6 Ghidra recovers the control flow graph,
GDBFuzz places a bounded number of breakpoints (max_breakpoints in the config)
and rotates them once they stop hitting.
There is a QEMU target mode. The README explains why it exists: the evaluation is split into GDBFuzz "directly on the hardware", which is the intended setup, and an emulated run so other people can reproduce and compare the results. The requirements list for a new board is a microcontroller with hardware breakpoints and a GDB-compliant debug probe. That is not a hardware-free tool with an optional board.
The bugs it found came off real devices: an infinite loop in the STM32 USB device stack, plus a buffer overflow and a null pointer dereference in the Cypress JSON parser.
The trade is honest and explicit. Cortex-M parts give you a handful of hardware breakpoints, so coverage is sampled and rotated rather than complete. You accept worse feedback to keep the real chip as the oracle.

Two nearby tools people file in the wrong bucket
Tardigrade gets miscredited as Antmicro fuzzing. It is a third-party fault-injection testbed that runs OTA bootloaders under Renode and checks recovery from power loss, interrupted erase, bit corruption, instruction skip, and similar faults.7 Its fuzzer bridge consumes crash artifacts from libFuzzer, AFL or honggfuzz; it does not make Tardigrade a fuzzer. The actual work still happens in hand-written peripheral models with fault hooks. Same lesson: the model is the product.
SAFIREFUZZ sits on the other side. It drops the emulator and runs ARM firmware natively on a same-architecture host, replacing HAL functions as it goes. Its paper reports a 690x average throughput increase over HALucinator and up to 147x over Fuzzware.8 Those are results from its own target set and comparison setups, not a shared bake-off. Near-native speed also gives up the emulator introspection you may have wanted in the first place.
What no-hardware fuzzing cannot prove
A clean run means the campaign found no failing execution in firmware running against that model. That is a smaller claim than it sounds.
- A missing crash proves nothing about the registers you never modeled. P2IM infers peripheral behavior from firmware access patterns. If the inference is wrong in the direction of "permissive", the fuzzer walks past the bug.
- Timing and analog behavior are not in scope. I wrote up the full list in what simulation still cannot catch.
- Errata are absent unless someone models them explicitly. Most peripheral models do not promise to reproduce every documented silicon mistake.
- Model bugs produce phantom crashes. Every one of those costs a triage afternoon, and enough of them train the team to ignore the fuzzer.
The capability that survives is worth having anyway: no-hardware fuzzing can find parser, state-machine and protocol bugs at a scale no board farm can reach. A clean campaign does not prove the device survives hostile input. Those are two different sentences, and firmware teams keep printing the first one and reading the second.
Then it has to reach CI, which is where most firmware pipelines already stop short. Firmware CI ends at the linker for most teams, and a fuzzing campaign that only runs on someone's laptop is a hobby.

The part an agent gets wrong
An agent can write a fuzzing harness in a minute. Feed it the Antmicro post and
it will produce a working quantum_hook. What it cannot do is tell you whether
the peripheral model underneath is honest, because nothing in the loop reports
that. The run comes back green either way.
That is the same gap I hit writing about how LLMs write good firmware and cannot prove it. Generation is cheap now. The judge is the expensive part, and hardware-free fuzzing is a judge you have to build before you can use it.
So build it, run it, and be precise in the write-up about which one found the bug: the firmware, or the model standing in for the chip.
Sources
Footnotes
-
Feng et al., "P2IM: Scalable and Hardware-independent Firmware Testing via Automatic Peripheral Interface Modeling," USENIX Security 2020. https://arxiv.org/abs/1909.06472 ↩
-
Clements et al., "HALucinator: Firmware Re-hosting Through Abstraction Layer Emulation," USENIX Security 2020. https://www.usenix.org/system/files/sec20summer_clements_prepub.pdf ↩
-
Scharnowski et al., "Fuzzware: Using Precise MMIO Modeling for Effective Firmware Fuzzing," USENIX Security 2022. https://www.usenix.org/system/files/sec22summer_scharnowski.pdf ↩
-
Antmicro, "Fuzzing Zephyr with AFL and Renode," 2023-10-04. https://antmicro.com/blog/2023/10/fuzzing-zephyr-with-afl-renode/ ↩
-
Eisele et al., "Fuzzing Embedded Systems using Debug Interfaces," ISSTA 2023. https://doi.org/10.1145/3597926.3598115 ↩
-
Bosch Research, "GDBFuzz: Debugger-Driven Fuzzing," companion code. https://github.com/boschresearch/gdbfuzz ↩
-
Berkman, "tardigrade: OTA firmware update fault injection testbed." https://github.com/neilberkman/tardigrade ↩
-
Seidel et al., "Forming Faster Firmware Fuzzers," USENIX Security 2023. https://www.usenix.org/system/files/usenixsecurity23-seidel.pdf ↩