Files
BFM-decomp/cookbook/C0139.md
T

2.8 KiB

§128 — A raw NUL in C source makes grep SILENTLY SKIP the file (P30 S28, 137 files)

Symptom. grep -rn func_XXXXXXXX src/ returns nothing for a function that is plainly defined in src/. corpus.stubs says it is not a stub (so: matched). The build is byte-identical. Every statement is true and they look contradictory.

Cause. The file contains a raw NUL byte, almost always a control character written straight into a character literal — the source reads == '<NUL>' where it should read == '\0':

if (*(char *)(p + 4) == '\0') { ... }     /* correct */
if (*(char *)(p + 4) == '<NUL>') { ... }  /* compiles the same; file is now BINARY */

file(1) reports data instead of C source, and grep treats any file containing NUL as binary and prints nothing — no warning, no error, exit 0. The file disappears from every grep-based audit and every hand search, invisibly.

Why no existing gate catches it. The byte-gate is structurally blind here (R34): the compiled bytes are correct, so it has nothing to say. cc1 accepts the literal. check-all stays green. The defect lives entirely in the readability of the source to tooling — a dimension no byte oracle measures. Same family as §124 (a scanner that cannot see something reports it is not there) and §126a (a bare except swallowing a coverage assertion), one layer lower: in the tool everyone reaches for first.

Scope when it was found: 137 files. Every _o0c/_o0e region created in one session — a templated body carried the NUL, so a single defective source propagated it fleet-wide in an afternoon. A defect that is invisible to grep is also invisible to the review that would have caught it spreading.

The oracle: tools/audit_text_sources.py / make audit-text-sources, in tools-health, coverage-asserted over every tracked .c/.h (R32). Fix is '<NUL>' → '\0', then re-gate to prove byte-neutrality (it is, but prove it).

§128a — a negative control must corrupt a SCRATCH COPY, never the tracked file

Proving the new guard fires, I injected a NUL into the real tracked file and restored it through nested shell escaping. The restore left '\\0' — an escaped backslash, a multi-character constant, not a NUL. That IS a semantic change, and R22 duly failed 139/140. Repaired and re-verified 140/140; nothing was ever committed.

The rule: test a guard against a throwaway copy under .run/. A negative control that mutates the artifact it is validating can introduce the exact defect it exists to detect — and the more convincing the control, the more dangerous the restore. Corollary, learned the same minute: a verification pipeline ending in grep -c PATTERN exits 1 when the count is 0, so the SUCCESS case reports failure. Read the output; an exit status is not the oracle (§125's rule 1, again).