This document describes the fuzzing infrastructure for the robocodec library, including setup instructions, usage guidelines, and best practices for finding bugs and security vulnerabilities.
Fuzzing is an automated testing technique that provides random, invalid, or unexpected data as inputs to a program. The goals are to:
- Find crashes - Segmentation faults, panics, assertion failures
- Find hangs - Infinite loops, deadlocks, slow operations
- Find memory leaks - Unbounded memory growth
- Find logic errors - Incorrect handling of edge cases
┌─────────────────────────────────────────────────────────┐
│ libFuzzer Engine │
│ - Generates random/mutated test cases │
│ - Monitors for crashes, hangs, leaks │
│ - Minimizes failing test cases │
└────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Fuzz Target Function │
│ - Receives raw bytes from libFuzzer │
│ - Attempts to parse/decode data │
│ - Must handle panics gracefully │
└────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Robocodec Parsers │
│ - MCAP parser (mcap_parser) │
│ - ROS1 bag parser (bag_parser) │
│ - RRF2 parser (rrd_parser) │
│ - CDR decoder (cdr_decoder) │
│ - Schema parser (schema_parser) │
└─────────────────────────────────────────────────────────┘
Run the initialization script:
./scripts/fuzz_init.shOr manually:
# Install nightly Rust toolchain
rustup install nightly
# Install cargo-fuzz
cargo +nightly install cargo-fuzz --locked
# Build fuzz targets
cargo +nightly fuzz buildmake fuzzThis runs all fuzz targets for 30 seconds each, providing a quick check for obvious issues.
make fuzz-mcap # Fuzz MCAP parser only
make fuzz-bag # Fuzz bag parser only
make fuzz-cdr # Fuzz CDR decoder only
make fuzz-schema # Fuzz schema parser onlyFor more thorough testing:
# Run all fuzzers for 1 minute each
make fuzz-all
# Run single fuzzer with custom options
cargo +nightly fuzz run mcap_parser -- \
-timeout=10 \
-max_total_time=300 \
-jobs=4 \
-dict=fuzz/dictionaries/mcap.dictTests the MCAP format parser with arbitrary byte sequences. Validates:
- Magic number detection
- Record parsing
- Chunk handling
- Compression/decompression
- Message indexing
Dictionary: fuzz/dictionaries/mcap.dict contains MCAP magic numbers, opcodes, and common strings.
Tests the ROS1 bag format parser with arbitrary byte sequences. Validates:
- Header parsing
- Record parsing
- Chunk handling
- Message data extraction
- Connection tracking
Dictionary: fuzz/dictionaries/bag.dict contains bag magic, opcodes, and common message types.
Tests the RRF2 (Rerun Data) format parser with arbitrary byte sequences. Validates:
- Magic number detection
- Chunk parsing
- Arrow message handling
- Compression/decompression
Tests the CDR (Common Data Representation) decoder with arbitrary byte sequences. Validates:
- CDR header parsing
- Primitive type decoding
- Array handling
- String decoding
- Nested structure handling
Tests the ROS/IDL schema parser with arbitrary text sequences. Validates:
- Type parsing
- Field declaration parsing
- Array notation parsing
- Comment handling
- Multi-file dependencies
Dictionary: fuzz/dictionaries/schema.dict contains common types, field names, and IDL keywords.
A successful fuzzing run produces output like:
INFO: Seed: 1234567890
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 12 ft: 12 corp: 1/1b exec/s: 0 rss: 25Mb
#1024 NEW cov: 145 ft: 234 corp: 15/234b exec/s: 512 rss: 45Mb
...
Key metrics:
cov: Code coverage (number of edges covered)ft: Number of unique featurescorp: Number of interesting test cases in corpusexec/s: Executions per secondrss: Memory usage
When a crash is found, libFuzzer will report:
==91234==ERROR: libFuzzer: deadly signal
SUMMARY: libFuzzer: deadly signal
artifact_prefix='fuzz/artifacts/mcap_parser/'; Test unit written to fuzz/artifacts/mcap_parser/crash-abc123def456
The crashing input is saved to fuzz/artifacts/<target>/crash-<hash>.
-
Reproduce the crash:
cargo +nightly fuzz run mcap_parser fuzz/artifacts/mcap_parser/crash-abc123
-
Minimize the crash input:
cargo +nightly fuzz cmin mcap_parser fuzz/artifacts/mcap_parser/crash-abc123
-
Debug the crash:
- Add debug prints to the fuzz target
- Use
gdborlldbto investigate - Check for out-of-bounds access, use-after-free, etc.
-
Fix the bug and verify the fix:
# After fixing, verify the crash no longer occurs cargo +nightly fuzz run mcap_parser fuzz/artifacts/mcap_parser/crash-abc123
Provide existing test files as seed corpus for better coverage:
# Copy test files to corpus
cp tests/fixtures/*.mcap fuzz/corpus/mcap_parser/
# Run fuzzer with seed corpus
cargo +nightly fuzz run mcap_parserCreate dictionaries with format-specific magic numbers and common values:
# MCAP magic
"\x14\x08\xB2\xC1\x43\x49\x0A\x0A"
# Common opcodes
"\x01" # Header
"\x05" # Message
Run with dictionary:
cargo +nightly fuzz run mcap_parser -- -dict=fuzz/dictionaries/mcap.dictRun multiple fuzzing jobs in parallel:
cargo +nightly fuzz run mcap_parser -- -jobs=4 -workers=4Enable AddressSanitizer and UndefinedBehaviorSanitizer:
# Set environment variable
export RUSTFLAGS="-Z sanitizer=address"
# Run fuzzer
cargo +nightly fuzz run mcap_parser -- -sanitizers=addressAdd fuzzing to CI pipelines to catch regressions:
# GitHub Actions example
- name: Run fuzzers
run: |
./scripts/fuzz_init.sh
make fuzz
continue-on-error: true # Don't fail CI on fuzzing
- name: Upload corpus artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: fuzz-corpus
path: fuzz/corpus/When developing new fuzz targets, start with short runs:
cargo +nightly fuzz run new_target -- -timeout=1 -runs=1000Prevent infinite loops with timeouts:
cargo +nightly fuzz run mcap_parser -- -timeout=10Prevent memory exhaustion:
cargo +nightly fuzz run mcap_parser -- -max_len=1048576 # 1 MBAlways use catch_unwind in fuzz targets:
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
// Fuzzing logic here
}));cargo +nightly fuzz run mcap_parser -- -rss_limit_mb=512For reproducible results:
cargo +nightly fuzz run mcap_parser -- -seed=12345Install cargo-fuzz:
cargo +nightly install cargo-fuzz --lockedUpdate nightly:
rustup update nightlyEnsure the fuzz target has #![no_main] and uses fuzz_target! macro.
- Increase fuzzing time
- Use dictionaries for better coverage
- Add seed corpus from existing test files
- Check if the target is actually parsing the input
SPDX-License-Identifier: MulanPSL-2.0