12 KiB
Benchmark Next Increments Audit
Date: 2026-06-03 Scope: what it takes to add hybrid goldens, Rust/Swift goldens, performance/RSS budget gates, and portable binary fixtures to Fold's maturity benchmark harness.
Implementation status
This audit has been implemented in the current working tree:
- portable binary fixtures and
build_sourcebinary specs; - merge-only hybrid golden runner;
- Rust/Swift frontend golden runner with structured skips;
- wall/CPU/RSS measurement and optional loose budget gates;
- umbrella summary wiring for all implemented suites.
Current baseline
Implemented benchmark pieces:
scripts/benchmarks/compare_findings.py- validates report shape;
- checks physical locations;
- checks backend provenance;
- supports
must_report,must_not_report,min_total,max_total, andmax_by_rule.
scripts/benchmarks/run_binary_goldens.py- runs
fold scan --mode binary --binary ...; - supports optional host-specific smokes via
skip_if_missing; - supports generated fixtures via
build_source.
- runs
scripts/benchmarks/run_source_goldens.py- generates temporary
compile_commands.json; - runs
fold scan --mode source.
- generates temporary
scripts/benchmarks/run_hybrid_goldens.py- generates source compile DBs, resolves/builds binary fixtures, and runs merge-only hybrid scans.
scripts/benchmarks/run_rust_swift_goldens.py- runs Rust/Swift frontend scans with structured missing-tool skips.
scripts/benchmarks/runner_common.py- records wall/CPU/RSS metrics and enforces optional budget gates.
scripts/benchmarks/summarize_maturity.py- one-command JSON umbrella over all implemented suites.
- Existing shipped specs include binary, source, hybrid, Rust, and Swift goldens.
The comparator is intentionally dependency-free and has a SARIF/JSON report view only; runners handle scan invocation.
1. Hybrid golden runner
What it needs
A new runner should mirror run_source_goldens.py plus run_binary_goldens.py:
- spec directory:
tests/benchmarks/hybrid/; - runner:
scripts/benchmarks/run_hybrid_goldens.py; - build or resolve a source compile DB;
- resolve a binary path;
- invoke:
fold scan --mode hybrid \
--compile-db <tmp compile_commands.json> \
--binary <fixture binary> \
--source-detectors <...> \
--binary-detectors <...> \
--hybrid-strategy merge-only \
--format json --output <report>
Initial target should be merge-only. Hint-exchange strategies remain future work.
Fixture strategy
Use a tiny source fixture and matching tiny compiled binary fixture. The first hybrid golden should not require exact source/binary correlation yet; it should prove that both backends can contribute findings and provenance survives merge.
Candidate first fixture:
- source side:
tests/fixtures/security/div_const_zero.cpp/div_guarded.cpp; - binary side: existing
tests/fixtures/binary/bof_*or a new compiled small binary with a hardcoded string / simple null-deref heuristic; - expected:
- at least one
backend=sourcefinding; - at least one
backend=binaryorbackend=binary-*finding; - guarded source lookalike must not report;
- no locationless findings.
- at least one
Comparator gap
compare_findings.py can already match backend and rule IDs, but it cannot yet
assert aggregate backend presence such as require_backends: ["source", "binary"].
Options:
- encode with
must_reportentries that match known source and binary rules; - extend comparator with
expect.require_backends.
Recommendation: add require_backends because hybrid provenance is the point of
this suite.
Risks
- Exact binary findings may vary by architecture/compiler.
- Existing
bof_arm64/bof_x64are committed binaries, but a generated fixture would be more portable if built during the runner. - Hybrid returns 1 if either backend emits actionable findings; runner already accepts 0/1 patterns from source/binary runners.
Acceptance criteria
scripts/benchmarks/run_hybrid_goldens.py --build build/releaseruns at least one spec.summarize_maturity.py --allincludeshybrid_goldens.- First spec proves source + binary backend provenance in one report.
- Spec fails closed on missing required inputs unless explicitly marked skippable.
2. Rust/Swift golden runner
What it needs
A new language-frontend runner should cover fold scan --mode rust and
fold scan --mode swift without assuming both toolchains exist everywhere.
Proposed runner:
scripts/benchmarks/run_frontend_goldens.py
or split runners:
scripts/benchmarks/run_rust_swift_goldens.py
Spec directories:
tests/benchmarks/rust/
tests/benchmarks/swift/
Spec fields:
mode:rustorswift;paths: source file or crate/package directory;detectors;env: optional environment overrides such asFOLD_RUST_BIN;skip_if_missing_tools: true by default for Rust/Swift until CI images are pinned.
Existing fixture surface
Rust:
tests/fixtures/rust/security_crate/exists;- integration tests already locate
fold-rustviaFOLD_RUST_BINortools/fold-rust/target/release/fold-rust; - detectors include
rust-unsafe-api,rust-unchecked-index,rust-path-traversal,rust-command-injection, andrust-crypto-misuse.
Swift:
tests/fixtures/swift/Package.swiftandSources/fuzz_me.swiftexist;fold-swiftis optional through Meson-Dswift-frontend=true/false;- Swift mode is already routed in
scan_routerand forces serial execution.
Comparator gaps
compare_findings.py is enough for initial Rust/Swift findings if specs use
ruleId, backend, and message regexes.
Possible extension later:
require_confidence;require_correlationKind;- per-frontend degradation counters if profile/report properties expose them.
Risks
- Rust frontend depends on nightly/toolchain and
fold-rustbuild output. - Swift frontend depends on
swiftcand platform availability. - These suites should skip when tools are missing, but a skipped suite should be visible in the maturity summary.
Acceptance criteria
- Runner handles missing frontend binaries/toolchains as structured skips, not false passes or hard crashes.
- At least one Rust spec checks a must-report unsafe API and a safe/missing rule shape.
- At least one Swift spec checks a stable finding if current Swift detectors emit
one; otherwise the first Swift spec should be a report-shape/no-crash golden
with explicit
max_totaland provenance/location requirements for any emitted findings. summarize_maturity.py --allincludes Rust/Swift suite status.
3. Performance/RSS budget gates
What it needs
Current runners record wall-clock seconds only. A maturity budget gate needs structured resource measurement:
- wall time;
- CPU time when available;
- max RSS;
- optional Fold
--profileJSON fields.
On macOS, /usr/bin/time -l gives maximum resident set size. On Linux,
/usr/bin/time -v gives maximum resident set size. Python resource.getrusage
can capture child maxrss but units differ by platform and it may aggregate over
children. Recommendation: implement a small cross-platform Python measurement
wrapper using resource.getrusage(RUSAGE_CHILDREN) plus optional
platform-specific /usr/bin/time fallback only if needed.
Spec model
Add optional budget fields under expect:
"budgets": {
"max_wall_seconds": 10.0,
"max_cpu_seconds": 20.0,
"max_rss_mb": 512,
"profile_max_total_wall_ms": 10000
}
Budget gates should start loose and informational for local work. CI can tighten specific small fixtures once stable.
Integration options
- Embed measurement in each runner.
- Create shared helper module used by all benchmark runners.
Recommendation: shared helper to avoid drift:
scripts/benchmarks/runner_common.py
Potential contents:
run_fold_measured(cmd, cwd);load_json,resolve_path,find_fold_binary;- resource result schema.
Risks
- RSS is noisy across Apple Silicon/Linux and linked-r2 vs CLI-r2 modes.
- Cold caches make first run slower.
- Profile mode forces serial execution for source/Rust/Swift paths, so profile timing should not be compared directly against non-profile timing unless the spec opts in.
Acceptance criteria
- Benchmark summaries include
wall_seconds,cpu_seconds, andrss_mbwhen available. - Specs can set loose budgets and fail closed when exceeded.
- Initial budgets are applied only to small, deterministic source/binary
fixtures; host
/usr/bin/curlsmoke remains informational or skip-budgeted. - Docs explain platform noise and how to update budgets.
4. More portable binary fixtures
What it needs
The current real-binary curl smoke is useful but host-specific. Portable binary fixtures should be compiled from checked-in C/C++ source during setup or runner execution, or committed in controlled small forms for multiple architectures.
Recommended layout:
tests/benchmarks/binary/fixtures/
guarded_division.c
fortified_wrappers.c
scanner_vs_writer.c
hardcoded_secret.c
build_binary_fixtures.py
Generated outputs should live under the benchmark output directory, not be checked in unless there is a strong cross-toolchain reason.
First fixture set
-
guarded_division.c- contains actual machine division but guarded denominator;
- expected: no
fold.div-by-zero.
-
scanner_vs_writer.c- contains read-only scanners:
memchr,strstr,strchr; - contains controlled writer:
memcpyorstrncpyin safe shape; - expected: no P-Code OOB false positives from scanner/import-stub adjacency.
- contains read-only scanners:
-
fortified_wrappers.c- compiled with
_FORTIFY_SOURCE=2where supported; - includes
__*_chk-lowered patterns; - expected: fortified wrappers are not normalized to unchecked sinks.
- compiled with
-
hardcoded_secret.c- stable positive control for binary string/credential finding;
- expected: at least one
fold.hardcoded-credentialbackend binary finding.
Runner support
Extend run_binary_goldens.py or add helper support for specs with
build_source:
{
"name": "binary_guarded_division",
"build_source": "fixtures/guarded_division.c",
"compiler": "cc",
"cflags": ["-O1"],
"detectors": "security",
"expect": { ... }
}
If compiler is missing, fail closed for portable fixtures; unlike curl, these are core benchmark assets.
Risks
_FORTIFY_SOURCEbehavior varies by libc/platform; on macOS it may not lower exactly like glibc.- Division instruction selection varies under optimization.
- r2/lifter behavior can vary by architecture.
Mitigations:
- Use separate specs for portable generic fixtures and platform-specific fortified behavior.
- Include relaxed exact-count expectations; prefer
must_not_reportfor known FP signatures and stable positive controls for one finding. - Use
--binary-max-functionssmall caps only when function ordering is stable; otherwise scan all tiny fixture functions.
Acceptance criteria
- At least two generated binary fixtures are built and scanned in the binary golden suite.
- One fixture is a stable positive control.
- One fixture locks a P-Code false-positive shape independent of
/usr/bin/curl. - Curl smoke remains optional and complementary.
Recommended sequencing
- Portable binary fixtures first: they also feed hybrid and perf suites.
- Hybrid runner using one portable binary fixture plus existing source fixtures.
- Performance/RSS budget infrastructure around existing source/binary runners.
- Rust/Swift runner after deciding skip policy and stable Swift expectations.
This sequencing gives hybrid/perf deterministic inputs before adding toolchain- optional frontend complexity.