14.1 Fuzz Testing Dependencies Before Adoption¶
In 2014, security researcher Neel Mehta at Google discovered the Heartbleed vulnerability in OpenSSL through code review—a bug that had existed for two years in one of the internet's most critical libraries.1 In the years since, automated fuzzing has become one of the most effective techniques for finding security vulnerabilities that human reviewers and traditional testing miss, with Google's OSS-Fuzz project alone finding over 10,000 vulnerabilities in critical open source projects.2 When evaluating dependencies for your supply chain, checking whether a project undergoes continuous fuzzing—or running your own fuzz campaigns—provides evidence about code robustness that vulnerability databases cannot.
This section introduces fuzzing as a dependency evaluation technique, covering tools, integration approaches, and how to contribute findings back to the open source ecosystem.
Fuzzing Fundamentals¶
Fuzzing (or fuzz testing) is an automated testing technique that feeds random, malformed, or unexpected inputs to a program to discover bugs that cause crashes, hangs, or security vulnerabilities.
How Fuzzing Works:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Fuzzer Engine │────▶│ Target Code │────▶│ Monitor │
│ (generates │ │ (library, │ │ (detects │
│ inputs) │ │ function) │ │ crashes) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│◀──────────────── Feedback ───────────────────┘
│ (coverage, crashes)
Modern fuzzers use coverage-guided fuzzing: they monitor which code paths inputs exercise and mutate inputs to explore new paths. This intelligent exploration finds edge cases that random input generation would miss.
What Fuzzing Finds:
| Vulnerability Type | How Fuzzing Triggers It |
|---|---|
| Buffer overflows | Inputs exceeding expected lengths |
| Integer overflows | Large or negative numbers |
| Null pointer dereference | Missing or malformed fields |
| Use-after-free | Sequences triggering memory issues |
| Infinite loops | Inputs causing non-termination |
| Format string bugs | Special characters in strings |
| Injection vulnerabilities | Metacharacters in inputs |
Why Fuzzing Matters for Supply Chains:
Traditional security testing (code review, static analysis) misses bugs that only manifest with specific inputs. Fuzzing complements these techniques by:
- Exploring input spaces too large for manual testing
- Finding implementation bugs that reviewers overlook
- Providing evidence of code robustness
- Discovering vulnerabilities before attackers do
Security-focused organizations prioritize fuzzing parsing code in dependencies, where most memory safety bugs concentrate. Parsers handle untrusted input with complex state machines, making them prime targets. Extended fuzzing campaigns (months, not days) routinely discover crashes in popular libraries before they reach production.
Fuzzing Tools¶
Several fuzzing tools are available, each with different strengths.
OSS-Fuzz (Google):
OSS-Fuzz is Google's continuous fuzzing infrastructure for critical open source projects.
- Coverage: 1,000+ projects, 50,000+ bugs found, 13,000+ security vulnerabilities identified
- Cost: Free for accepted projects
- Infrastructure: Google-provided compute, continuous operation
- Integration: Automatic issue filing, tracking
Checking if a Dependency is in OSS-Fuzz:
# Check OSS-Fuzz project list via GitHub API
curl -s "https://api.github.com/repos/google/oss-fuzz/contents/projects" | \
jq -r '.[].name' | grep -i "project-name"
# Or browse directly:
# https://github.com/google/oss-fuzz/tree/master/projects
# For a specific project, check if its directory exists:
curl -s "https://api.github.com/repos/google/oss-fuzz/contents/projects/openssl" | \
jq -r '.name' 2>/dev/null && echo "Project is in OSS-Fuzz" || echo "Not found"
Projects in OSS-Fuzz receive ongoing security testing. This is a positive signal when evaluating dependencies.
AFL (American Fuzzy Lop):
AFL is the foundational coverage-guided fuzzer, now maintained as AFL++.
- Best for: C/C++ binaries with file input
- Approach: Genetic algorithm for input mutation
- Strengths: Battle-tested, extensive documentation
# Basic AFL++ usage
# Compile with instrumentation
afl-clang-fast -o target target.c
# Run fuzzer
afl-fuzz -i seeds/ -o output/ ./target @@
libFuzzer:
libFuzzer is LLVM's in-process fuzzer, integrated into the compilation toolchain.
- Best for: Library functions, APIs
- Approach: In-process, coverage-guided
- Strengths: Fast, low overhead, integrates with sanitizers
// Example fuzz target
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Call the function you want to fuzz
parse_input(data, size);
return 0;
}
# Compile with fuzzer and sanitizers
clang++ -fsanitize=fuzzer,address -o fuzz_target fuzz_target.cpp
# Run
./fuzz_target corpus/
Jazzer (Java):
Jazzer brings coverage-guided fuzzing to the Java ecosystem.
- Best for: Java and JVM languages
- Approach: Bytecode instrumentation
- Strengths: Works with existing Java code
// Example Jazzer fuzz target
class MyFuzzer {
public static void fuzzerTestOneInput(byte[] data) {
try {
MyParser.parse(data);
} catch (ParseException e) {
// Expected - not a bug
}
}
}
Atheris (Python):
Atheris is Google's fuzzer for Python code.
- Best for: Python libraries
- Approach: Coverage-guided, native extension support
import atheris
import sys
def test_one_input(data):
fdp = atheris.FuzzedDataProvider(data)
my_library.parse(fdp.ConsumeString(100))
atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()
Tool Selection Guide:
| Target | Recommended Tools |
|---|---|
| C/C++ libraries | libFuzzer, AFL++ |
| Java libraries | Jazzer |
| Python libraries | Atheris |
| Rust libraries | cargo-fuzz (libFuzzer) |
| Go libraries | go-fuzz |
| Network protocols | boofuzz, AFLNet |
Evaluating Dependency Robustness¶
Use fuzzing as part of dependency evaluation to assess code quality.
Pre-Adoption Fuzzing:
Before adopting a critical dependency:
- Check OSS-Fuzz status: Is it already being fuzzed?
- Review past findings: Check issue tracker for fuzzing-discovered bugs
- Run quick fuzz campaign: Even a few hours can reveal issues
Quick Fuzz Campaign:
For a C library you're considering:
# 1. Clone and build with instrumentation
git clone https://github.com/project/library.git
cd library
CC=afl-clang-fast ./configure
make
# 2. Create minimal seed inputs
mkdir seeds
echo "minimal valid input" > seeds/seed1
# 3. Fuzz for 24 hours
timeout 86400 afl-fuzz -i seeds/ -o findings/ ./library-test @@
# 4. Analyze crashes
afl-collect findings/ crashes/
What Results Indicate:
| Finding | Interpretation |
|---|---|
| No crashes in 24h | Good (but not conclusive) |
| Few crashes, quickly triaged | Healthy project with good practices |
| Many crashes, known issues | Project aware, possibly under-resourced |
| Many crashes, no prior awareness | Concerning—may indicate quality issues |
| Hangs or resource exhaustion | DoS vulnerability potential |
Robustness Signals:
Beyond crash counts, look for:
- Sanitizer integration: Does the project test with ASan/MSan/UBSan?
- Fuzz targets in repo: Does
fuzz/directory exist? - CI fuzzing: Is fuzzing part of continuous integration?
- Historical response: How quickly were past fuzzing bugs fixed?
Integrating Fuzzing into Vetting¶
Make fuzzing a formal part of your dependency vetting process.
Tiered Approach:
| Dependency Risk | Fuzzing Requirement |
|---|---|
| Critical (security-sensitive) | Must be in OSS-Fuzz or run internal campaign |
| High (core functionality) | Verify OSS-Fuzz status or fuzz entry points |
| Standard | Check for fuzzing infrastructure presence |
| Low (dev tools) | No fuzzing required |
Vetting Workflow Integration:
# Dependency Vetting Checklist - Fuzzing
### For Critical Dependencies:
- [ ] Is the project in OSS-Fuzz?
- Check: https://github.com/google/oss-fuzz/tree/master/projects
- [ ] If not in OSS-Fuzz, are there fuzz targets in the repo?
- Check: `fuzz/`, `fuzzing/`, `tests/fuzz/` directories
- [ ] Review fuzzing bug history
- Check issue tracker for "fuzzing", "fuzz", "crash"
- Assess fix velocity and quality
- [ ] Run internal fuzz campaign (if critical and no evidence of fuzzing)
- Duration: minimum 24 hours, ideally 1 week
- Document findings and report upstream
- [ ] Decision: Accept / Reject / Require upstream improvements
Automated Checks:
#!/bin/bash
# check_fuzzing_status.sh
PROJECT=$1
# Check OSS-Fuzz
if curl -s "https://api.github.com/repos/google/oss-fuzz/contents/projects/$PROJECT" | grep -q "name"; then
echo "✓ Project is in OSS-Fuzz"
else
echo "✗ Not in OSS-Fuzz"
fi
# Check for fuzz targets in repo
if curl -s "https://api.github.com/repos/$PROJECT/contents" | grep -qiE "fuzz|fuzzing"; then
echo "✓ Fuzz directory found"
else
echo "? No fuzz directory detected"
fi
Contributing Fuzzing Results Upstream¶
When your fuzzing discovers bugs, contributing findings upstream improves the ecosystem.
Responsible Disclosure:
Fuzzing often finds security vulnerabilities. Follow responsible disclosure:
- Report privately: Use security contact (SECURITY.md)
- Provide details: Crashing input, stack trace, analysis
- Allow fix time: Typically 90 days for security issues
- Coordinate disclosure: Work with maintainer on timeline
Contribution Types:
| Contribution | Value |
|---|---|
| Bug reports with inputs | Immediately actionable fixes |
| Fuzz targets | Ongoing prevention of similar bugs |
| OSS-Fuzz integration | Continuous protection |
| Fixes for discovered bugs | Complete contribution cycle |
Writing Effective Bug Reports:
## Fuzzing Bug Report
### Summary
Heap buffer overflow in parse_header() triggered by malformed input.
### Environment
- Library version: 2.3.4
- Compiler: clang 15.0 with ASan
- Fuzzer: libFuzzer, 48-hour campaign
### Reproduction
```bash
./library_test crash-input-abc123
Crashing Input¶
Attached: crash-abc123.bin (17 bytes)
Stack Trace¶
ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 4 at 0x... thread T0
#0 parse_header src/parser.c:147
#1 process_input src/main.c:89
Analysis¶
The parser reads 4 bytes at offset specified in header without bounds checking. Malformed header with offset > buffer_size triggers out-of-bounds read.
Suggested Fix¶
Add bounds check before read at parser.c:145
**OSS-Fuzz Integration:**
For critical projects not in OSS-Fuzz, consider contributing integration:
```bash
# Create OSS-Fuzz project structure
oss-fuzz/
├── projects/
│ └── myproject/
│ ├── Dockerfile # Build environment
│ ├── build.sh # Build script
│ └── project.yaml # Project metadata
Benefits of getting a project into OSS-Fuzz: - Continuous fuzzing with Google's infrastructure - Automatic bug filing - Regression testing - Coverage reporting
Continuous Fuzzing Infrastructure¶
For critical dependencies, establish ongoing fuzzing rather than one-time campaigns.
Continuous Fuzzing Setup:
Option 1: ClusterFuzz (Google Infrastructure):
- OSS-Fuzz uses ClusterFuzz internally
- For accepted projects, runs continuously
- Automatic regression detection
Option 2: Self-Hosted Continuous Fuzzing:
# Docker-based continuous fuzzing
version: '3'
services:
fuzzer:
build: ./fuzzing
volumes:
- ./corpus:/corpus
- ./crashes:/crashes
command: |
while true; do
timeout 3600 ./fuzz_target corpus/
sync
done
deploy:
replicas: 4
Option 3: CI Integration:
# GitHub Actions fuzzing
name: Fuzz
on:
schedule:
- cron: '0 0 * * *' # Daily
jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build fuzzer
run: |
clang++ -fsanitize=fuzzer,address -o fuzz fuzz.cpp
- name: Download corpus
uses: actions/download-artifact@v4
with:
name: fuzz-corpus
path: corpus/
- name: Fuzz
run: |
mkdir -p new_corpus
timeout 3600 ./fuzz corpus/ new_corpus/ || true
- name: Upload corpus
uses: actions/upload-artifact@v4
with:
name: fuzz-corpus
path: new_corpus/
Corpus Management:
Fuzzing effectiveness depends on good seed corpus:
# Build corpus from tests, examples, real data
find tests/ -type f -name "*.input" -exec cp {} corpus/ \;
# Minimize corpus (remove redundant inputs)
afl-cmin -i raw_corpus/ -o minimized_corpus/ ./target @@
# Merge new findings
./fuzz -merge=1 corpus/ new_findings/
Fuzzing Metrics and Success Indicators¶
Track metrics to assess fuzzing effectiveness.
Key Metrics:
| Metric | What It Measures | Target |
|---|---|---|
| Code coverage | Percentage of code exercised | > 70% for critical code |
| Edge coverage | Control flow edges exercised | Increasing over time |
| Bugs found | Crashes, sanitizer violations | Any is valuable |
| Execution speed | Inputs tested per second | Higher is better |
| Corpus size | Unique test cases | Should grow, then stabilize |
Coverage Reporting:
# Generate coverage report
LLVM_PROFILE_FILE="fuzz.profraw" ./fuzz corpus/
llvm-profdata merge -sparse fuzz.profraw -o fuzz.profdata
llvm-cov show ./fuzz -instr-profile=fuzz.profdata -format=html > coverage.html
When to Stop Fuzzing:
| Signal | Interpretation |
|---|---|
| Coverage plateaus | Most reachable code exercised |
| No new paths in 24h | Likely saturated current seeds |
| Bug rate drops to zero | Initial bugs found and fixed |
| Time/resource limits | Practical constraints |
Diminishing returns are expected—initial hours find most bugs.
Recommendations¶
For Security Practitioners:
-
Check OSS-Fuzz first. Before evaluating a critical dependency, verify if it's continuously fuzzed. OSS-Fuzz participation is a strong quality signal.
-
Run quick campaigns. Even 24 hours of fuzzing on entry points can reveal concerning issues.
-
Focus on parsers. Functions that parse untrusted input (JSON, XML, network protocols) are highest value targets.
For Developers:
-
Include fuzzing in vetting. Add fuzzing status to your dependency evaluation checklist.
-
Contribute findings upstream. When you find bugs, report them responsibly. Better yet, contribute fixes and fuzz targets.
-
Write fuzz targets for your code. Make your own libraries easier to fuzz. This helps your users and improves your security.
For Organizations:
-
Establish fuzzing infrastructure. For critical dependencies, continuous fuzzing provides ongoing assurance.
-
Define fuzzing requirements. Require OSS-Fuzz participation or internal fuzzing for security-sensitive dependencies.
-
Support OSS-Fuzz contributions. Help get your critical dependencies into OSS-Fuzz. The ecosystem benefits, and so do you.
Fuzzing provides evidence of robustness that other evaluation methods cannot. A library that has survived millions of malformed inputs is more trustworthy than one that hasn't been tested. While fuzzing doesn't guarantee security, its absence for critical dependencies is a warning sign worth heeding.
-
"The Heartbleed Bug," heartbleed.com, April 2014, https://heartbleed.com/ ↩
-
Google, "OSS-Fuzz: Five Years Later, and Rewarding Projects," Google Security Blog, January 2022, https://security.googleblog.com/2022/01/oss-fuzz-five-years-later-and-rewarding.html ↩