High-performance cache policies and supporting data structures.
This guide shows how to add new fuzz targets to CacheKit. Thanks to dynamic target discovery, no CI/CD updates are required when adding new targets.
Create a new file in fuzz/fuzz_targets/:
// fuzz/fuzz_targets/my_module_arbitrary_ops.rs
#![no_main]
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::arbitrary::Arbitrary;
#[derive(Debug, Arbitrary)]
enum Operation {
Insert(u32, u32),
Remove(u32),
Get(u32),
Clear,
}
fuzz_target!(|ops: Vec<Operation>| {
let mut my_module = MyModule::new();
for op in ops {
match op {
Operation::Insert(k, v) => { my_module.insert(k, v); }
Operation::Remove(k) => { my_module.remove(&k); }
Operation::Get(k) => { let _ = my_module.get(&k); }
Operation::Clear => { my_module.clear(); }
}
}
});
fuzz/Cargo.tomlAdd a [[bin]] section:
[[bin]]
name = "my_module_arbitrary_ops"
path = "fuzz_targets/my_module_arbitrary_ops.rs"
test = false
doc = false
cd fuzz
cargo fuzz run my_module_arbitrary_ops -- -max_total_time=60
git add fuzz/fuzz_targets/my_module_arbitrary_ops.rs fuzz/Cargo.toml
git commit -m "Add fuzz target for my_module"
git push
Thatβs it! π The CI/CD pipeline will automatically:
*_arbitrary_ops)Follow this naming convention for optimal CI integration:
*_arbitrary_ops.rs)Purpose: Test random operation sequences CI: Runs in PR smoke tests (60s) + nightly deep fuzzing (1h) Recommended: Always create this for new modules
#[derive(Debug, Arbitrary)]
enum Operation {
// All public operations
}
fuzz_target!(|ops: Vec<Operation>| {
// Execute operations
});
*_stress.rs)Purpose: Heavy load with reference implementation validation CI: Runs in nightly deep fuzzing only (1h) Recommended: For critical data structures
fuzz_target!(|data: (Vec<(u32, u32)>, Vec<u32>)| {
let (inserts, removes) = data;
// System under test
let mut sut = MyModule::new();
// Reference implementation
let mut reference = std::collections::HashMap::new();
// Validate equivalence
for (k, v) in inserts {
sut.insert(k, v);
reference.insert(k, v);
}
for k in removes {
assert_eq!(sut.remove(&k), reference.remove(&k));
}
});
*_property_tests.rs)Purpose: Test specific invariants and properties CI: Runs in nightly deep fuzzing only (1h) Recommended: For complex invariants
fuzz_target!(|ops: Vec<Operation>| {
let mut module = MyModule::new();
for op in ops {
// Execute operation
match op {
// ...
}
// Validate invariants after each operation
assert!(module.is_consistent());
assert_eq!(module.len(), module.count());
assert!(module.capacity() >= module.len());
}
});
Leverage libfuzzer_sys::arbitrary::Arbitrary for input generation:
use libfuzzer_sys::arbitrary::Arbitrary;
#[derive(Debug, Arbitrary)]
enum Operation {
Insert { key: u32, value: String },
Remove { key: u32 },
}
Use early returns to skip invalid inputs:
fuzz_target!(|data: (usize, Vec<u32>)| {
let (capacity, items) = data;
// Skip unreasonable inputs
if capacity == 0 || capacity > 10_000 {
return;
}
// Test with valid inputs
let mut module = MyModule::with_capacity(capacity);
// ...
});
Check data structure invariants after operations:
fuzz_target!(|ops: Vec<Operation>| {
let mut list = IntrusiveList::new();
for op in ops {
// Execute operation
match op {
Operation::PushFront(val) => {
let id = list.push_front(val);
// Validate invariants
assert!(list.contains(id));
assert_eq!(list.front(), Some(id));
assert_eq!(list.len(), list.iter().count());
}
// ...
}
}
});
Compare against standard library types:
use std::collections::VecDeque;
fuzz_target!(|ops: Vec<Operation>| {
let mut ghost = GhostList::new(100);
let mut reference = VecDeque::new();
for op in ops {
match op {
Operation::Record(key) => {
ghost.record(key);
reference.push_back(key);
if reference.len() > 100 {
reference.pop_front();
}
}
Operation::Contains(key) => {
assert_eq!(ghost.contains(&key), reference.contains(&key));
}
}
}
});
Prevent OOM and timeouts:
fuzz_target!(|ops: Vec<Operation>| {
// Limit input size
if ops.len() > 10_000 {
return;
}
let mut module = MyModule::new();
let mut item_count = 0;
for op in ops {
match op {
Operation::Insert(k, v) => {
// Limit total items
if item_count >= 1_000 {
continue;
}
module.insert(k, v);
item_count += 1;
}
// ...
}
}
});
For thread-safe data structures:
use std::sync::Arc;
use std::thread;
fuzz_target!(|data: (Vec<Vec<Operation>>, u8)| {
let (op_lists, num_threads) = data;
let num_threads = (num_threads as usize % 8) + 1; // 1-8 threads
let module = Arc::new(MyThreadSafeModule::new());
let handles: Vec<_> = op_lists
.into_iter()
.take(num_threads)
.map(|ops| {
let module = Arc::clone(&module);
thread::spawn(move || {
for op in ops {
// Execute operations concurrently
}
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
});
cd fuzz
cargo fuzz run my_module_arbitrary_ops -- -max_total_time=60
cd fuzz
cargo fuzz run my_module_arbitrary_ops -- -max_total_time=3600
cd fuzz
cargo fuzz run my_module_arbitrary_ops -- -workers=4
cd fuzz
cargo fuzz run my_module_arbitrary_ops fuzz/artifacts/my_module_arbitrary_ops/crash-<hash>
Add a section to fuzz/README.md:
### MyModule (3 targets)
#### `my_module_arbitrary_ops`
Tests arbitrary sequences of insert, remove, get, and clear operations.
**Run**:
```bash
cd fuzz
cargo fuzz run my_module_arbitrary_ops
my_module_stressStress tests with heavy insertion/deletion and validates against HashMap.
Run:
cd fuzz
cargo fuzz run my_module_stress
my_module_property_testsTests specific invariants: length tracking, capacity bounds, etc.
Run:
cd fuzz
cargo fuzz run my_module_property_tests
## Troubleshooting
### Target Not Discovered
**Problem**: CI doesn't run your new target
**Solution**: Ensure it's registered in `fuzz/Cargo.toml`:
```toml
[[bin]]
name = "my_target"
path = "fuzz_targets/my_target.rs"
test = false
doc = false
Verify locally:
cd fuzz
cargo fuzz list | grep my_target
Problem: Fuzz target panics on all inputs
Solution: Add input validation and early returns:
fuzz_target!(|data: Vec<u32>| {
if data.is_empty() || data.len() > 10_000 {
return; // Skip invalid inputs
}
// ... rest of fuzzing logic
});
Problem: Operations are too slow or allocate too much
Solution: Add resource limits:
fuzz_target!(|ops: Vec<Operation>| {
if ops.len() > 1_000 {
return; // Limit operation count
}
let mut module = MyModule::new();
let mut size = 0;
for op in ops {
if size > 10_000 {
break; // Limit total size
}
// ... execute operation
}
});
Problem: Fuzzer isnβt exploring interesting inputs
Solution:
Arbitrarycargo fuzz coverageSee existing fuzz targets for complete examples:
fuzz/fuzz_targets/fixed_history_arbitrary_ops.rsfuzz/fuzz_targets/interner_stress.rsfuzz/fuzz_targets/lazy_heap_property_tests.rsfuzz/fuzz_targets/intrusive_list_arbitrary_ops.rsYour new fuzz target will automatically be:
cargo fuzz list*_arbitrary_ops (60 seconds)No workflow file updates needed!