Summary
Add a coverage-guided fuzzing harness that drives the pool's public surface
(create → alloc/free → destroy, plus dynamic growth) through randomized operation
sequences under ASan/UBSan, and wire it into CI. Today the test suite is example-based
(doctest) and the benchmark is a fixed loop; there is no fuzz target anywhere in the
repo (grep -ri fuzz is empty).
This is a proposed improvement in the spec review (#105, Section 6 "missing tooling").
Motivation
Allocators are exactly the kind of stateful, pointer-manipulating component where fuzzing
earns its keep: the interesting bugs live in sequences (allocate-to-exhaustion, free in
LIFO vs. FIFO order, free-then-realloc across a growth event, interleaved foreign-pointer
rejections) rather than in any single call. A deterministic example test cannot enumerate
these; a coverage-guided fuzzer explores them automatically. The in-repo bug ledger
(ADR-0039) already shows this class of defect matters — a fuzzer is the cheapest way to
find the next one before a user does.
Design sketch
- New target under
src/test/cpp/it/d4np/memorypool/ (e.g. pool_fuzz.cpp) guarded by a
CMake option (PBR_MEMORY_POOL_BUILD_FUZZERS, default OFF), built with
-fsanitize=fuzzer,address,undefined on Clang.
LLVMFuzzerTestOneInput interprets the byte buffer as an opcode stream over a small
state machine: alloc, free(valid handle), free(NULL), free(foreign ptr),
free(double-free), grow (dynamic pools). The harness maintains its own shadow set of
live blocks so it can assert the invariants the pool promises:
- every
alloc returns either a distinct in-range pointer or NULL (fixed) / a valid
pointer from a new chunk (dynamic, ADR-0022);
- no two live allocations alias;
free of a foreign/out-of-range/NULL pointer is handled per
ADR-0012 and never
corrupts the free list;
- after freeing everything, capacity/high-water-mark match (cross-checked via
InstrumentedPool, ADR-0025).
- Provide a small seed corpus and document a short local OSS-Fuzz-style run in the
target's README.
CI
- Add a dedicated CI job (Clang, POSIX) that builds the fuzzer and runs it for a bounded
time (e.g. -max_total_time=60 -runs=…) on every PR, plus the seed corpus as a
regression gate. Keep it off the MSVC leg (libFuzzer is Clang-only), consistent with the
existing sanitizer-preset platform split.
- The fuzzer must be build-gated off by default so it never perturbs the release build
or the ADR-0014 benchmark methodology.
Scope & compatibility
- Test-only / additive. No product-code change required (may surface some).
SemVer-neutral; can ship in a PATCH or ride a MINOR. Per ADR-0004.
Acceptance criteria
References
Summary
Add a coverage-guided fuzzing harness that drives the pool's public surface
(
create→alloc/free→destroy, plus dynamic growth) through randomized operationsequences under ASan/UBSan, and wire it into CI. Today the test suite is example-based
(doctest) and the benchmark is a fixed loop; there is no fuzz target anywhere in the
repo (
grep -ri fuzzis empty).This is a proposed improvement in the spec review (#105, Section 6 "missing tooling").
Motivation
Allocators are exactly the kind of stateful, pointer-manipulating component where fuzzing
earns its keep: the interesting bugs live in sequences (allocate-to-exhaustion, free in
LIFO vs. FIFO order, free-then-realloc across a growth event, interleaved foreign-pointer
rejections) rather than in any single call. A deterministic example test cannot enumerate
these; a coverage-guided fuzzer explores them automatically. The in-repo bug ledger
(ADR-0039) already shows this class of defect matters — a fuzzer is the cheapest way to
find the next one before a user does.
Design sketch
src/test/cpp/it/d4np/memorypool/(e.g.pool_fuzz.cpp) guarded by aCMake option (
PBR_MEMORY_POOL_BUILD_FUZZERS, default OFF), built with-fsanitize=fuzzer,address,undefinedon Clang.LLVMFuzzerTestOneInputinterprets the byte buffer as an opcode stream over a smallstate machine:
alloc,free(valid handle),free(NULL),free(foreign ptr),free(double-free),grow(dynamic pools). The harness maintains its own shadow set oflive blocks so it can assert the invariants the pool promises:
allocreturns either a distinct in-range pointer orNULL(fixed) / a validpointer from a new chunk (dynamic, ADR-0022);
freeof a foreign/out-of-range/NULL pointer is handled perADR-0012 and never
corrupts the free list;
InstrumentedPool, ADR-0025).target's README.
CI
time (e.g.
-max_total_time=60 -runs=…) on every PR, plus the seed corpus as aregression gate. Keep it off the MSVC leg (libFuzzer is Clang-only), consistent with the
existing sanitizer-preset platform split.
or the ADR-0014 benchmark methodology.
Scope & compatibility
SemVer-neutral; can ship in a PATCH or ride a MINOR. Per ADR-0004.
Acceptance criteria
pool_fuzztarget builds under-fsanitize=fuzzer,address,undefinedand runs cleanon the seed corpus.
exercises both fixed and dynamic-growth pools.
docs/bugs/, ADR-0039) with the crashinginput as the reproducer.
References