nx_mmapbal.nx source
↩ module page · 1328 lines · 73257 B
1// nx_mmapbal.nx -- STATIC mmap/munmap BALANCE SCANNER over the .nx corpus.
2//
3// WHY THIS EXISTS (measured 2026-07-31, debt 1785515861): the ecosystem's ONLY leak instrument,
4// nx_leak_check, is DYNAMIC and daemon-only -- its `fleet` verb certifies organs that are RUNNING and
5// a LEAK verdict requires LK_MIN_AGE_S=120s of process age. One-shot organs (5365 gates + every CLI
6// organ) live for milliseconds and exit, so they can NEVER appear in a fleet scan and can NEVER reach
7// the age floor. That hole is why nx_gate_verdict.nx -- the D001 base class 150+ organs import --
8// leaked on EVERY call for ~13 days while every instrument reported healthy. Dynamic and static
9// detection are not redundant here; they cover disjoint halves of the fleet.
10//
11// THE RULE, stated once so it cannot drift: within ONE function, every sys_mmap must be matched by a
12// sys_munmap, UNLESS the signature RETURNS A POINTER -- that is ownership TRANSFER to the caller, not
13// a leak (gv_ctr is the correct exemplar: it returns the counter, which must outlive the call). A
14// function that allocates and returns i64 has no way to hand the memory back, so an unmatched mmap
15// there is a leak BY CONSTRUCTION, provable from the source alone with no runtime sampling.
16//
17// main() IS REPORTED SEPARATELY, NOT CONVICTED: it runs once per process and the kernel reclaims at
18// exit, so an unbalanced mmap in main is a style note, not a defect. Convicting it would bury the
19// real signal (helpers called in loops) under noise -- the ranked-wrong-thing failure.
20//
21// COMMENT LINES ARE SKIPPED so a header that merely MENTIONS sys_mmap cannot fabricate a finding.
22//
23// ⚠ KNOWN LIMITATION -- OWNERSHIP VIA STRUCT FIELD IS INVISIBLE (measured 2026-07-31).
24// mb_owns detects ownership transfer ONLY through a `-> *` RETURN TYPE. A function that hands its
25// allocation to the caller by STORING IT IN AN OUT-PARAM STRUCT FIELD is reported as a leak even
26// though it is correct. PROVEN CASE: nx_opaque_login.olg_ctx_setup_ttl mmaps 5 key buffers and
27// passes 3 to nx_auth_context_init, which does `ctx.opaque_skS_32 = ...` / `ctx.server_ed25519_priv_32
28// = ...` -- storing the POINTERS, not copying the bytes. Those live for the whole daemon lifetime;
29// freeing them would be a use-after-free on every login and would corrupt the server signing key.
30// CONSEQUENCE: *_init / *_ctx_setup / *_new functions are the FALSE-POSITIVE class of this scanner.
31// A headline count that does not exclude them OVERSTATES the defect. Treat any finding in an
32// initializer as UNPROVEN until the callee is read. Fixing this properly needs callee-aware
33// escape analysis (does the callee store the pointer?), which is a real rung, not a tweak.
34//
35// FAIL-LOUD ON COVERAGE (law L011): prints files_scanned / files_skipped / coverage_complete. A corpus
36// scanner that hides partial coverage presents partial-as-complete, which is the self-ceiling defect.
37//
38// WHY `deep` EXISTS AND `scan` CANNOT SUBSTITUTE FOR IT (measured 2026-08-14): `scan` streams every
39// finding and its summary block sits BELOW that stream, so BOTH transports truncate before the totals
40// are reached -- the MCP capture cap at 163,840 B and the async job capture cap at 1 MiB, both hit on
41// this organ on the same day. A caller using `scan` to answer a whole-corpus question silently
42// receives an ALPHABETICAL PREFIX that looks like a complete answer: the 163,840 B run stopped inside
43// nx_c* and never reached nx_docportal, nx_gallery or nx_hub_gw. Use `deep` for any POPULATION
44// question and `file` to attribute a single fix on a shared, concurrently-edited tree.
45// THESE THREE LINES ARE THE PUBLISHED CONTRACT: nx_toolgrammar harvests them verbatim into
46// knowledge/tool_grammar.conf, which nx_tools_api serves as this tool's MCP call grammar. A verb
47// missing HERE is invisible to every caller even though the code implements it -- `deep` and `file`
48// were exactly that, and the omission cost a near-duplicate rebuild of `deep` on 2026-08-14.
49//
50// nx_mmapbal scan [dir] walk dir (default buildroot/runtime) for .nx, report unbalanced funcs
51// nx_mmapbal deep [dir] RECURSIVE SUMMARY-ONLY: classified totals + coverage envelope -- USE THIS FOR POPULATION QUESTIONS
52// nx_mmapbal hot [dir] ENUMERATE the page-backed-in-a-loop worklist: one line per function, HOT-TOTAL last
53// nx_mmapbal file <path> ONE file: the only attributable verify on a shared tree (a total-count delta is not attributable)
54//
55// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
56// LAYERING (law): runtime/ CANNOT import _hdl_build/, and nx_gate_verdict_lib.nx (the verdict READER,
57// carrying gv_slurp/gv_len) is misplaced in _hdl_build/ -- so this organ cannot reuse it from here and
58// carries a minimal local slurp instead. That is the layering law winning over rule 15, not a DRY miss.
59// Lifting that lib to runtime/ via nx_liblift is filed separately; it is not safe to move mid-flight
60// while a sibling lane holds nx_gate_rollup, which imports it.
61import "nx_syscalls.nx"
62import "nx_gate_verdict.nx"
63
64const MB_DIRBUF: i64 = 262144
65const MB_FILECAP: i64 = 524288
66const MB_PATHCAP: i64 = 1024
67const MB_DIR_TYPE: i64 = 4
68const MB_NL_C: i64 = 10
69const MB_SLASH_C: i64 = 47
70const MB_SPACE_C: i64 = 32
71const MB_TAB_C: i64 = 9
72const MB_DOT_C: i64 = 46
73const MB_N_C: i64 = 110
74const MB_X_C: i64 = 120
75const MB_WORD: i64 = 8
76const MB_ST_SLOTS: i64 = 32
77const MB_S_FILES: i64 = 0
78const MB_S_FUNCS: i64 = 1
79const MB_S_BAD: i64 = 2
80const MB_S_MAIN: i64 = 3
81const MB_S_SKIP: i64 = 4
82const MB_S_SITES: i64 = 5
83// Added 2026-08-06 -- see the `deep` verb for why each of these exists.
84const MB_S_TRUNC: i64 = 6 // files that FILLED the read cap, i.e. were silently cut short
85const MB_S_INIT: i64 = 7 // unbalanced funcs whose name marks them the documented FP class
86const MB_S_INITSITES: i64 = 8 // their sites, kept OUT of the convictable headline
87const MB_S_DIRS: i64 = 9 // directories walked (1 == the old non-recursive behaviour)
88const MB_S_QOVF: i64 = 10 // directory-queue overflows; any is a coverage hole
89// SHAPE CLASSES (mode 2). A corpus total tells you HOW MUCH is leaking; it does not tell you which
90// codemod pays. These buckets rank the remediation by drop-in-ability, which is the only ordering
91// that matters when the total is five figures and hand edits are not an option.
92const MB_S_C_SHIM: i64 = 11 // calls ccz_cat_num but hand-rolls the mmap'd fd shim -> nxi_fd drop-in
93const MB_S_C_SHIMS: i64 = 12
94const MB_S_C_INLINE: i64 = 13 // open-coded %10 digit loop into an mmap'd scratch -> same output, needs proof
95const MB_S_C_INLINES: i64 = 14
96const MB_S_C_OTHER: i64 = 15 // everything else: a real buffer with a real lifetime, no free
97const MB_S_C_OTHERS: i64 = 16
98
99// Iterative directory worklist. Deliberately a QUEUE and not recursion: this organ must report
100// coverage honestly, and a recursion depth limit that trips is far harder to detect and report than
101// a queue slot that refuses. Overflow increments MB_S_QOVF and drops coverage_complete to 0.
102const MB_QCAP: i64 = 512
103const MB_DOT_ENT: *u8 = "."
104const MB_DOTDOT_ENT: *u8 = ".."
105
106func mb_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
107
108func mb_streq(a: *u8, b: *u8) -> i64 {
109 var i: i64 = 0
110 while a[i] != (0 as u8) {
111 if a[i] != b[i] { return 0 }
112 i = i + 1
113 }
114 if b[i] != (0 as u8) { return 0 }
115 return 1
116}
117
118// read a whole file into buf; returns bytes read, 0 if unreadable. FAIL-CLOSED: an unreadable file is
119// counted as SKIPPED and drops coverage_complete to 0 -- it is never silently treated as clean.
120func mb_slurp(path: *u8, buf: *u8, cap: i64) -> i64 {
121 let fd: i64 = sys_openat_rd(path)
122 if fd < 0 { return 0 }
123 var tot: i64 = 0
124 var go: i64 = 1
125 while go == 1 {
126 let r: i64 = sys_read(fd, ((buf as i64) + tot) as *u8, cap - tot)
127 if r <= 0 { go = 0 } else { tot = tot + r }
128 if tot >= cap { go = 0 }
129 }
130 sys_close(fd)
131 return tot
132}
133
134func mb_line_end(buf: *u8, s: i64, e: i64) -> i64 {
135 var i: i64 = s
136 while i < e {
137 if buf[i] == (MB_NL_C as u8) { return i }
138 i = i + 1
139 }
140 return e
141}
142
143func mb_starts(buf: *u8, s: i64, e: i64, needle: *u8) -> i64 {
144 var k: i64 = 0
145 while needle[k] != (0 as u8) {
146 if s + k >= e { return 0 }
147 if buf[s+k] != needle[k] { return 0 }
148 k = k + 1
149 }
150 return 1
151}
152
153// leading whitespace then "//" = a comment line; its mentions of sys_mmap are prose, not code
154func mb_is_comment(buf: *u8, s: i64, e: i64) -> i64 {
155 var i: i64 = s
156 var go: i64 = 1
157 while go == 1 {
158 if i >= e { return 0 }
159 let c: i64 = buf[i] as i64
160 if c == MB_SPACE_C { i = i + 1 } else {
161 if c == MB_TAB_C { i = i + 1 } else { go = 0 }
162 }
163 }
164 return mb_starts(buf, i, e, "//" as *u8)
165}
166
167// ⚠⚠STRING LITERALS ARE SKIPPED, FOR THE SAME REASON COMMENTS ARE. This organ has always skipped
168// comment lines "so a header that merely MENTIONS sys_mmap cannot fabricate a finding" -- and then
169// counted the identical text inside a QUOTED STRING, which is the other way source can quote source.
170// MEASURED 2026-08-15, on this very file: mb_loop_allocs contains the literal "sys_mmap(" as the needle
171// it searches for, so the scanner counted its own search pattern as an allocation and reported
172// `mmap=2 munmap=1` for a function with exactly one of each. A FALSE LEAK, in the leak detector,
173// caused by the detector's own source.
174// ★★★★★★A DETECTOR THAT SCANS SOURCE FINDS ITS OWN SEARCH PATTERN -- and the organ had already
175// learned this lesson for comments and not applied it one syntax over.
176// Escapes are honoured so a \" inside a literal does not end it early and re-expose the tail.
177const MB_QUOTE_C: i64 = 34
178const MB_BSLASH_C: i64 = 92
179func mb_count_range(buf: *u8, s: i64, e: i64, needle: *u8) -> i64 {
180 var cnt: i64 = 0
181 var ls: i64 = s
182 while ls < e {
183 let le: i64 = mb_line_end(buf, ls, e)
184 if mb_is_comment(buf, ls, le) == 0 {
185 var i: i64 = ls
186 var instr: i64 = 0
187 while i < le {
188 let c: i64 = buf[i] as i64
189 if instr == 1 {
190 if c == MB_BSLASH_C { i = i + 2 } else {
191 if c == MB_QUOTE_C { instr = 0; i = i + 1 } else { i = i + 1 }
192 }
193 } else {
194 if c == MB_QUOTE_C { instr = 1; i = i + 1 } else {
195 if mb_starts(buf, i, le, needle) == 1 { cnt = cnt + 1 }
196 i = i + 1
197 }
198 }
199 }
200 }
201 ls = le + 1
202 }
203 return cnt
204}
205
206// signature returns a pointer => ownership TRANSFER to the caller, which is correct, not a leak
207func mb_owns(buf: *u8, s: i64, e: i64) -> i64 {
208 var i: i64 = s
209 while i < e {
210 if mb_starts(buf, i, e, "-> *" as *u8) == 1 { return 1 }
211 i = i + 1
212 }
213 return 0
214}
215
216func mb_put_range(buf: *u8, s: i64, e: i64) -> i64 {
217 sys_write(1, ((buf as i64) + s) as *u8, e - s)
218 return 0
219}
220
221func mb_is_nx(nm: *u8, n: i64) -> i64 {
222 if n < 4 { return 0 }
223 if nm[n-3] != (MB_DOT_C as u8) { return 0 }
224 if nm[n-2] != (MB_N_C as u8) { return 0 }
225 if nm[n-1] != (MB_X_C as u8) { return 0 }
226 return 1
227}
228
229func mb_join(dir: *u8, nm: *u8, out: *u8) -> i64 {
230 var o: i64 = 0
231 var i: i64 = 0
232 while dir[i] != (0 as u8) { out[o] = dir[i]; o = o + 1; i = i + 1 }
233 out[o] = MB_SLASH_C as u8; o = o + 1
234 i = 0
235 while nm[i] != (0 as u8) { out[o] = nm[i]; o = o + 1; i = i + 1 }
236 out[o] = 0 as u8
237 return o
238}
239
240// ARENA WEIGHTING (2026-08-06). After the small-allocation bump arena landed in nx_syscalls.sys_mmap,
241// a SITE IS NO LONGER A PAGE. Requests <= NXA_SMALL_MAX are bump-allocated at ~48 bytes with no VMA of
242// their own; larger requests still cost a full page AND a kernel VMA per call. A raw site total now
243// overstates harm by roughly 85x across the cheap half, so this ruler must WEIGH what it counts or it
244// will keep reporting a five-figure alarm for a cost that has largely been priced away.
245// MB_ARENA_MAX MUST TRACK NXA_SMALL_MAX in nx_syscalls.nx. If the threshold moves and this does not,
246// the weighing lies in the SAFE-LOOKING direction, which is the one direction a leak ruler must never
247// drift.
248//
249// ⚠⚠IT DRIFTED, AND "MUST TRACK" IS WHY. MEASURED 2026-08-15: this read 64 while nx_syscalls has
250// declared NXA_SMALL_MAX = 256 since the arena landed. Every request in the 65..256 band is bump
251// allocated at ~48 bytes with no VMA, and this ruler was weighing all of them as a full page plus a
252// kernel VMA -- so the headline overstated harm across that whole band. The header predicted the
253// opposite drift and called that the dangerous one; this drift is the OTHER failure, crying wolf, and
254// ★A DETECTOR WITH FALSE POSITIVES IS WORSE THAN NONE BECAUSE EVERYONE LEARNS TO IGNORE IT.
255// ★★★★★★A COMMENT SAYING "MUST TRACK X" IS A LAW THAT HAS TO BE REMEMBERED, THEREFORE ONE THAT
256// WILL BE SKIPPED -- AND THE SECOND COPY OF A THRESHOLD IS THE DUPLICATE-RULER DEFECT WEARING A
257// CONSTANT. nx_syscalls is already imported here, so the value is now DERIVED and drift is impossible
258// by construction rather than by anybody's diligence.
259const MB_ARENA_MAX: i64 = NXA_SMALL_MAX
260const MB_S_W_ARENA: i64 = 17
261const MB_S_W_PAGE: i64 = 18
262const MB_S_W_UNK: i64 = 19
263const MB_S_W_TOT: i64 = 20
264
265// ARENA-ONLY: convicted functions whose EVERY unmatched request is <= NXA_SMALL_MAX (2026-08-15).
266// ★★★★★★A RULER MUST NOT DEMAND AN ACTION ITS OWN ALLOCATOR DEFINES AS A NO-OP. sys_munmap in
267// nx_syscalls opens with `if len <= NXA_SMALL_MAX { return 0 }` -- for a small request the free is a
268// LITERAL NO-OP, because the bytes came from a bump chunk and unmapping an interior pointer would tear
269// a hole in a chunk still holding other callers' live allocations. So "add the missing munmap" is not
270// a fix for these; it is a call that provably does nothing. Convicting them is the same shape as a
271// predicate that is equally true of the correct design.
272// This does NOT move MB_S_BAD: the legacy headline is untouched and every existing consumer keeps its
273// number. It SPLITS it, so a reader can see which part is the segfault-causing class the header cares
274// about ("CRITICAL for long-running loops ... the leak hits RLIMIT_AS -> mmap returns -12 -> SEGFAULT")
275// and which part is arena churn that costs ~48 bytes and no VMA.
276// ⚠UNKNOWN SIZES STAY CONVICTED. A site whose argument this organ cannot resolve is NOT counted as
277// arena-only -- moving an unresolved size into the reassuring bucket is the one direction a leak ruler
278// must never drift, which is this file's own standing rule.
279const MB_S_ARENAONLY: i64 = 21
280const MB_S_ARENAONLYSITES: i64 = 22
281// PAGE-BACKED and REPEATEDLY EXECUTED -- the class that actually exhausts vm.max_map_count.
282// ⚠⚠THE LEXICAL LOOP AXIS ALONE IS NOT THIS CLASS, AND I SHIPPED IT BELIEVING IT WAS. Found by using
283// it: nx_gallery_serve -- a daemon that has been UP on :18090 throughout -- reported `page_backed=35
284// of which in_a_loop=0`, while 35 of its unbalanced functions are HTTP request handlers taking
285// (rbuf, req, rn). They allocate once per CALL and the accept loop is in another function entirely, so
286// a lexical intra-function scan scores the estate's most dangerous leaks at ZERO.
287// ★★★★★★"REPEATEDLY EXECUTED" HAS TWO SOURCES AND A LEXICAL SCAN SEES ONLY ONE: a loop INSIDE the
288// function, or a long-lived process calling the function forever. Measuring one and naming it after
289// both is the same narrower-subject defect this organ was built to expose.
290// The second source is detectable per FILE with no call graph: a source that calls sys_listen/sys_accept
291// IS a server, and every unbalanced per-call allocation in it repeats for the life of the process.
292// The two reasons OVERLAP by construction, so the union is the worklist and the parts are printed
293// beside it rather than summed.
294const MB_S_HOT: i64 = 23 // union: the worklist
295const MB_S_HOT_LOOP: i64 = 24 // reason A: lexically inside a while body
296const MB_S_HOT_SRV: i64 = 25 // reason B: in a NON-forking server (handlers run in the long-lived parent)
297const MB_S_SRV_FORK: i64 = 26 // EXCLUDED: server that forks per connection -- the child's exit frees it
298
299// THE RATCHET (2026-08-15). D2 used to assert `unbalanced_funcs == 0` over a 102,396-function corpus.
300// That can never pass, so this gate was PERMANENTLY RED -- and ★★★★★★A DETECTOR THAT IS PERMANENTLY
301// RED IS ONE EVERYONE LEARNS TO IGNORE, which is the likeliest reason a scanner this good was not in
302// any roster. A ratchet against a recorded floor can be GREEN today, turns RED the moment somebody
303// adds a page-backed leak, and tightens itself when the number falls -- ★A RATCHET THAT DOES NOT
304// TIGHTEN WHEN YOU IMPROVE IS JUST A THRESHOLD.
305// It ratchets PAGE-BACKED, not the raw headline: the arena-only two thirds cannot be fixed by adding a
306// munmap, so holding them in the number would make the gate demand impossible work forever.
307// ⚠MISSING FLOOR IS THE THIRD STATE, NOT A PASS. An absent conf means this run has no baseline to
308// judge against, which is "I could not look", and a gate that treats that as success is the exact
309// shape of a green that means UNEXAMINED.
310// ⚠⚠STATED LIMITATION: this is a COUNT ratchet on a SHARED tree, so a rise says a leak appeared but
311// not WHOSE -- the estate's own law (nx_unwired) is a NAME SET for precisely that reason. Carrying
312// 6,394 names is the correct next rung and is named here rather than left implied.
313const MB_RATCHET_PATH: *u8 = "knowledge/status/mmapbal_pagebacked.conf"
314const MB_RATCHET_CAP: i64 = 4096
315
316// PER-FILE CONST TABLE (2026-08-06). 31pct of weighed sites passed a NAMED CONST rather than a literal,
317// so a literal-only weigher leaves a third of the corpus in UNKNOWN and the headline stays soft. This
318// resolves `const NAME: i64 = <decimal>` declared IN THE SAME FILE and looks up bare-identifier args.
319// SCOPE IS DELIBERATELY FILE-LOCAL: a const imported from another module stays UNKNOWN rather than
320// being guessed, because resolving across the import graph is a real rung and a wrong resolution would
321// move a site into ARENA -- the reassuring direction, which is the one this ruler must never drift.
322const MBC_MAX: i64 = 1024
323const MBC_NAMELEN: i64 = 48
324
325static mbc_names: *u8
326static mbc_vals: *i64
327static mbc_n: *i64
328
329func mbc_init() -> i64 {
330 if (mbc_n as i64) != 0 { return 0 }
331 mbc_names = sys_mmap(MBC_MAX * MBC_NAMELEN)
332 mbc_vals = sys_mmap(MBC_MAX * MB_WORD) as *i64
333 mbc_n = sys_mmap(16) as *i64
334 return 0
335}
336
337// 1 iff c is [A-Za-z0-9_] -- the identifier alphabet this corpus actually uses.
338func mbc_isident(c: i64) -> i64 {
339 if c == 95 { return 1 }
340 if c >= 48 { if c <= 57 { return 1 } }
341 if c >= 65 { if c <= 90 { return 1 } }
342 if c >= 97 { if c <= 122 { return 1 } }
343 return 0
344}
345
346// Rebuild the table from one file buffer. ONLY `const NAME: i64 = <decimal>` is accepted -- a computed
347// or hex initialiser is SKIPPED rather than half-parsed, so anything not certainly known stays UNKNOWN.
348func mbc_scan(buf: *u8, n: i64) -> i64 {
349 mbc_init()
350 mbc_n[0] = 0
351 var ls: i64 = 0
352 while ls < n {
353 let le: i64 = mb_line_end(buf, ls, n)
354 if mb_starts(buf, ls, le, "const " as *u8) == 1 {
355 let ns: i64 = ls + 6
356 var q: i64 = ns
357 var go3: i64 = 1
358 while go3 == 1 {
359 if q >= le { go3 = 0 } else {
360 if mbc_isident(buf[q] as i64) == 1 { q = q + 1 } else { go3 = 0 }
361 }
362 }
363 let ne: i64 = q
364 var eq: i64 = 0 - 1
365 var r: i64 = ne
366 while r < le {
367 if buf[r] == (61 as u8) { eq = r; r = le } else { r = r + 1 }
368 }
369 if eq > 0 {
370 var d: i64 = eq + 1
371 var go4: i64 = 1
372 while go4 == 1 {
373 if d >= le { go4 = 0 } else {
374 if buf[d] == (32 as u8) { d = d + 1 } else { go4 = 0 }
375 }
376 }
377 var v: i64 = 0
378 var digs: i64 = 0
379 var ok: i64 = 1
380 var go5: i64 = 1
381 while go5 == 1 {
382 if d >= le { go5 = 0 } else {
383 let c: i64 = buf[d] as i64
384 if c < 48 { ok = 0; go5 = 0 } else {
385 if c > 57 { ok = 0; go5 = 0 } else {
386 v = v * 10 + (c - 48)
387 digs = digs + 1
388 d = d + 1
389 }
390 }
391 }
392 }
393 let nl: i64 = ne - ns
394 if digs > 0 { if ok == 1 { if nl > 0 { if nl < MBC_NAMELEN {
395 let cnt: i64 = mbc_n[0]
396 if cnt < MBC_MAX {
397 let dst: *u8 = ((mbc_names as i64) + cnt * MBC_NAMELEN) as *u8
398 var k: i64 = 0
399 while k < nl { dst[k] = buf[ns + k]; k = k + 1 }
400 dst[nl] = 0 as u8
401 mbc_vals[cnt] = v
402 mbc_n[0] = cnt + 1
403 }
404 } } } }
405 }
406 }
407 ls = le + 1
408 }
409 return mbc_n[0]
410}
411
412// Value of the const named by buf[s,e), or -1 when THIS FILE does not declare it.
413func mbc_lookup(buf: *u8, s: i64, e: i64) -> i64 {
414 // ⚠⚠REFUSE ON AN UNBUILT TABLE INSTEAD OF DEREFERENCING IT. MEASURED 2026-08-15: the weigher used to
415 // be reachable only from the directory walk, which calls mbc_scan first; widening its call site to
416 // every mode reached it from the `file` verb, where the table had never been built -- mbc_n was
417 // still 0 and this line SIGSEGV'd at address 0. ★MOVING A CALL INTO A NEW CONTEXT MOVES IT AWAY
418 // FROM THE SETUP THAT MADE IT SAFE. The entry point now builds the table (the real fix), and this
419 // refuses as UNKNOWN if it ever happens again -- unknown keeps a site OUT of ARENA, which is the
420 // direction this ruler must never drift.
421 if (mbc_n as i64) == 0 { return 0 - 1 }
422 let nl: i64 = e - s
423 if nl <= 0 { return 0 - 1 }
424 if nl >= MBC_NAMELEN { return 0 - 1 }
425 let cnt: i64 = mbc_n[0]
426 var i: i64 = 0
427 while i < cnt {
428 let nm: *u8 = ((mbc_names as i64) + i * MBC_NAMELEN) as *u8
429 var k: i64 = 0
430 var same: i64 = 1
431 while k < nl {
432 if nm[k] != buf[s + k] { same = 0; k = nl } else { k = k + 1 }
433 }
434 if same == 1 { if nm[nl] == (0 as u8) { return mbc_vals[i] } }
435 i = i + 1
436 }
437 return 0 - 1
438}
439
440// Classify every sys_mmap( site in [s,e) by its LITERAL argument. A non-literal -- a named const or an
441// expression -- is counted UNKNOWN and NEVER assumed cheap: assuming would bias the headline downward,
442// and a ruler that drifts toward reassurance is worse than no ruler.
443func mb_weigh_range(buf: *u8, s: i64, e: i64, st: *i64) -> i64 {
444 var ls: i64 = s
445 while ls < e {
446 let le: i64 = mb_line_end(buf, ls, e)
447 if mb_is_comment(buf, ls, le) == 0 {
448 var i: i64 = ls
449 while i < le {
450 if mb_starts(buf, i, le, "sys_mmap(" as *u8) == 1 {
451 st[MB_S_W_TOT] = st[MB_S_W_TOT] + 1
452 var p: i64 = i + 9
453 var v: i64 = 0
454 var digits: i64 = 0
455 var lit: i64 = 1
456 var go2: i64 = 1
457 while go2 == 1 {
458 if p >= le { lit = 0; go2 = 0 } else {
459 let c: i64 = buf[p] as i64
460 if c == 41 { go2 = 0 } else {
461 if c < 48 { lit = 0; go2 = 0 } else {
462 if c > 57 { lit = 0; go2 = 0 } else {
463 v = v * 10 + (c - 48)
464 digits = digits + 1
465 p = p + 1
466 }
467 }
468 }
469 }
470 }
471 if digits == 0 { lit = 0 }
472 if lit == 0 {
473 // Not a literal. If the WHOLE argument is a bare identifier, resolve it against
474 // this file's const table. An expression (MB_ST_SLOTS * MB_WORD) stays UNKNOWN
475 // rather than being guessed -- a wrong resolution would move a site into ARENA,
476 // the reassuring direction.
477 let a: i64 = i + 9
478 var b2: i64 = a
479 var idok: i64 = 1
480 var go6: i64 = 1
481 while go6 == 1 {
482 if b2 >= le { idok = 0; go6 = 0 } else {
483 let c2: i64 = buf[b2] as i64
484 if c2 == 41 { go6 = 0 } else {
485 if mbc_isident(c2) == 1 { b2 = b2 + 1 } else { idok = 0; go6 = 0 }
486 }
487 }
488 }
489 var rv: i64 = 0 - 1
490 if idok == 1 { rv = mbc_lookup(buf, a, b2) }
491 if rv < 0 { st[MB_S_W_UNK] = st[MB_S_W_UNK] + 1 } else {
492 if rv <= MB_ARENA_MAX { st[MB_S_W_ARENA] = st[MB_S_W_ARENA] + 1 } else { st[MB_S_W_PAGE] = st[MB_S_W_PAGE] + 1 }
493 }
494 } else {
495 if v <= MB_ARENA_MAX { st[MB_S_W_ARENA] = st[MB_S_W_ARENA] + 1 } else { st[MB_S_W_PAGE] = st[MB_S_W_PAGE] + 1 }
496 }
497 }
498 i = i + 1
499 }
500 }
501 ls = le + 1
502 }
503 return 0
504}
505
506// Read the ratchet floor: the first run of decimal digits in the conf. Returns -1 when the file is
507// absent or holds no number, which the caller MUST treat as UNMEASURED rather than as zero.
508func mb_read_floor() -> i64 {
509 let b: *u8 = sys_mmap(MB_RATCHET_CAP)
510 let n: i64 = mb_slurp(MB_RATCHET_PATH, b, MB_RATCHET_CAP)
511 if n <= 0 { sys_munmap(b, MB_RATCHET_CAP); return 0 - 1 }
512 var i: i64 = 0
513 var v: i64 = 0
514 var got: i64 = 0
515 while i < n {
516 let c: i64 = b[i] as i64
517 if c >= 48 {
518 if c <= 57 { v = v * 10 + (c - 48); got = 1; i = i + 1 } else { if got == 1 { i = n } else { i = i + 1 } }
519 } else { if got == 1 { i = n } else { i = i + 1 } }
520 }
521 sys_munmap(b, MB_RATCHET_CAP)
522 if got == 0 { return 0 - 1 }
523 return v
524}
525
526// Tighten the floor. Announces, because a ratchet that moves in silence is indistinguishable from one
527// that never moved, and the next reader cannot tell an improvement from a stale file.
528func mb_write_floor(v: i64) -> i64 {
529 let fd: i64 = sys_openat_wr(MB_RATCHET_PATH, MODE_0644)
530 if fd < 0 {
531 gv_puts(" RATCHET-UNWRITABLE " as *u8); gv_puts(MB_RATCHET_PATH)
532 gv_puts(" -- the floor did NOT tighten; fix the path before trusting the next run\n" as *u8)
533 return 0 - 1
534 }
535 let b: *u8 = sys_mmap(64)
536 var t: *u8 = sys_mmap(32)
537 var m: i64 = v
538 var k: i64 = 0
539 if m == 0 { t[0] = 48 as u8; k = 1 }
540 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
541 var w: i64 = 0
542 while k > 0 { k = k - 1; b[w] = t[k]; w = w + 1 }
543 b[w] = 10 as u8
544 w = w + 1
545 sys_write(fd, b, w)
546 sys_close(fd)
547 sys_munmap(b, 64)
548 sys_munmap(t, 32)
549 return 0
550}
551
552// LOOP SCOPE (2026-08-15). This organ's own header names the real signal -- "the real signal (helpers
553// called in loops)" -- and then never measures it: an unmatched page allocation in a helper called ONCE
554// costs one page for the life of a process that is about to exit, while the SAME code inside a while
555// body is the RLIMIT_AS / vm.max_map_count exhaustion nx_syscalls documents with a dmesg-proven
556// SEGFAULT. Those two deserve opposite priorities and had one number.
557//
558// Lexical, single pass, no regex: track brace depth; a '{' that follows a `while` token opens a loop
559// body, and every depth so marked stays marked until its matching '}'. A sys_mmap( seen while any
560// depth is marked is inside a loop.
561// ⚠STATED IMPRECISION, because the next reader would otherwise trust this as exact: unbalancedness is
562// measured PER FUNCTION (na > nf), so this proves the function is unbalanced AND allocates in a loop --
563// it does NOT prove the specific unmatched site is the looping one. That is strictly stronger than
564// "unbalanced" and strictly weaker than per-site attribution, and it is the shape the header asks for.
565// ⚠Depths beyond MB_LOOPDEPTH_MAX are treated as IN a loop if any shallower depth is marked -- the
566// conservative direction, since the alternative would silently exonerate deeply nested code.
567// ⚠⚠THIS LOOP DETECTOR IS A KNOWN DUPLICATE AND IS NAMED AS ONE. `as_mmap_in_loop` in nx_assure
568// (rule R3) already walks brace depth with a per-depth "opened by a while" bit -- the same algorithm.
569// I did not run nx_capsearch before writing this, and ★★★★★★"I DIDN'T KNOW IT EXISTED" IS A RETRIEVAL
570// FAILURE, NOT A DISCOVERY.
571// It is NOT deleted, because this file's own header states the layering law that forbids the fix:
572// runtime/ CANNOT import _hdl_build/, and nx_assure lives there. So the duplication is FORCED, and the
573// honest response is to make the two AGREE rather than pretend one does not exist.
574// The incumbent was MORE CORRECT than my copy: it word-boundary-checks the `while` keyword and mine did
575// not, so mine matched any identifier ENDING in "while". ★A SECOND RULER IS NOT MERELY REDUNDANT -- IT
576// IS USUALLY THE WORSE OF THE TWO, BECAUSE THE INCUMBENT HAS ALREADY BEEN BITTEN. Semantics adopted.
577// (The purposes do differ: R3 flags ANY mmap in a loop as a rule smell; this one only asks the question
578// for functions ALREADY convicted as unbalanced AND page-backed.)
579const MB_LOOPDEPTH_MAX: i64 = 64
580// 1 iff a `while` KEYWORD starts at i -- not the tail of a longer identifier.
581func mb_while_at(buf: *u8, i: i64, e: i64) -> i64 {
582 if mb_starts(buf, i, e, "while" as *u8) == 0 { return 0 }
583 if i == 0 { return 1 }
584 let p: i64 = buf[i-1] as i64
585 if p >= 97 { if p <= 122 { return 0 } }
586 if p >= 65 { if p <= 90 { return 0 } }
587 if p >= 48 { if p <= 57 { return 0 } }
588 if p == 95 { return 0 }
589 return 1
590}
591const MB_BRACE_OPEN: i64 = 123
592const MB_BRACE_CLOSE: i64 = 125
593// ⚠⚠COMMENTS AND STRINGS ARE SKIPPED HERE TOO, AND THAT IS NOT COSMETIC: this walks BRACE DEPTH, so a
594// '{' or '}' inside a comment or a quoted string does not merely add a spurious match -- it CORRUPTS
595// THE DEPTH for the rest of the function and every loop verdict after it. The first cut of this scanned
596// raw bytes, and its own explanatory comment contained a brace. ★THE FIX FOR ONE SYNTAX IS THE FIX FOR
597// THE OTHER; EXTRACT IT, DO NOT RE-TYPE IT SOMEWHERE ELSE AND FORGET A CASE.
598func mb_loop_allocs(buf: *u8, s: i64, e: i64) -> i64 {
599 let mark: *i64 = sys_mmap(MB_LOOPDEPTH_MAX * MB_WORD) as *i64
600 var z: i64 = 0
601 while z < MB_LOOPDEPTH_MAX { mark[z] = 0; z = z + 1 }
602 var depth: i64 = 0
603 var marked: i64 = 0
604 var pending: i64 = 0
605 var hits: i64 = 0
606 var ls: i64 = s
607 while ls < e {
608 let le: i64 = mb_line_end(buf, ls, e)
609 if mb_is_comment(buf, ls, le) == 0 {
610 var i: i64 = ls
611 var instr: i64 = 0
612 while i < le {
613 let c: i64 = buf[i] as i64
614 if instr == 1 {
615 if c == MB_BSLASH_C { i = i + 2 } else {
616 if c == MB_QUOTE_C { instr = 0; i = i + 1 } else { i = i + 1 }
617 }
618 } else {
619 if c == MB_QUOTE_C { instr = 1; i = i + 1 } else {
620 if c == MB_BRACE_OPEN {
621 depth = depth + 1
622 if pending == 1 {
623 if depth < MB_LOOPDEPTH_MAX { mark[depth] = 1 }
624 marked = marked + 1
625 pending = 0
626 }
627 i = i + 1
628 } else {
629 if c == MB_BRACE_CLOSE {
630 if depth < MB_LOOPDEPTH_MAX {
631 if mark[depth] == 1 { mark[depth] = 0; marked = marked - 1 }
632 }
633 if depth > 0 { depth = depth - 1 }
634 i = i + 1
635 } else {
636 if mb_while_at(buf, i, le) == 1 { pending = 1; i = i + 5 } else {
637 if mb_starts(buf, i, le, "sys_mmap(" as *u8) == 1 {
638 if marked > 0 { hits = hits + 1 }
639 i = i + 9
640 } else { i = i + 1 }
641 }
642 }
643 }
644 }
645 }
646 }
647 }
648 ls = le + 1
649 }
650 sys_munmap(mark, MB_LOOPDEPTH_MAX * MB_WORD)
651 return hits
652}
653
654// 1 iff buf[s,e) ends with needle.
655func mb_ends_at(buf: *u8, s: i64, e: i64, needle: *u8) -> i64 {
656 var nl: i64 = 0
657 while needle[nl] != (0 as u8) { nl = nl + 1 }
658 if e - nl < s { return 0 }
659 var k: i64 = 0
660 while k < nl {
661 if buf[e - nl + k] != needle[k] { return 0 }
662 k = k + 1
663 }
664 return 1
665}
666
667// 1 iff the function NAME -- the text between "func " and its first '(' -- ends in one of the
668// suffixes this organ's own header names as the ownership-via-struct-field FALSE-POSITIVE class
669// (*_init / *_ctx_setup / *_new). SUFFIX-ANCHORED AT THE '(' ON PURPOSE: a plain substring search
670// would also match a function merely MENTIONING _new in a parameter name, and over-exempting is
671// strictly worse than over-reporting here -- it hides a real leak behind a plausible label. This
672// only ever moves a finding into a separate bucket; nothing is dropped.
673func mb_is_initclass(buf: *u8, s: i64, e: i64) -> i64 {
674 var p: i64 = s
675 var lp: i64 = 0 - 1
676 while p < e {
677 if buf[p] == (40 as u8) { lp = p; p = e } else { p = p + 1 }
678 }
679 if lp < 0 { return 0 }
680 if mb_ends_at(buf, s, lp, "_init" as *u8) == 1 { return 1 }
681 if mb_ends_at(buf, s, lp, "_ctx_setup" as *u8) == 1 { return 1 }
682 if mb_ends_at(buf, s, lp, "_new" as *u8) == 1 { return 1 }
683 return 0
684}
685
686// scan one file buffer for unbalanced functions; accumulates into st.
687// mode 0 = LEGACY (print every finding, no classification -- byte-identical to the pre-2026-08-06
688// behaviour, so `scan` and `file` remain the baseline oracle this organ is measured against)
689// mode 1 = DEEP (silent, and the initializer class is diverted out of the convictable headline)
690func mb_scan_buf(path: *u8, buf: *u8, n: i64, st: *i64, mode: i64) -> i64 {
691 // Computed ONCE per file, not per function: does this source run a server loop? A file that calls
692 // sys_listen or sys_accept keeps calling its handlers for the life of the process, so an unbalanced
693 // per-call allocation in it repeats without bound even with no loop in sight.
694 // ★★★★★★A LEAK IN A FORKED CHILD DIES WITH THE CHILD, SO A FORKING SERVER IS THE SAFEST CASE
695 // FOR THIS QUESTION, NOT THE MOST DANGEROUS. The first cut of this axis counted every accepting
696 // file as hot and was BACKWARDS for the estate's dominant server shape. MEASURED on
697 // nx_gallery_serve: it reported 35 hot handlers, and the accept loop is
698 // let pid: i64 = sys_fork()
699 // if pid == 0 { ...handle... sys_exit(0) }
700 // -- every handler runs in a short-lived child the kernel reclaims at exit. All 35 were FALSE.
701 // The daemon's own comments show its author already designs for this ("build the indexes in a
702 // CHILD so its big reads don't persist in the long-lived parent VA").
703 // ⚠STATED IMPRECISION, and it drifts REASSURING, which is the direction this ruler must never
704 // drift -- so it is named loudly rather than buried: the fork evidence is FILE-level, so a server
705 // that forks only at BOOT and serves requests in the parent is wrongly excluded here. Separating
706 // those needs call-graph reachability (is this handler reached from the parent's accept path?),
707 // which is a real rung, not a tweak. Until then the two server kinds are counted SEPARATELY and
708 // printed, so the excluded population is visible instead of silently absorbed.
709 var is_server: i64 = 0
710 if mb_count_range(buf, 0, n, "sys_listen(" as *u8) > 0 { is_server = 1 }
711 if mb_count_range(buf, 0, n, "sys_accept(" as *u8) > 0 { is_server = 1 }
712 var forks: i64 = 0
713 if mb_count_range(buf, 0, n, "sys_fork(" as *u8) > 0 { forks = 1 }
714 var srv_hot: i64 = 0
715 if is_server == 1 { if forks == 0 { srv_hot = 1 } }
716 var ls: i64 = 0
717 while ls < n {
718 let le: i64 = mb_line_end(buf, ls, n)
719 if mb_starts(buf, ls, le, "func " as *u8) == 1 {
720 var fe: i64 = le + 1
721 var scanning: i64 = 1
722 while scanning == 1 {
723 if fe >= n { scanning = 0 } else {
724 let xe: i64 = mb_line_end(buf, fe, n)
725 if mb_starts(buf, fe, xe, "func " as *u8) == 1 { scanning = 0 } else { fe = xe + 1 }
726 }
727 }
728 st[MB_S_FUNCS] = st[MB_S_FUNCS] + 1
729 let na: i64 = mb_count_range(buf, ls, fe, "sys_mmap(" as *u8)
730 let nf: i64 = mb_count_range(buf, ls, fe, "sys_munmap(" as *u8)
731 if na > nf {
732 if mb_owns(buf, ls, le) == 0 {
733 if mb_starts(buf, ls, le, "func main(" as *u8) == 1 {
734 st[MB_S_MAIN] = st[MB_S_MAIN] + 1
735 } else {
736 // CLASSIFY, NEVER SILENTLY EXCLUDE. In mode 1 an initializer-class finding
737 // goes to its own counter instead of the convictable one; in mode 0 nothing
738 // moves, so the legacy verdict is unchanged.
739 var icls: i64 = 0
740 if mode >= 1 { icls = mb_is_initclass(buf, ls, le) }
741 if icls == 1 {
742 st[MB_S_INIT] = st[MB_S_INIT] + 1
743 st[MB_S_INITSITES] = st[MB_S_INITSITES] + na - nf
744 } else {
745 st[MB_S_BAD] = st[MB_S_BAD] + 1
746 st[MB_S_SITES] = st[MB_S_SITES] + na - nf
747 }
748 // SHAPE CLASSIFICATION (mode 2). Deliberately three coarse buckets tested in
749 // priority order, not a taxonomy: ccz_cat_num present means the function already
750 // calls the correct zero-alloc primitive and only hand-rolled the freeing shim
751 // around it, so nxi_fd is a literal drop-in. An open-coded %10 loop is the same
752 // OUTPUT reached the long way -- migratable, but it must be PROVEN byte-equal
753 // per function rather than assumed. Everything else is a buffer with a real
754 // lifetime and is NOT codemod work.
755 // ONLY classify what actually landed in the CONVICTABLE bucket. The first cut
756 // of this ran for every convicted function including the init-class ones, which
757 // had already been diverted to their own counter -- so the buckets summed to
758 // BAD + INIT and overcounted by exactly 173. The K2 partition check caught it;
759 // without that check it would have been reported as fact.
760 // ★COMPOSE THE WEIGHER, DO NOT RE-PARSE SIZES. Snapshot the three size counters,
761 // run the ONE size parser over this function's range, read the delta back: that
762 // is this function's own sites, measured by the same code that produces the
763 // corpus weights, so a second size parser can never drift from the first.
764 // Runs for EVERY convicted function, not just mode 2, because `deep` -- the verb
765 // that answers population questions -- is mode 1 and would otherwise have no
766 // size information at all.
767 if icls == 0 {
768 let a0: i64 = st[MB_S_W_ARENA]
769 let p0: i64 = st[MB_S_W_PAGE]
770 let u0: i64 = st[MB_S_W_UNK]
771 mb_weigh_range(buf, ls, fe, st)
772 // ★★★★★★A RULER THAT CANNOT TELL WHICH SITE IS UNMATCHED WILL KEEP CONVICTING
773 // CODE THAT IS ALREADY FIXED. Unbalancedness is per-FUNCTION (na > nf) but the
774 // weigher sees EVERY site, so a function that correctly frees its 2 KiB page
775 // buffer and still holds a 64-byte arena scratch kept reporting PAGE-BACKED --
776 // MEASURED on nx_estate right after fixing it. Left alone, every function fixed
777 // by this campaign stays RED forever and the ratchet can never reach zero, which
778 // is the permanently-red pathology arriving through a different door.
779 // The sound test with the data available: if the count of page-backed-or-unknown
780 // sites is covered by the munmap count, then every expensive allocation is
781 // plausibly freed and whatever remains unmatched must be arena -- where
782 // sys_munmap is a documented no-op and there is nothing to fix.
783 // ⚠Still per-function, not per-site: it says the pages COULD all be covered, not
784 // that they are the ones that were. Wrong in the direction of exonerating, so it
785 // is bounded by the same call that makes it useful -- and unknown sizes count
786 // AGAINST exoneration, which keeps the reassuring direction expensive.
787 let dpages: i64 = (st[MB_S_W_PAGE] - p0) + (st[MB_S_W_UNK] - u0)
788 var arena_only: i64 = 0
789 if dpages <= nf { arena_only = 1 }
790 if arena_only == 1 {
791 st[MB_S_ARENAONLY] = st[MB_S_ARENAONLY] + 1
792 st[MB_S_ARENAONLYSITES] = st[MB_S_ARENAONLYSITES] + na - nf
793 } else {
794 // PAGE-BACKED. Only now is the loop question worth asking: a page
795 // allocation in a one-shot helper costs one page until exit, the same
796 // code in a while body is the documented SEGFAULT.
797 var in_loop: i64 = 0
798 if mb_loop_allocs(buf, ls, fe) > 0 { in_loop = 1 }
799 if in_loop == 1 { st[MB_S_HOT_LOOP] = st[MB_S_HOT_LOOP] + 1 }
800 if srv_hot == 1 { st[MB_S_HOT_SRV] = st[MB_S_HOT_SRV] + 1 }
801 if is_server == 1 { if forks == 1 { st[MB_S_SRV_FORK] = st[MB_S_SRV_FORK] + 1 } }
802 var hot: i64 = 0
803 if in_loop == 1 { hot = 1 }
804 if srv_hot == 1 { hot = 1 }
805 if hot == 1 {
806 st[MB_S_HOT] = st[MB_S_HOT] + 1
807 // ★★★★★A COUNT WITHOUT A WORKLIST IS NOT ACTIONABLE. `deep` could rank
808 // this class and could not name it, and `scan` names everything and
809 // truncates. mode 3 prints ONLY these, so the stream is bounded by
810 // the class itself rather than by the corpus.
811 if mode == 3 {
812 // ★NAME THE REASON ON THE ROW. The two reasons need different
813 // judgement -- a loop hit is fixed by hoisting or freeing inside
814 // the function, a non-forking-server hit is a per-request leak in
815 // a long-lived parent and is a production severity. A worklist
816 // that prints only HOT makes every reader re-derive which.
817 if in_loop == 1 {
818 if srv_hot == 1 { gv_puts("HOT[loop+srv] " as *u8) } else { gv_puts("HOT[loop] " as *u8) }
819 } else { gv_puts("HOT[srv] " as *u8) }
820 gv_puts(path)
821 gv_puts(" " as *u8)
822 mb_put_range(buf, ls, le)
823 gv_puts("\n" as *u8)
824 }
825 }
826 }
827 }
828 var do_cls: i64 = 0
829 if mode == 2 { if icls == 0 { do_cls = 1 } }
830 if do_cls == 1 {
831 let nshim: i64 = mb_count_range(buf, ls, fe, "ccz_cat_num(" as *u8)
832 var ndig: i64 = mb_count_range(buf, ls, fe, "%10" as *u8)
833 if ndig == 0 { ndig = mb_count_range(buf, ls, fe, "% 10" as *u8) }
834 if nshim > 0 {
835 st[MB_S_C_SHIM] = st[MB_S_C_SHIM] + 1
836 st[MB_S_C_SHIMS] = st[MB_S_C_SHIMS] + na - nf
837 } else {
838 if ndig > 0 {
839 st[MB_S_C_INLINE] = st[MB_S_C_INLINE] + 1
840 st[MB_S_C_INLINES] = st[MB_S_C_INLINES] + na - nf
841 } else {
842 st[MB_S_C_OTHER] = st[MB_S_C_OTHER] + 1
843 st[MB_S_C_OTHERS] = st[MB_S_C_OTHERS] + na - nf
844 }
845 }
846 }
847 if mode == 0 {
848 gv_puts(" UNBALANCED " as *u8)
849 gv_puts(path)
850 gv_puts("\n " as *u8)
851 mb_put_range(buf, ls, le)
852 gv_puts("\n mmap=" as *u8)
853 gv_num(na)
854 gv_puts(" munmap=" as *u8)
855 gv_num(nf)
856 gv_puts(" leaked_sites=" as *u8)
857 gv_num(na - nf)
858 gv_puts("\n" as *u8)
859 }
860 }
861 }
862 }
863 ls = fe
864 } else {
865 ls = le + 1
866 }
867 }
868 return 0
869}
870
871// ITERATIVE BFS over root AND EVERY SUBDIRECTORY (2026-08-06).
872// THE OLD WALK WAS ONE LEVEL DEEP. It tested `if ty != MB_DIR_TYPE` and then simply DROPPED every
873// directory entry -- not scanned, and not counted as skipped either -- so an entire subtree could be
874// absent from a run that still printed coverage_complete=1. MEASURED: buildroot/runtime/_hdl_build
875// holds 7,918 files and hundreds of unbalanced functions, none of which have ever appeared in the
876// corpus total this organ has reported since 2026-07-31. A scanner that cannot see a directory must
877// not be able to claim completeness over it.
878//
879// A QUEUE, NOT RECURSION, on purpose: this organ's job is honest coverage, and a recursion depth
880// limit that trips is far harder to detect and report than a queue slot that refuses. Overflow is
881// counted and drops coverage_complete.
882func mb_walk(root: *u8, st: *i64, mode: i64) -> i64 {
883 let q: *u8 = sys_mmap(MB_QCAP * MB_PATHCAP)
884 let dbuf: *u8 = sys_mmap(MB_DIRBUF)
885 let fbuf: *u8 = sys_mmap(MB_FILECAP)
886 let path: *u8 = sys_mmap(MB_PATHCAP)
887 var head: i64 = 0
888 var tail: i64 = 0
889 var si: i64 = 0
890 while root[si] != (0 as u8) { q[si] = root[si]; si = si + 1 }
891 q[si] = 0 as u8
892 tail = 1
893 while head < tail {
894 let dcur: *u8 = ((q as i64) + head * MB_PATHCAP) as *u8
895 head = head + 1
896 let fd: i64 = sys_openat_rd(dcur)
897 if fd < 0 { st[MB_S_SKIP] = st[MB_S_SKIP] + 1 } else {
898 st[MB_S_DIRS] = st[MB_S_DIRS] + 1
899 var go: i64 = 1
900 while go == 1 {
901 let nr: i64 = sys_getdents64(fd, dbuf, MB_DIRBUF)
902 if nr <= 0 { go = 0 } else {
903 var off: i64 = 0
904 while off < nr {
905 let rec: *u8 = ((dbuf as i64) + off) as *u8
906 let ty: i64 = dirent_type(rec)
907 let nm: *u8 = dirent_name(rec)
908 let nmn: i64 = mb_len(nm)
909 if ty == MB_DIR_TYPE {
910 if mb_streq(nm, MB_DOT_ENT) == 0 {
911 if mb_streq(nm, MB_DOTDOT_ENT) == 0 {
912 if tail >= MB_QCAP { st[MB_S_QOVF] = st[MB_S_QOVF] + 1 } else {
913 let slot: *u8 = ((q as i64) + tail * MB_PATHCAP) as *u8
914 mb_join(dcur, nm, slot)
915 tail = tail + 1
916 }
917 }
918 }
919 } else {
920 if mb_is_nx(nm, nmn) == 1 {
921 mb_join(dcur, nm, path)
922 let bn: i64 = mb_slurp(path, fbuf, MB_FILECAP)
923 if bn <= 0 {
924 st[MB_S_SKIP] = st[MB_S_SKIP] + 1
925 } else {
926 st[MB_S_FILES] = st[MB_S_FILES] + 1
927 // mb_slurp STOPS AT THE CAP, it does not fail. A full buffer means
928 // the tail of the file was never read and every function past it is
929 // invisible -- silently, while the old summary still said complete.
930 // Counting it and dropping coverage is the L011 honesty this organ
931 // claims in its own header.
932 if bn >= MB_FILECAP { st[MB_S_TRUNC] = st[MB_S_TRUNC] + 1 }
933 // Rebuild the const table for THIS file before scanning it, so a
934 // sys_mmap(SOME_CONST) can be weighed instead of shrugged at.
935 mbc_scan(fbuf, bn)
936 mb_scan_buf(path, fbuf, bn, st, mode)
937 }
938 }
939 }
940 off = off + dirent_reclen(rec)
941 }
942 }
943 }
944 sys_close(fd)
945 }
946 }
947 sys_munmap(q, MB_QCAP * MB_PATHCAP)
948 sys_munmap(dbuf, MB_DIRBUF)
949 sys_munmap(fbuf, MB_FILECAP)
950 sys_munmap(path, MB_PATHCAP)
951 return 0
952}
953
954func main(argc: i64, argv: *i64) -> i64 {
955 var dir: *u8 = "buildroot/runtime" as *u8
956 if argc >= 3 { dir = argv[2] as *u8 }
957
958 // SINGLE-FILE MODE: `nx_mmapbal file <path>`. Added 2026-07-31 because a TOTAL-COUNT delta over a
959 // SHARED tree is NOT attributable to one lane. Measured: between two scans of buildroot/runtime,
960 // sibling seats added 7 files / 84 funcs / 13 unbalanced funcs, masking this lane's -3 fix under a
961 // +16 drift -- and directory (getdents) order is not stable across runs either, so absence at a byte
962 // offset proves nothing. Per-file scanning is the ONLY sound way to verify one fix on a live corpus.
963 if argc >= 3 {
964 if mb_streq(argv[1] as *u8, "file" as *u8) == 1 {
965 let one: *u8 = argv[2] as *u8
966 let fb1: *u8 = sys_mmap(MB_FILECAP)
967 let st1: *i64 = sys_mmap(MB_ST_SLOTS * MB_WORD) as *i64
968 var z1: i64 = 0
969 while z1 < MB_ST_SLOTS { st1[z1] = 0; z1 = z1 + 1 }
970 gv_puts("=== NX-MMAPBAL -- single file " as *u8)
971 gv_puts(one)
972 gv_puts(" ===\n" as *u8)
973 let n1: i64 = mb_slurp(one, fb1, MB_FILECAP)
974 if n1 <= 0 {
975 gv_puts("verdict=RED rule=file-unreadable\n" as *u8)
976 sys_exit(1)
977 return 1
978 }
979 st1[MB_S_FILES] = 1
980 // Build the const table for THIS file first, exactly as mb_walk does before every scan.
981 // ★A PRECONDITION BELONGS AT EVERY ENTRY POINT, NOT AT THE ONE YOU HAPPENED TO TEST.
982 mbc_scan(fb1, n1)
983 mb_scan_buf(one, fb1, n1, st1, 0)
984 gv_puts("\nfiles_scanned=1 funcs_scanned=" as *u8)
985 gv_num(st1[MB_S_FUNCS])
986 gv_puts("\nunbalanced_funcs=" as *u8)
987 gv_num(st1[MB_S_BAD])
988 gv_puts(" leaked_sites=" as *u8)
989 gv_num(st1[MB_S_SITES])
990 gv_puts(" main_scope_informational=" as *u8)
991 gv_num(st1[MB_S_MAIN])
992 gv_puts("\n of the unbalanced: arena_only=" as *u8)
993 gv_num(st1[MB_S_ARENAONLY])
994 gv_puts(" (every unmatched request <= " as *u8)
995 gv_num(MB_ARENA_MAX)
996 gv_puts(" B, where sys_munmap is a documented NO-OP) page_backed_or_unknown=" as *u8)
997 gv_num(st1[MB_S_BAD] - st1[MB_S_ARENAONLY])
998 gv_puts(" of which REPEATEDLY EXECUTED=" as *u8)
999 gv_num(st1[MB_S_HOT])
1000 gv_puts(" (in_a_loop=" as *u8)
1001 gv_num(st1[MB_S_HOT_LOOP])
1002 gv_puts(" in_a_NON_FORKING_server=" as *u8)
1003 gv_num(st1[MB_S_HOT_SRV])
1004 gv_puts(" | excluded_forking_server=" as *u8)
1005 gv_num(st1[MB_S_SRV_FORK])
1006 gv_puts(") <- the fixable class, ranked" as *u8)
1007 gv_puts("\ncoverage_complete=1\n\n" as *u8)
1008 let c1: *i64 = gv_ctr()
1009 gv_check("F1 no unbalanced non-owning function in this file", st1[MB_S_BAD] == 0, c1)
1010 let r1: i64 = gv_verdict("MMAPBAL-FILE", c1, "every mmap matched by a munmap or handed to the caller")
1011 sys_exit(r1)
1012 return r1
1013 }
1014 }
1015
1016 // DEEP VERB (2026-08-06): `nx_mmapbal deep [dir]` -- RECURSIVE, SUMMARY-ONLY, CLASSIFIED.
1017 // Summary-only is not a convenience. The per-finding stream is ~6.7k findings and the summary
1018 // block sits BELOW it, so every transport this organ is called through (MCP capture cap 163840B,
1019 // nx_fs 1MiB line scan) truncates before reaching it -- the organ whose entire job is to produce a
1020 // number could not deliver that number through any channel it is actually invoked by. Quiet mode
1021 // puts the number in front of the flood instead of behind it.
1022 // HOT VERB (2026-08-15): ENUMERATE the page-backed-in-a-loop class -- the ranked worklist `deep`
1023 // can count but not name. Bounded by the class (hundreds), not by the corpus (tens of thousands),
1024 // which is why this can stream where `scan` cannot. The summary still prints last, so a caller who
1025 // gets a truncated read can tell: no trailing HOT-TOTAL line means the list is PARTIAL.
1026 if argc >= 2 {
1027 if mb_streq(argv[1] as *u8, "hot" as *u8) == 1 {
1028 let sth: *i64 = sys_mmap(MB_ST_SLOTS * MB_WORD) as *i64
1029 var zh: i64 = 0
1030 while zh < MB_ST_SLOTS { sth[zh] = 0; zh = zh + 1 }
1031 gv_puts("=== NX-MMAPBAL hot -- page-backed AND allocating inside a loop -- over " as *u8)
1032 gv_puts(dir)
1033 gv_puts(" ===\n" as *u8)
1034 mb_walk(dir, sth, 3)
1035 gv_puts("HOT-TOTAL " as *u8)
1036 gv_num(sth[MB_S_HOT])
1037 gv_puts(" of page_backed " as *u8)
1038 gv_num(sth[MB_S_BAD] - sth[MB_S_ARENAONLY])
1039 gv_puts(" of convictable " as *u8)
1040 gv_num(sth[MB_S_BAD])
1041 gv_puts(" files=" as *u8)
1042 gv_num(sth[MB_S_FILES])
1043 gv_puts(" coverage_complete=" as *u8)
1044 var covh: i64 = 1
1045 if sth[MB_S_SKIP] != 0 { covh = 0 }
1046 if sth[MB_S_TRUNC] != 0 { covh = 0 }
1047 if sth[MB_S_QOVF] != 0 { covh = 0 }
1048 gv_num(covh)
1049 gv_puts("\n" as *u8)
1050 return 0
1051 }
1052 if mb_streq(argv[1] as *u8, "deep" as *u8) == 1 {
1053 let std: *i64 = sys_mmap(MB_ST_SLOTS * MB_WORD) as *i64
1054 var zd: i64 = 0
1055 while zd < MB_ST_SLOTS { std[zd] = 0; zd = zd + 1 }
1056 gv_puts("=== NX-MMAPBAL deep -- recursive, truncation-honest, classified -- over " as *u8)
1057 gv_puts(dir)
1058 gv_puts(" ===\n" as *u8)
1059 mb_walk(dir, std, 1)
1060 gv_puts("dirs_walked=" as *u8)
1061 gv_num(std[MB_S_DIRS])
1062 gv_puts(" files_scanned=" as *u8)
1063 gv_num(std[MB_S_FILES])
1064 gv_puts(" files_skipped=" as *u8)
1065 gv_num(std[MB_S_SKIP])
1066 gv_puts(" files_truncated=" as *u8)
1067 gv_num(std[MB_S_TRUNC])
1068 gv_puts(" queue_overflows=" as *u8)
1069 gv_num(std[MB_S_QOVF])
1070 gv_puts("\nfuncs_scanned=" as *u8)
1071 gv_num(std[MB_S_FUNCS])
1072 gv_puts("\nCONVICTABLE unbalanced_funcs=" as *u8)
1073 gv_num(std[MB_S_BAD])
1074 gv_puts(" leaked_sites=" as *u8)
1075 gv_num(std[MB_S_SITES])
1076 gv_puts("\n ARENA-ONLY unbalanced_funcs=" as *u8)
1077 gv_num(std[MB_S_ARENAONLY])
1078 gv_puts(" leaked_sites=" as *u8)
1079 gv_num(std[MB_S_ARENAONLYSITES])
1080 gv_puts("\n (every unmatched request <= " as *u8)
1081 gv_num(MB_ARENA_MAX)
1082 gv_puts(" B, so it came from the bump arena and sys_munmap\n" as *u8)
1083 gv_puts(" RETURNS 0 WITHOUT FREEING -- adding the call is provably a no-op, not a fix.\n" as *u8)
1084 gv_puts(" These are a subset of CONVICTABLE above, not a separate population.)\n" as *u8)
1085 gv_puts(" PAGE-BACKED unbalanced_funcs=" as *u8)
1086 gv_num(std[MB_S_BAD] - std[MB_S_ARENAONLY])
1087 gv_puts(" of which REPEATEDLY EXECUTED=" as *u8)
1088 gv_num(std[MB_S_HOT])
1089 gv_puts(" <- RANK THE WORKLIST BY THIS\n" as *u8)
1090 gv_puts(" reasons (they OVERLAP, so they are not summed): in_a_loop=" as *u8)
1091 gv_num(std[MB_S_HOT_LOOP])
1092 gv_puts(" in_a_NON_FORKING_server=" as *u8)
1093 gv_num(std[MB_S_HOT_SRV])
1094 gv_puts(" <- A FLOOR, NOT A VALUE\n" as *u8)
1095 gv_puts(" (the accept loop and its handlers are in DIFFERENT files in the estate's larger\n" as *u8)
1096 gv_puts(" daemons -- MEASURED: nx_mgmt_api.nx holds 42 page-backed handlers and scores\n" as *u8)
1097 gv_puts(" ZERO here because sys_accept lives in another file. A FILE-LEVEL AXIS CANNOT\n" as *u8)
1098 gv_puts(" SEE A SPLIT HANDLER/LOOP, so this undercounts by an unknown amount and only\n" as *u8)
1099 gv_puts(" call-graph reachability can close it.)" as *u8)
1100 gv_puts("\n EXCLUDED AS SAFE: in_a_FORKING_server=" as *u8)
1101 gv_num(std[MB_S_SRV_FORK])
1102 gv_puts(" -- the handler runs in a child that exits,\n" as *u8)
1103 gv_puts(" so the kernel reclaims it. Counting these was BACKWARDS: for THIS question a\n" as *u8)
1104 gv_puts(" forking server is the SAFEST shape. ⚠Fork evidence is FILE-level, so a server that\n" as *u8)
1105 gv_puts(" forks only at BOOT and serves in the parent is wrongly excluded -- a REASSURING\n" as *u8)
1106 gv_puts(" -direction miss, named because closing it needs call-graph reachability.\n" as *u8)
1107 gv_puts(" (a page allocation in a one-shot helper costs one page until the process\n" as *u8)
1108 gv_puts(" exits; the same code executed forever is the documented SEGFAULT. Two ways to\n" as *u8)
1109 gv_puts(" be executed forever -- a while body, or a process that listens and accepts --\n" as *u8)
1110 gv_puts(" and a lexical scan sees only the first. Per-FUNCTION, so it proves the function\n" as *u8)
1111 gv_puts(" is unbalanced AND repeatedly executed, not that the unmatched site is the one.)\n" as *u8)
1112 gv_puts(" THE FIXABLE CLASS: a full page and a kernel VMA per call,\n" as *u8)
1113 gv_puts(" which is the RLIMIT_AS/vm.max_map_count exhaustion this scanner exists for.\n" as *u8)
1114 gv_puts(" (includes sites whose size this organ could not resolve -- unknown is\n" as *u8)
1115 gv_puts(" never counted as arena, because that is the reassuring direction.)\n" as *u8)
1116 gv_puts("INIT-UNPROVEN unbalanced_funcs=" as *u8)
1117 gv_num(std[MB_S_INIT])
1118 gv_puts(" leaked_sites=" as *u8)
1119 gv_num(std[MB_S_INITSITES])
1120 gv_puts("\n (*_init/*_ctx_setup/*_new: ownership via an out-param struct field is\n" as *u8)
1121 gv_puts(" invisible to mb_owns, so these are UNPROVEN, not exonerated -- read\n" as *u8)
1122 gv_puts(" the callee before convicting or clearing any of them.)\n" as *u8)
1123 gv_puts("main_scope_informational=" as *u8)
1124 gv_num(std[MB_S_MAIN])
1125 gv_puts("\ncoverage_complete=" as *u8)
1126 var cov: i64 = 1
1127 if std[MB_S_SKIP] != 0 { cov = 0 }
1128 if std[MB_S_TRUNC] != 0 { cov = 0 }
1129 if std[MB_S_QOVF] != 0 { cov = 0 }
1130 if cov == 1 { gv_puts("1" as *u8) } else { gv_puts("0 -- every count above is a FLOOR; see files_skipped/files_truncated/queue_overflows" as *u8) }
1131 gv_puts("\n\n" as *u8)
1132 let ctd: *i64 = gv_ctr()
1133 gv_check("D1 corpus readable (at least one .nx scanned)", std[MB_S_FILES] > 0, ctd)
1134
1135 // D2 IS NOW A RATCHET ON THE FIXABLE CLASS. See MB_RATCHET_PATH for why the old
1136 // `== 0` tooth could never pass and what that cost.
1137 let pb: i64 = std[MB_S_BAD] - std[MB_S_ARENAONLY]
1138 let floor: i64 = mb_read_floor()
1139 if floor < 0 {
1140 gv_puts(" ratchet floor ABSENT at " as *u8); gv_puts(MB_RATCHET_PATH)
1141 gv_puts(" -- seeding it at " as *u8); gv_num(pb)
1142 gv_puts("\n This run therefore has NO baseline to judge against and does not claim one.\n" as *u8)
1143 mb_write_floor(pb)
1144 gv_need("D2 page-backed leaks are at or below the recorded floor", 0, ctd)
1145 } else {
1146 gv_puts(" ratchet floor=" as *u8); gv_num(floor)
1147 gv_puts(" page_backed=" as *u8); gv_num(pb)
1148 if pb < floor {
1149 gv_puts(" TIGHTENING -> " as *u8); gv_num(pb)
1150 gv_puts("\n" as *u8)
1151 mb_write_floor(pb)
1152 } else {
1153 if pb > floor {
1154 gv_puts(" REGRESSION +" as *u8); gv_num(pb - floor)
1155 gv_puts(" page-backed leak(s) appeared since the floor was set.\n" as *u8)
1156 gv_puts(" ⚠This is a COUNT on a shared tree: it proves a leak appeared, NOT whose.\n" as *u8)
1157 } else { gv_puts(" HOLDING at the floor\n" as *u8) }
1158 }
1159 gv_check("D2 page-backed leaks are at or below the recorded floor", pb <= floor, ctd)
1160 }
1161 gv_check("D3 coverage complete (nothing skipped, truncated or unwalked)", cov == 1, ctd)
1162 let rcd: i64 = gv_verdict("MMAPBAL-DEEP", ctd, "recursive, truncation-honest, initializer class separated from the headline")
1163 sys_exit(rcd)
1164 return rcd
1165 }
1166 }
1167
1168 // CLASSES VERB (2026-08-06): `nx_mmapbal classes [dir]` -- recursive, summary-only, bucketed by
1169 // SHAPE. A five-figure site total answers "how bad" and says nothing about "what to build". This
1170 // ranks the remediation: SHIM is a literal one-line drop-in onto nx_itoa_lib, INLINE is the same
1171 // output reached the long way and needs a per-function byte-equality proof before it can be
1172 // rewritten, OTHER is a buffer with a genuine lifetime and is not codemod work at all.
1173 if argc >= 2 {
1174 if mb_streq(argv[1] as *u8, "classes" as *u8) == 1 {
1175 let stc: *i64 = sys_mmap(MB_ST_SLOTS * MB_WORD) as *i64
1176 var zc: i64 = 0
1177 while zc < MB_ST_SLOTS { stc[zc] = 0; zc = zc + 1 }
1178 gv_puts("=== NX-MMAPBAL classes -- convictable sites bucketed by remediation shape -- over " as *u8)
1179 gv_puts(dir)
1180 gv_puts(" ===\n" as *u8)
1181 mb_walk(dir, stc, 2)
1182 gv_puts("dirs_walked=" as *u8)
1183 gv_num(stc[MB_S_DIRS])
1184 gv_puts(" files_scanned=" as *u8)
1185 gv_num(stc[MB_S_FILES])
1186 gv_puts(" files_skipped=" as *u8)
1187 gv_num(stc[MB_S_SKIP])
1188 gv_puts(" files_truncated=" as *u8)
1189 gv_num(stc[MB_S_TRUNC])
1190 gv_puts("\nCONVICTABLE funcs=" as *u8)
1191 gv_num(stc[MB_S_BAD])
1192 gv_puts(" sites=" as *u8)
1193 gv_num(stc[MB_S_SITES])
1194 gv_puts("\n SHIM (calls ccz_cat_num, hand-rolls the freeing shim -- nxi_fd DROP-IN) funcs=" as *u8)
1195 gv_num(stc[MB_S_C_SHIM])
1196 gv_puts(" sites=" as *u8)
1197 gv_num(stc[MB_S_C_SHIMS])
1198 gv_puts("\n INLINE (open-coded %10 digit loop -- same output, PROVE byte-equal per func) funcs=" as *u8)
1199 gv_num(stc[MB_S_C_INLINE])
1200 gv_puts(" sites=" as *u8)
1201 gv_num(stc[MB_S_C_INLINES])
1202 gv_puts("\n OTHER (buffer with a real lifetime -- NOT codemod work) funcs=" as *u8)
1203 gv_num(stc[MB_S_C_OTHER])
1204 gv_puts(" sites=" as *u8)
1205 gv_num(stc[MB_S_C_OTHERS])
1206 gv_puts("\nWEIGHED BY ALLOCATION SIZE (a site is no longer a page -- arena threshold " as *u8)
1207 gv_num(MB_ARENA_MAX)
1208 gv_puts("B)\n DENOMINATOR -- THIS BLOCK COUNTS A DIFFERENT POPULATION FROM THE CLASS BUCKETS ABOVE, and until\
1209 2026-08-07 both were printed under the same bare word sites=. The class buckets count the LEAKED\
1210 EXCESS (na - nf) and sum to CONVICTABLE sites. These weight buckets count EVERY non-comment\
1211 sys_mmap( site inside a convicted function (na), so their total is LARGER and their denominator is\
1212 mmap_sites_scanned below -- NEVER the leaked-site count. The gap is exactly the munmaps those\
1213 functions DO perform, so a nonzero value means PARTIAL-FREE, not total-leak.\
1214 A READER WHO UNIFIES TWO ADJACENT COUNTERS SHARING ONE LABEL HAS BEEN MISLED BY THE REPORT.\
1215 mmap_sites_scanned=" as *u8)
1216 gv_num(stc[MB_S_W_TOT])
1217 gv_puts(" munmaps_inside_convicted=" as *u8)
1218 gv_num(stc[MB_S_W_TOT] - stc[MB_S_SITES])
1219 gv_puts("\
1220 ARENA-SERVED (<=thr: bump-allocated ~48B, no VMA of its own) mmap_sites=" as *u8)
1221 gv_num(stc[MB_S_W_ARENA])
1222 gv_puts("\n REAL-PAGE (>thr: still a full page AND a kernel VMA per call) mmap_sites=" as *u8)
1223 gv_num(stc[MB_S_W_PAGE])
1224 gv_puts("\n UNKNOWN (const or expression -- never assumed cheap) mmap_sites=" as *u8)
1225 gv_num(stc[MB_S_W_UNK])
1226 gv_puts("\nINIT-UNPROVEN funcs=" as *u8)
1227 gv_num(stc[MB_S_INIT])
1228 gv_puts(" sites=" as *u8)
1229 gv_num(stc[MB_S_INITSITES])
1230 gv_puts("\ncoverage_complete=" as *u8)
1231 var cvc: i64 = 1
1232 if stc[MB_S_SKIP] != 0 { cvc = 0 }
1233 if stc[MB_S_TRUNC] != 0 { cvc = 0 }
1234 if stc[MB_S_QOVF] != 0 { cvc = 0 }
1235 if cvc == 1 { gv_puts("1" as *u8) } else { gv_puts("0 -- counts are FLOORS" as *u8) }
1236 gv_puts("\n\n" as *u8)
1237 let ctc: *i64 = gv_ctr()
1238 gv_check("K1 corpus readable", stc[MB_S_FILES] > 0, ctc)
1239 gv_check("K2 buckets sum to the convictable total (a partition is a claim -- check it)", stc[MB_S_C_SHIM] + stc[MB_S_C_INLINE] + stc[MB_S_C_OTHER] == stc[MB_S_BAD], ctc)
1240 gv_check("K4 weight buckets sum to the sites weighed (same partition discipline as K2)", stc[MB_S_W_ARENA] + stc[MB_S_W_PAGE] + stc[MB_S_W_UNK] == stc[MB_S_W_TOT], ctc)
1241 // K5 BINDS THE TWO POPULATIONS THAT K2 AND K4 EACH CHECK ONLY IN ISOLATION. Both walks cover
1242 // the SAME convicted non-init functions and BOTH skip comments (mb_count_range and
1243 // mb_weigh_range carry the identical mb_is_comment guard), so W_TOT = sum(na) while
1244 // SITES = sum(na - nf) with nf >= 0 -- therefore W_TOT >= SITES is STRUCTURAL, not a
1245 // calibrated threshold. It can invert only if the two walks diverge in SUBJECT, which is
1246 // precisely the confusion that let these two counters be printed as if comparable.
1247 // TWO PARTITIONS THAT ARE EACH INTERNALLY CONSISTENT CAN STILL DESCRIBE DIFFERENT
1248 // POPULATIONS -- ONLY A TOOTH ACROSS THEM CAN CATCH THAT, AND NEITHER K2 NOR K4 COULD.
1249 gv_check("K5 all-mmap-sites >= leaked-excess (proves the two walks still share a subject)", stc[MB_S_W_TOT] >= stc[MB_S_SITES], ctc)
1250 gv_check("K3 coverage complete", cvc == 1, ctc)
1251 let rcc: i64 = gv_verdict("MMAPBAL-CLASSES", ctc, "convictable sites bucketed by remediation shape")
1252 sys_exit(rcc)
1253 return rcc
1254 }
1255 }
1256
1257 let fd: i64 = sys_openat_rd(dir)
1258 if fd < 0 {
1259 gv_puts("NX-MMAPBAL\nverdict=RED rule=scandir-absent dir=" as *u8)
1260 gv_puts(dir)
1261 gv_puts("\n" as *u8)
1262 sys_exit(1)
1263 return 1
1264 }
1265
1266 let dbuf: *u8 = sys_mmap(MB_DIRBUF)
1267 let fbuf: *u8 = sys_mmap(MB_FILECAP)
1268 let path: *u8 = sys_mmap(MB_PATHCAP)
1269 let st: *i64 = sys_mmap(MB_ST_SLOTS * MB_WORD) as *i64
1270 var z: i64 = 0
1271 while z < MB_ST_SLOTS { st[z] = 0; z = z + 1 }
1272
1273 gv_puts("=== NX-MMAPBAL -- static mmap/munmap balance over " as *u8)
1274 gv_puts(dir)
1275 gv_puts(" ===\n" as *u8)
1276
1277 var go: i64 = 1
1278 while go == 1 {
1279 let nr: i64 = sys_getdents64(fd, dbuf, MB_DIRBUF)
1280 if nr <= 0 { go = 0 } else {
1281 var off: i64 = 0
1282 while off < nr {
1283 let rec: *u8 = ((dbuf as i64) + off) as *u8
1284 let ty: i64 = dirent_type(rec)
1285 let nm: *u8 = dirent_name(rec)
1286 let nmn: i64 = mb_len(nm)
1287 if ty != MB_DIR_TYPE {
1288 if mb_is_nx(nm, nmn) == 1 {
1289 mb_join(dir, nm, path)
1290 let bn: i64 = mb_slurp(path, fbuf, MB_FILECAP)
1291 if bn <= 0 {
1292 st[MB_S_SKIP] = st[MB_S_SKIP] + 1
1293 } else {
1294 st[MB_S_FILES] = st[MB_S_FILES] + 1
1295 mb_scan_buf(path, fbuf, bn, st, 0)
1296 }
1297 }
1298 }
1299 off = off + dirent_reclen(rec)
1300 }
1301 }
1302 }
1303 sys_close(fd)
1304
1305 gv_puts("\nfiles_scanned=" as *u8)
1306 gv_num(st[MB_S_FILES])
1307 gv_puts(" files_skipped=" as *u8)
1308 gv_num(st[MB_S_SKIP])
1309 gv_puts(" funcs_scanned=" as *u8)
1310 gv_num(st[MB_S_FUNCS])
1311 gv_puts("\nunbalanced_funcs=" as *u8)
1312 gv_num(st[MB_S_BAD])
1313 gv_puts(" leaked_sites=" as *u8)
1314 gv_num(st[MB_S_SITES])
1315 gv_puts(" main_scope_informational=" as *u8)
1316 gv_num(st[MB_S_MAIN])
1317 gv_puts("\ncoverage_complete=" as *u8)
1318 if st[MB_S_SKIP] == 0 { gv_puts("1" as *u8) } else { gv_puts("0" as *u8) }
1319 gv_puts("\n\n" as *u8)
1320
1321 let ctr: *i64 = gv_ctr()
1322 gv_check("C1 corpus readable (at least one .nx scanned)", st[MB_S_FILES] > 0, ctr)
1323 gv_check("C2 no unbalanced non-owning function", st[MB_S_BAD] == 0, ctr)
1324 gv_check("C3 coverage complete (zero unreadable files)", st[MB_S_SKIP] == 0, ctr)
1325 let rc: i64 = gv_verdict("MMAPBAL", ctr, "every mmap matched by a munmap or handed to the caller")
1326 sys_exit(rc)
1327 return rc
1328}