7.5 KiB
Fold Production Source Complexity Audit
Date: 2026-06-03 Scope: production C++ in
src/andinclude/fold/, not benchmark harness code.
Summary
The highest complexity is concentrated in a few production orchestration and encoder files:
src/cli/scan_runner.cppsrc/symbolic/encoder.cppsrc/symbolic/rust_ir_encoder.cppsrc/symbolic/pcode_encoder.cppsrc/detect/security_walker.cpp- large enum/string mapping helpers in
ir/report/ghidra_lifter
The benchmark harness refactor was useful cleanup, but it was not the primary maturity risk. Fold's production complexity risk is orchestration coupling: detector registration, frontend-specific observers, reporting/profile stamping, and backend-specific passes are currently concentrated in large functions.
Methods
- Rough lexical complexity pass over
src/andinclude/fold/. clang-tidycognitive complexity check on current production diff files:readability-function-cognitive-complexity, threshold 30.- Focused Fold source scan over touched production files:
./build/release/src/cli/fold scan --mode source \
--compile-db build/release/compile_commands.json \
src/binary/binary_pcode_scan.cpp src/binary/ghidra_lifter.cpp \
src/binary/pcode_router.cpp src/binary/r2_backend.cpp \
src/cli/scan_runner.cpp src/symbolic/pcode_encoder.cpp \
src/symbolic/rust_ir_encoder.cpp \
--detectors security --format json
Top rough lexical complexity results
| Approx CC | LOC | Location | Function |
|---|---|---|---|
| 81 | 402 | src/symbolic/encoder.cpp:1339 |
encodeExpr |
| 57 | 133 | src/ir/expr_json.cpp:390 |
toJson |
| 55 | 128 | src/ir/expr_json.cpp:260 |
fromJson |
| 53 | 138 | src/symbolic/pcode_encoder.cpp:229 |
Encoder::step |
| 50 | 100 | include/fold/ir/pcode.hpp:163 |
opName |
| 49 | 99 | src/binary/ghidra_lifter.cpp:90 |
mapOp |
| 42 | 203 | src/detect/security_walker.cpp:1711 |
walkCFG |
| 39 | 466 | src/cli/scan_runner.cpp:2032 |
handleTranslationUnitImpl |
| 39 | 165 | src/symbolic/summary_encoder.cpp:403 |
walk |
| 32 | 218 | src/cli/fuzz_run_runner.cpp:244 |
runFuzzRun |
clang-tidy cognitive complexity highlights
Changed production files flagged by clang-tidy:
| Cognitive complexity | Location | Function |
|---|---|---|
| 335 | src/cli/scan_runner.cpp:1651 |
runClangDetectorsOnShard |
| 154 | src/cli/scan_runner.cpp:1852 |
nested CreateASTConsumer |
| 76 | src/symbolic/rust_ir_encoder.cpp:126 |
encodeRustNode |
| 75 | src/cli/scan_runner.cpp:290 |
buildProjectCallGraph |
| 61 | src/cli/scan_runner.cpp:298 |
nested CreateASTConsumer |
| 50 | src/cli/scan_runner.cpp:480 |
computeSummariesForFunctions |
| 46 | src/cli/scan_runner.cpp:2032 |
handleTranslationUnitImpl |
| 35 | src/cli/scan_runner.cpp:3139 |
runDetectorsOnShard |
| 35 | src/binary/r2_backend.cpp:174 |
runCommand |
| 34 | src/binary/r2_backend.cpp:874 |
decodeBase64Candidate |
Focused Fold scan findings
Focused source scan returned 26 user-facing findings across the current production diff files:
fold.int-overflow: 14fold.null-deref: 4fold.sign-conversion: 4fold.div-by-zero: 2fold.memory-leak: 1fold.uninit-read: 1
Many are bounded-model false positives on sizes/counters or nullable Clang API objects, but one finding revealed a real C++ undefined behavior risk:
Fixed during audit
src/cli/scan_runner.cpp:3355 and src/cli/scan_runner.cpp:3526 used:
1LL << (w - 1)
When w == 64, this computes 1LL << 63, which is undefined behavior for a
signed long long. These expressions built signed min/max bit-vector bounds in
Rust/Swift overflow observers.
Fix applied:
const std::uint64_t sign_bit = 1ULL << (w - 1);
z3::expr min_val = ctx.bv_val(sign_bit, w);
z3::expr max_val = ctx.bv_val(sign_bit - 1, w);
Validation passed:
ninja -C build/release
meson test -C build/release fold:rust_ir_encoder fold:rust_frontend fold:scan_e2e --print-errorlogs
git diff --check
Findings and recommendations
1. scan_runner.cpp is the primary complexity hotspot
runClangDetectorsOnShard owns too many responsibilities:
- quarantine filtering;
- incremental cache/fingerprint handling;
- detector selection booleans;
- detector profile construction;
- nested AST action and consumer definitions;
- per-TU watchdog/signal isolation;
- composite security detector orchestration;
- individual detector execution;
- Rust and Swift backend-specific Z3 observer definitions;
- BinaryIR/P-Code additive passes.
This makes detector additions risky because one file/function accumulates every new backend lane and every profile/provenance convention.
Recommended refactor:
- Extract detector selection/profile construction into a
DetectorPlanhelper. - Extract Clang TU execution into a
ClangShardRunnerorTranslationUnitRunner. - Move Rust/Swift IR observer definitions out of
scan_runner.cppinto frontend or symbolic helper files. - Keep
runDetectorsOnShardas a thin backend dispatcher.
2. Rust/Swift observer code should not live inside scan orchestration
The inline RustSafetyObserver and SwiftSafetyObserver duplicate emission
logic and symbolic overflow bound construction. This caused the 64-bit signed
shift UB in two places.
Recommended refactor:
- Create a shared language-IR observer helper, e.g.
src/symbolic/language_ir_observer.cppwith:- backend tag (
rust/swift); - detector enable flags;
- shared
emitFinding; - shared signed-bound helpers.
- backend tag (
- Keep Rust/Swift frontend-specific differences as small option structs.
3. P-Code encoder step is dense but bounded
Encoder::step is a large switch over P-Code operations. This is natural for an
interpreter, but hazard-specific branches are now mixed with basic value-modeling
branches.
Recommended refactor:
- Extract helpers:
stepMemory(op);stepBoolean(op);stepCompare(op);stepCall(op)already exists;stepDivision(op).
- Keep switch cases as dispatch only.
- Add unit tests around division-address gating and CBranch fallthrough behavior before refactoring.
4. Opcode/string mapping functions are large but low-risk
mapOp, opName, kindToString, fromJson, and SARIF/finding rule maps are
large switch/if mapping functions. They inflate lexical complexity but are less
risky than orchestration code.
Recommended refactor:
- Prefer table-driven maps generated from one canonical enum/key source where wire-format stability matters.
- Do not prioritize over
scan_runnerand observers unless they keep producing drift bugs.
5. Fold source scan should become a production-code review gate
The focused scan found the signed-shift UB. It also surfaced likely model false positives, but those are useful pressure cases for Fold itself.
Recommended benchmark extension:
- Add a production self-scan golden that runs a small subset such as
src/symbolic/rust_ir_encoder.cppandsrc/cli/scan_runner.cppwith a loosemust_not_reportlist for known false positives and a warning-only summary. - Do not make the full self-scan a hard gate until false positives are triaged.
Suggested sequencing
- Extract Rust/Swift language IR observers and signed-bound helpers.
- Introduce
DetectorPlanfor source detector selection/profile setup. - Extract
ClangShardRunner/ TU execution fromrunClangDetectorsOnShard. - Split
PcodeEncoder::stepinto operation-family helpers. - Table-drive enum/wire-format mapping only after behavior-critical refactors.