Skip to main content

Overview

Fuzzing is an automated testing technique that finds bugs by providing random or mutated inputs to code. Bitcoin Core supports multiple fuzzing engines:
  • libFuzzer - LLVM’s coverage-guided fuzzer (recommended)
  • AFL++ - Advanced fuzzing with instrumentation
  • Honggfuzz - Security-oriented fuzzer
Fuzzing is particularly effective at finding:
  • Memory safety issues
  • Assertion failures
  • Crashes and undefined behavior
  • Edge cases not covered by unit tests
  • Denial-of-service vulnerabilities
Benchmarks are ill-suited for denial-of-service testing as they use restricted input sets. Fuzz tests explore the full input space and are better suited for this purpose.

Quick Start with libFuzzer

Build and Run

For fuzzing without sanitizers (increased throughput):

Fuzz Runner Script

Execute all fuzz targets using the runner:

Understanding Fuzz Output

libFuzzer Output

The fuzzer outputs NEW when it creates an input that covers new code areas:
Key metrics:
  • cov: Code coverage (edges covered)
  • ft: Feature count (more detailed coverage)
  • corp: Corpus size (number of inputs / total bytes)
  • exec/s: Executions per second
  • rss: Resident set size (memory usage)
See libFuzzer documentation for complete output interpretation.

Using Corpus Directories

Specify a corpus directory to save coverage-increasing inputs:
The fuzzer saves new interesting inputs to this directory:

Fuzzing Harnesses

Available harnesses are in src/test/fuzz/. Example: process_message - Fuzzes the ProcessMessage() function in net_processing. List all available targets:

Passing bitcoind Arguments

Fuzz tests can accept bitcoind arguments (some may ignore them):
Use -- to distinguish fuzzer args from bitcoind args.

Fuzzing Corpora

The official seed corpora collection is at bitcoin-core/qa-assets.

Using qa-assets

Output:
Starting with a good corpus significantly improves fuzzing efficiency by providing known-good inputs.

Sanitizers

Address and Undefined Behavior Sanitizers

Default libFuzzer preset includes sanitizers:

Memory Sanitizer (MSan)

MSan requires all linked code to be instrumented:
  1. Compile clang from source
  2. Use it to compile an instrumented libc++
  3. Build Bitcoin Core dependencies from source with instrumentation
  4. Build the fuzz binary
See the MSan CI job for a working example. Alternative: Use Valgrind, which doesn’t require custom libc++.

Running Without Sanitizers

For increased throughput (finding new coverage faster):
Fuzzing with sanitizers is good for finding bugs. Fuzzing without sanitizers is good for finding new coverage. A good strategy: run long sessions without sanitizers to find coverage, then merge inputs and test with sanitizers to find bugs.

Reproducing CI Crashes

Steps to Reproduce

  1. Update qa-assets:
  2. Locate crash case from CI output:
  3. Compile with sanitizers if needed
  4. Run fuzzer with the crash input:

Using Base64 Encoded Input

If the file isn’t in qa-assets, use the base64 from CI logs:

Contributing Coverage

If you find coverage-increasing inputs, submit them to bitcoin-core/qa-assets.
Every PR against Bitcoin Core is automatically tested against all qa-assets inputs. Contributing new inputs helps make Bitcoin Core more robust.

Building Fuzz Tests

Three ways to build fuzz tests:

1. BUILD_FOR_FUZZING=ON (Standard)

  • Forces on fuzz determinism
  • Skips proof-of-work checks
  • Disables random number seeding
  • Disables clock time
  • Assume() checks abort on failure
  • Only fuzz binary can be built (other binaries disabled)
Use for: Normal fuzzing and generating new inputs

2. BUILD_FUZZ_BINARY=ON with Debug (Optional Determinism)

  • Assume() checks abort on failure
  • Determinism enabled by default
  • Can disable determinism with FUZZ_NONDETERMINISM environment variable
  • Allows non-fuzz binaries to coexist
Use for: Reproducing fuzz failures in normal builds

3. BUILD_FUZZ_BINARY=ON with Release (Compile-only)

  • Fuzz binary builds but refuses to run
  • Determinism forced off
  • Assume() checks don’t abort
Use for: Ensuring fuzz tests compile and link

Coverage Reports

Generate fuzz coverage reports with Clang:
View at build/coverage_report/index.html. See also: Developer Notes - Compiling for Fuzz Coverage

Alternative Fuzzing Engines

AFL++ (American Fuzzy Lop)

Read AFL++ documentation for advanced usage.

Honggfuzz

Read Honggfuzz documentation for more information.

OSS-Fuzz Integration

Bitcoin Core participates in Google’s OSS-Fuzz program. Resources:
Bitcoin Core follows its own security disclosure policy, which may differ from Google’s standard 90-day disclosure window.

macOS Notes

Fuzzing support on macOS is not officially maintained. Recommendation: Fuzz on Linux for best results. Use Docker or a virtual machine on macOS. Reproducing crashes on macOS: Build the fuzz binary without a specific fuzzing engine:

External Resources

Best Practices

Fuzzing Strategy

  1. Start with corpus: Use qa-assets seed corpus for better initial coverage
  2. Run long sessions: Fuzzing may need days and millions of executions to reach deep targets
  3. Alternate modes: Periodically switch between sanitizer and non-sanitizer builds
  4. Merge corpora: Regularly merge new inputs back to qa-assets
  5. Focus on targets: Prioritize fuzzing of security-critical components

Writing Fuzz Harnesses

  • Keep harnesses simple and focused
  • Minimize state between iterations
  • Test one component or interface per harness
  • Add assertions to catch unexpected states
  • Avoid sources of non-determinism (random numbers, timestamps)
  • Document what the harness tests

Performance Tips

  • Use -max_len to limit input size for faster iteration
  • Run multiple instances in parallel (-fork=N)
  • Use corpus minimization (-merge=1)
  • Profile to ensure fuzzer spends time in target code, not setup