Files
BFM-decomp/cookbook/C0430.md
T

2.5 KiB
Raw Blame History

§389 ★★★ — h_norm IS BLIND TO INDEXED-GLOBAL RELOCS, SO FREE WORK BECOMES AN INVISIBLE SINGLETON (P31 S69; 31 stubs / 4,811 ins recovered, 8 banked same day)

The hole. sig_image.norm_stream tracks a pending lui-hi so it can neutralise %hi/%lo pairs — but it DROPS that pending hi the moment an R-type intervenes. The indexed-global triad is exactly that shape:

lui   $at, %hi(arr)
addu  $at, $at, idx      <-- R-type; the pending hi is dropped here
lw    r,   %lo(arr)($at)  <-- %lo survives into the hash

So two per-overlay copies of ONE function that differ only in a data symbol's ADDRESS hash to different h_norm. They are byte-identical modulo relocation and they are invisible to every hash-keyed consumer at once: seed_ref (d=0 tier), twin_sweep, dup_report, config/dedup.us.yaml, and the family maps' structural tier.

Measured consequence (2026-09-01). Beyond the 22 stubs with a d=0 hash twin, 31 more reachable open stubs (4,811 ins) were PURE twins of already-banked bodies at instruction edit distance 1–5 — family_remap.classify_member seconds all 31 as PURE. Ten were clean of jtbl carve blockers; remapping them and gating banked 8, at ~0 agent tokens. One exemplar (ov_SC06_033:0x80185f6c, 94 ins) served five open copies; ov_MAIN_012:0x8016ab6c (188 ins) serves five more.

The fix is a NEW TIER, NOT A NEW NORMALIZER. Do not change h_norm: every stored map, ledger and calibration in the project keys on it, and a re-hash invalidates all of them. Instead read through the hole with an edit-distance tier over reloc-normalized streams — tools/seed_ref.py --near [--max-d N], which scans all 213 binaries, reproduces all 22 hash twins as an R34 cross-check on every run, and ships R39 controls (positive 200/200 at d=0; random-pair base rate 1.17%).

THE LAW, and it generalises past this project. A conservative normalizer is SAFE for a DEDUP claim and UNSAFE as a FRONTIER JOIN. Dedup asks "are these certainly the same?" — under-matching there is harmless. A frontier join asks "is there anything close to this?" — and there, every missed match silently converts free mechanical work into an apparent singleton that a future session will pay an agent to re-derive from scratch. Audit any hash you use for BOTH questions; they want opposite error directions.

Diff tell: an open stub your card calls "no banked twin — derive from the .s", whose body is a per-location copy of engine code that exists in a sibling overlay. Run the near tier before believing a singleton verdict.