Skip to content

test(ci): add a coverage-guided fuzzing harness for the pool surface #108

Description

@danielPoloWork

Summary

Add a coverage-guided fuzzing harness that drives the pool's public surface
(createalloc/freedestroy, 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

  • pool_fuzz target builds under -fsanitize=fuzzer,address,undefined and runs clean
    on the seed corpus.
  • Harness asserts the no-alias, in-range, and foreign-pointer invariants above, and
    exercises both fixed and dynamic-growth pools.
  • A bounded CI fuzz job runs on every PR and gates on the seed corpus.
  • Any defect found is filed in the bug ledger (docs/bugs/, ADR-0039) with the crashing
    input as the reproducer.
  • README/spec §6 test-strategy and CI docs updated to mention the fuzzing tier.

References

Metadata

Metadata

Labels

enhancementNew feature or requesttestAdding or correcting tests (Conventional Commit: test)

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions