9.6 KiB
Fold Tool Maturity Plan
Date: 2026-06-03 Scope: post linked-r2/P-Code FP hardening. This is a maturity plan, not a blocker list for shipped functionality.
Objective
Move Fold from feature-complete prototypes toward an analyzer with measurable maturity:
- fewer false positives on real binaries/source trees;
- stable report contracts and provenance;
- bounded runtime/memory behavior;
- repeatable benchmark evidence before and after detector/backend changes;
- tests that expose poor architectural shortcuts, not just local detector bugs.
Guiding principle
Before adding more detectors, define and guard the analyzer behaviors we already rely on:
- Findings must have physical locations and backend provenance.
- Source, binary, Rust, Swift, and confirmation lanes must remain separately measurable.
- Heuristic binary findings must not be upgraded silently to proved without a sound model.
- Encoder/lifter failures degrade locally and visibly, not by aborting a scan.
- Benchmarks should include safe code that looks dangerous, not only vulnerable code.
Maturity scorecard
Track this per release or major backend change.
| Metric | Gate | Why it matters |
|---|---|---|
| Full test suite | 100% pass | Baseline correctness |
| Focused binary suite | 100% pass | r2/Ghidra/Capstone regressions |
| Locationless findings | 0 emitted | Report quality / agent usability |
| Backend provenance | 100% findings tagged | Lane separation |
| Known FP corpus regressions | 0 new must-not-report hits | Precision discipline |
| Known TP corpus regressions | 0 missing must-report hits | Recall discipline |
| Scan crash rate | 0 on benchmark corpus | Operational stability |
| Encoder/lifter fatal aborts | 0 | Degrade, do not abort |
| Runtime regression | <= 20% unless justified | Usability on real codebases |
| Peak RSS regression | <= 20% unless justified | Large-binary/codebase stability |
| Schema compatibility | golden JSON/SARIF keys stable | Downstream tooling |
Benchmark layers
1. Unit regression fixtures
Fast tests that run on every local validation.
pcode_encoder: P-Code semantics and sink modeling.pcode_summary: interprocedural write-loop summaries.- report filtering/dedup tests.
- Rust/Swift IR encoder round-trips and degradation behavior.
Required additions:
- read-only scanners are never write sinks:
memchr,strchr,strstr; - fortified wrappers are not unchecked copies:
__strncpy_chk,__memcpy_chk; - guarded arithmetic/copy patterns suppress hazards;
- machine-instruction gating for raw P-Code div/rem;
- malformed child IR degrades per-node.
2. Golden source corpus
Small C/C++ fixtures with paired vulnerable/safe cases.
For each rule:
- true positive: minimal exploitable/reachable shape;
- safe lookalike: same syntax, but guarded by range/null/bounds checks;
- unsupported construct: must degrade without false proof;
- generated/vendor case: should be suppressible/excludable.
Rules to cover first:
- null-deref;
- div-by-zero;
- OOB;
- signed overflow;
- taint to sink;
- UAF/double-free;
- uninitialized read.
Each fixture should assert:
- rule ID;
- confidence;
- physical location;
- backend;
- no extra findings beyond budget.
3. Golden binary corpus
Small compiled binaries plus optional real-system smoke scans.
Must include:
- import-stub adjacency (
memchrnext tomemcpy); - fortified libc wrappers;
- stripped binaries;
- PIE and non-PIE variants;
- AArch64 guarded branch idioms (
b.ls,b.hi,cbz,cbnz); - x86/x64 division and guard idioms;
- safe read-only scanners;
- real unguarded copy overflow fixture;
- large binary runtime smoke with capped functions.
Golden checks:
- must-report findings for real unsafe copy/div cases;
- must-not-report findings for guarded-safe cases;
- backend provenance remains
binary,binary-ir,binary-pcode-raw, orbinary-pcode-ssaas expected; - no locationless findings.
4. Cross-lane/hybrid corpus
Purpose: catch router and provenance regressions.
Cases:
- source-only finding;
- binary-only finding;
- hybrid run with both inputs;
- same conceptual bug observed in source and binary with distinct provenance;
- confirmation task generated from source/Rust findings.
Gates:
- hybrid does not hide source coverage;
- binary heuristics do not masquerade as source proofs;
- confirmation tasks preserve lane/backend and target identity.
5. Poor-design pressure tests
These are intentionally hostile to bad analyzer architecture.
| Pressure test | Bad design exposed |
|---|---|
| Generated/vendor code with many scary imports | no suppression strategy / noisy defaults |
| Huge function with many branches | path explosion / no budget discipline |
| Stripped binary with dense import stubs | fragile target-name resolution |
| Malformed Rust/Swift NDJSON | frontend crash instead of local degradation |
| Empty/missing source locations | report artifacts leaking to users |
| Unsupported P-Code ops near real hazards | proof over unconstrained nonsense |
| Large executable section | memory blowup in gadget scan |
| Same finding through multiple lanes | broken dedup/provenance |
A maturity benchmark should include safe pressure tests. If Fold reports many high-confidence findings on intentionally safe pressure tests, the model is overclaiming.
Harness plan
Create scripts/benchmarks/ with:
scripts/benchmarks/
├── run_source_goldens.py # done: source fixture goldens
├── run_binary_goldens.py # done: binary fixture/smoke goldens
├── run_hybrid_goldens.py # done: merge-only hybrid goldens
├── run_rust_swift_goldens.py # done: optional frontend goldens
├── runner_common.py # done: resource measurement/budget helper
├── compare_findings.py # done: report/spec comparator
├── summarize_maturity.py # done: umbrella runner
└── baselines/ # next gate: required compare inputs
├── source.json
├── binary.json
├── hybrid.json
└── performance.json
Required comparator behavior:
- fail if a
must_reportfinding is missing; - fail if a
must_not_reportfinding appears; - fail if a finding has no physical location;
- fail if a finding lacks backend/lane provenance;
- warn or fail on runtime/RSS regression over configured threshold;
- emit markdown and JSON summaries.
CI / local validation tiers
Fast local gate
ninja -C build/release
meson test -C build/release \
fold:pcode_encoder fold:pcode_summary fold:scan_e2e \
fold:binary_backend fold:cli_paths --print-errorlogs
git diff --check
Binary maturity gate
scripts/benchmarks/run_binary_goldens.py --build build/release --compare scripts/benchmarks/baselines/binary.json
Full maturity gate
meson test -C build/release --print-errorlogs
scripts/benchmarks/summarize_maturity.py --build build/release --all --compare scripts/benchmarks/baselines/
The next maturity increment is to make those baseline paths first-class: missing optional toolchains may produce structured skips, but malformed or missing required baseline files should fail closed rather than silently degrading to an un-compared smoke run.
Assertion/invariant maturity
See also docs/research/assertion-invariant-maturity-audit-2026-06-03.md.
Add or audit always-on invariants where violation corrupts analysis:
- every emitted finding has a physical location unless explicitly internal-only;
- every emitted finding has backend provenance;
- scan router mode resolves to exactly the intended backend set;
- Rust/Swift frontend failures are attached to the node/function, not whole scan aborts;
- P-Code call target annotation does not overwrite a raw call-site name with a synthesized SSA target;
- confirmation task/result JSON keys remain centralized and round-trippable;
- benchmark comparator fails closed when a baseline file is malformed.
Avoid assertions for recoverable environment issues:
- missing KLEE;
- missing optional radare2 dev libs when CLI fallback is allowed;
- unsupported architecture;
- absent compile database in binary-only mode.
Task backlog
Completed in the current working tree:
- Define Fold maturity scorecard.
- Build binary FP regression corpus.
- Build source precision benchmark corpus.
- Add benchmark harness and umbrella summary.
- Audit assertion and invariant policy.
- Create initial poor-design pressure tests.
Open next increments:
- Promote benchmark baselines into a required maturity gate with fail-closed malformed-baseline handling.
- Add baselines for source, binary, hybrid, and performance/RSS summaries.
- Keep expanding safe pressure tests for generated/vendor code, unsupported P-Code near hazards, and same-finding multi-lane provenance.
- Require the maturity summary in release/checkpoint validation once baselines stabilize across supported hosts.
Done criteria for this maturity phase
Current status:
- A one-command benchmark summary exists:
scripts/benchmarks/summarize_maturity.py --all. - Source, binary, hybrid, Rust, and Swift golden suites exist.
- Known curl-derived binary FPs are locked by unit tests and a binary smoke baseline.
- Portable generated binary fixtures now cover guarded division, scanner-vs-writer P-Code pressure, and a hardcoded secret positive control.
- Report location/provenance checks are automated in
compare_findings.py, including aggregate backend presence checks for hybrid/frontends. - The benchmark corpus includes vulnerable/safe lookalike pairs and positive controls.
- Runtime, CPU, and RSS metrics are recorded where
/usr/bin/timeexposes them; loose optional budgets are enabled on deterministic small fixtures.