Three directions recorded, each grounded in what this session measured. **WordPress by flaw class, not by plugin** (tasks 5.8, 5.9). One plugin is a checkout; a class is a claim. WordPress's recurring shapes are narrow and each maps to a family that already exists -- unauthenticated wp_ajax_nopriv reaching $wpdb, a missing capability check, unescaped output, arbitrary file operations. Two of the four are blocked on gaps this session found: access control cannot be asked at all because PHP has no obligation facts (5.9), and unescaped output is blocked on the flat sanitizer list (5.6). The observable is a class-by-class table across several plugins saying found, missed, or out of scope -- a single found CVE is not the artifact worth teaching from. **Why a corpus of benchmarks is not enough on its own**, written into overview.md as the finding it is rather than an opinion. Twelve engine defects in one session against three real repositories, and the three gates stayed byte-identical through every fix. A gate that does not move is a corpus that could not see the bug. The reason is structural: a pair is a labelled function with a declared family and a known answer, so it cannot exercise which region to look at, what to spend, whether a capability is reachable, whether two entry points share a sink, whether the question fits in an argv slot, or whether a flow crosses a module. Three of the twelve were quiet failures where the tool reported clean and every honesty counter agreed -- which a benchmark score cannot express, because the score is computed from the verdicts that exist. The corpus is the holdout that stops per-repository tuning, and the wrong instrument for finding out whether the tool works. **ir-consolidation** (brief, unapproved) separates two questions the IR suggestion contains, which have opposite answers. The SOURCE IR is already carried rather than about to be added: semantic/ is 3218 lines, 16% of the codebase, measured at 2.2% entailment against the CPG's 41.3% and still running as a proposer -- and Vine and P-Code are binary IRs, so the work there is subtraction. The MISSING abstraction is C arithmetic, and there GhidraPAL's three-valued-logic abstract interpreter over Ghidra P-Code is exactly the right shape: "can this index exceed this allocation" is a value question, which is what a bit-level TVL domain answers and what every libpng overflow needs. Vine is dormant since 2009 and is not the candidate. The constraint is recorded as an ordering: question 1 lands before question 2 starts, or adopting an IR makes the growth problem worse. And the cost that matters is not lines but the number of things that can silently return nothing -- this session found three such in a single PHP scan, and a binary pipeline adds a build, a lifter and a domain, each able to produce an empty result that looks like a clean one. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
4.3 KiB
Brief: ir-consolidation — stop maintaining an IR we did not need, and adopt one we do
Status: brief only, unapproved.
Why now
"we should make sure our code base here doesn't explode. for example we have a custom IR. maybe vine (berkely) or another existing IR (rolf rolles had one) ... I know Ghidra has one we could steal"
Two different questions live inside that, and they have opposite answers. Keeping them apart is most of the value of this brief.
Question 1 — the source IR we already replaced and never retired
semantic/ is 3,218 lines, 16% of the codebase: a hand-written IR (ir.py, cst.py) and a flat taint
engine (taint.py), plus the overlay that runs them. model-grounded-detection measured that IR against
Joern's CPG and the result was decisive — 2.2% entailment against 41.3% — which is why the CPG was
adopted for the model layer. The flat IR was never removed, and still runs as a proposer in every scan.
So the outgrowth is not something we are about to add. It is something already carried.
Vine and P-Code do not help here. Both are binary IRs — Vine lifts executables (BitBlaze, UC Berkeley, GPL-2.0, last significant activity 2009), and P-Code lifts machine code. Our source languages are Python, PHP, JavaScript and Java, and the source IR we should adopt is the one we already have. The work is subtraction, not substitution:
- Can the CPG serve the overlay's job at equal or better recall?
- What does deleting
semantic/'s IR and taint cost, measured on the pair corpus and both repositories? - What has to stay?
obligations/reads the same facts and is a different analysis.
A candidate ~1,000-line deletion with a number attached, which is the only kind worth doing.
Question 2 — the abstraction we are missing, where an existing IR is exactly right
c/memory has produced zero true positives and three distinct classes of false one. The reason is
structural and now stated in the report (Req 5.6): it models unbounded string CALLS, while real C overflows
are arithmetic — an index, a pointer offset, a memcpy whose size is computed. Every one of libpng's is,
and its library code never calls a modelled string API at all.
"Can this expression exceed this allocation" is a value question, not a reachability one. Rolf Rolles'
GhidraPAL carries a three-valued-logic abstract interpreter over Ghidra P-Code — each bit definite 0,
definite 1, or unknown, with abstract transformers for INT_ADD, INT_MULT, INT_AND, the comparisons —
validated against concrete execution in its own tests. That is precisely the shape of analysis the C family
lacks, and it exists.
It is also the honest answer to a decision this project already took. model-grounded-detection chose
abstract interpretation over SMT, and chose to build the model from code because execution cannot be
assumed. A bit-level abstract interpreter over P-Code is still abstract interpretation and still static —
what changes is the artifact: it needs a built library rather than its source.
What this brief must decide
- Whether the source-IR subtraction is safe, measured rather than assumed.
- Whether the C arithmetic class is worth a second pipeline at all, or whether
c/memorystays scoped to string misuse and says so — which is where task 2.16 left it, deliberately. - If it is worth it: P-Code with a TVL domain, adapting GhidraPAL's transformers, versus writing one. Vine is dormant since 2009 and is not the candidate; Ghidra is maintained and P-Code has tooling.
- What a second pipeline costs in the one currency this project has been strict about — not lines, but the number of things that can silently return nothing. This session found three separate quiet failures in one PHP scan. A binary pipeline adds a build step, a lifter, and a domain, each of which can produce an empty result that looks like a clean one.
The constraint that governs it
Nothing here may grow the codebase without a measurement that justifies it, and the module audit
(benchmarks/measurements/2026-09-08-module-audit.json, currently 97 modules with no orphans) is the
instrument. A brief that ends in "adopt P-Code" and 3,000 new lines while semantic/ still carries 3,218
has made the problem worse, not better. Question 1 should land before Question 2 starts.