Mastering Honggfuzz: From Basics to Advanced
Fuzzing is a powerful technique for uncovering software vulnerabilities by feeding unexpected, malformed, or random inputs into a program. Among the many fuzzing tools available, Honggfuzz stands out for its efficiency, flexibility, and advanced features. In this blog post, we’ll take a deep dive into Honggfuzz, exploring its architecture, key features, usage, and how it compares to other fuzzers like AFL and libFuzzer.
Honggfuzz Honggfuzz is an open-source, security-oriented, feedback-driven fuzzer developed by Google engineer Robert Swiecki. It is designed to find bugs and vulnerabilities in software by mutating inputs and monitoring program execution for crashes, hangs, or other anomalies.
How Honggfuzz Works
Honggfuzz follows a feedback-driven fuzzing approach, where it:
- Generates Inputs – Starts with a seed corpus and mutates inputs.
- Executes Target – Runs the target program with the mutated input.
- Monitors Execution – Uses sanitizers or hardware features (Intel PT) to detect crashes, hangs, or coverage changes.
- Adjusts Strategy – Prioritizes inputs that trigger new code paths or crashes.
Installation
1
2
3
4
5
sudo apt-get install binutils-dev libunwind-dev libblocksruntime-dev clang make
git clone https://github.com/google/honggfuzz
cd honggfuzz
make
sudo make install
You’ll have hfuzz-cc
, hfuzz-clang
, hfuzz-clang++
, hfuzz-g++
, hfuzz-gcc
. Honggfuzz supports multiple instrumentation methods to track code coverage. Instrumentation refers to the process of modifying the target program—typically at compile time—to insert additional code that tracks code coverage and other execution metrics during fuzzing. This inserted code enables honggfuzz to observe which parts of the program are exercised by each test input, allowing it to guide mutations toward unexplored code paths and thus increase the likelihood of finding bugs. By default, honggfuzz uses compile-time instrumentation with flags such as -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp
(for clang) or -finstrument-functions
(for gcc/clang). This code tracks which parts of the program are executed by each input.
Basic Fuzzing Example
Let’s write a simple C program with a classic stack-based buffer overflow vulnerability. Our program will:
- Take a filename as input (passed via command line).
- Read its content into a small buffer.
- Trigger a buffer overflow if the file is too large.
vulnerable.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 16 // Intentionally small to force an overflow
void vulnerable_function(const char *filename) {
char buffer[BUFFER_SIZE]; // Small fixed-size buffer
FILE *file = fopen(filename, "rb"); // Open file in binary mode
if (!file) {
perror("Failed to open file");
return;
}
// Danger! No bounds checking → Potential buffer overflow!
fread(buffer, 1, BUFFER_SIZE * 10, file); // Read way more than buffer can hold
fclose(file);
printf("Buffer content: %.*s\n", BUFFER_SIZE, buffer); // Print (may be corrupted)
}
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <input_file>\n", argv[0]);
return 1;
}
vulnerable_function(argv[1]);
return 0;
}
Now, let’s fuzz this program to find the crash.
Compile with Honggfuzz Instrumentation
1
hfuzz-clang -o vulnerable vulnerable.c
(Or use hfuzz-gcc if you prefer GCC.)
Create a Seed Corpus
1
2
mkdir inputs DIR
echo "AAAA" > inputs/seed.txt
These are non-crashing inputs. Honggfuzz will mutate them to find crashes.
To get help:
1
honggfuzz -h
Essential Options
-i DIR
– Path to a directory containing initial file corpus-o DIR
– Output data (new dynamic coverage corpus, or the minimized coverage corpus) is written to this directory (default: input directory is re-used)-P
– Enable persistent fuzzing (use hfuzz_cc/hfuzz-clang to compile code).-s
– Feed input via stdin instead of___FILE___
-n N
– Number of concurrent fuzzing threads (default: number of CPUs / 2)-t SEC
– Timeout per run (default: 1 sec)-w FILE
– Dictionary file. Format:http://llvm.org/docs/LibFuzzer.html#dictionaries
Crash Handling
--crashdir DIR
– Explicitly set crash directory--exit_upon_crash
– Stop after first crash--verifier
– Re-test crashes to avoid duplicates
Advanced Modes
-x
– Static mode only, disable instrumentation (binary-only fuzzing)-z
– Force compile-time instrumentation (default)--only_printable
– Only generate printable inputs
Let’s learn about corpus. A corpus is a collection of input files used to guide fuzzing. Honggfuzz mutates these files to find new code paths and crashes.
-i DIR
→ Input corpus directory (seed files)-o DIR
→ Save new/mutated inputs (avoids modifying originals)-M
→ Minimize corpus (removes redundant files)
Example Commands
1
2
3
4
5
6
7
8
# Basic fuzzing (file input)
honggfuzz -i seeds -o crashes -- ./target ___FILE___
# STDIN input mode
honggfuzz -i seeds -s -- ./target
# Persistent mode (faster)
honggfuzz -i seeds -P -- ./persistent_target
Run Honggfuzz
1
honggfuzz --verifier -i inputs -o cov --crashdir crash -- ./vulnerable ___FILE___
-i inputs
: Input corpus directory.-o cov
: Where new/mutated inputs will be saved.___FILE___
: Honggfuzz replaces this with the mutated input file.
By specifying --crashdir crash
, crashes will be saved in a dedicated directory. Otherwise, all .fuzz
files will clutter your current working directory.
Honggfuzz will quickly find that long inputs (>16 bytes) crash the program.
We will stop honggfuzz after getting enough crashes by hitting Ctrl + C
.
------------------------[ 0 days 00 hrs 00 mins 02 secs ]----------------------
Iterations : 2,429 [2.43k]
Mode [3/3] : Feedback Driven Mode
Target : ./vulnerable ___FILE___
Threads : 12, CPUs: 24, CPU%: 1857% [77%/CPU]
Speed : 778/sec [avg: 1,214]
Crashes : 24 [unique: 23, blocklist: 0, verified: 0]
Timeouts : 12 [1 sec]
Corpus Size : 8, max: 8,192 bytes, init: 1 files
Cov Update : 0 days 00 hrs 00 mins 02 secs ago
Coverage : edge: 4/7 [57%] pc: 0 cmp: 60
---------------------------------- [ LOGS ] ------------------/ honggfuzz 2.6 /-
Analyze the Crash
Check the coverage saved in cov/
directory:
1
2
3
$ ls cov/
0e8f69c4e77001ea6de310801d61ea00.00000014.honggfuzz.cov 6dddddddddddb0006dddddddddddb000.00000006.honggfuzz.cov fb8dddddddddddddefddddddddddddbe.00000008.honggfuzz.cov
6dddddb0000000000000006dddddb000.00000006.honggfuzz.cov a4b87b8ddddddddd248addddddddddbe.0000000a.honggfuzz.cov
.cov
files in Honggfuzz store code coverage data collected during fuzzing. They help Honggfuzz track which parts of your program have been executed, allowing it to intelligently guide mutations toward unexplored code paths.
Understanding Crash Filenames
When Honggfuzz finds a crash, it saves the triggering input as a .fuzz
file. Let’s examine how to work with these crash files.
1
2
3
4
$ ls crash/
'SIGBUS.PC.555555583534.STACK.0.CODE.128.ADDR.0.INSTR.movl___$0x0,-0x4(%rbp).2025-07-04.18:25:46.1067732.fuzz'
'SIGBUS.PC.555555583534.STACK.0.CODE.128.ADDR.0.INSTR.movl___$0x0,-0x4(%rbp).2025-07-04.18:25:46.1067827.fuzz'
#...
SIG[signal].PC.[address].STACK.[stack_hash].CODE.[code].ADDR.[addr].INSTR.[instruction].[timestamp].fuzz
We can change the .fuzz
extension.
--extension|-e VALUE
Input file extension (e.g. 'swf'), (default: 'fuzz')
We can view the hex contents of a crash file using xxd
:
1
2
3
4
xxd SIGSEGV.PC.0.STACK.0.CODE.1.ADDR.0.INSTR.\[NOT_MMAPED\].2025-07-04.18\:25\:48.1069771.fuzz
00000000: 4124 4141 4102 0000 0000 0000 0000 0000 A$AAA...........
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
#...
This shows the exact input that triggered the crash - in this case, a malformed string starting with A$AAA
.
Reproducing the Crash
To verify the crash:
1
2
3
4
5
6
7
8
./vulnerable ./crash/SIGSEGV.PC.0.STACK.0.CODE.1.ADDR.0.INSTR.\[NOT_MMAPED\].2025-07-04.18\:25\:48.1069771.fuzz
Buffer content: A$AAA
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==1074633==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000000000000 bp 0x000000000000 sp 0x7ffefbf24050 T1074633)
==1074633==Hint: pc points to the zero page.
==1074633==The signal is caused by a READ memory access.
==1074633==Hint: address points to the zero page.
#...
The crash confirms our vulnerability:
- The program reads the malformed input
- A segmentation fault occurs (SEGV)
- The sanitizer detects illegal memory access
Happy Fuzzing! 🎉
For more hands on practice you can checkout Fuzzing101