code wiki / (root) / nx_mmapbal.nx

nx_mmapbal.nx

buildroot/runtime/nx_mmapbal.nx

73257 B1328 linesdepth 3pulls 3 transitivereach 0 importersview sourcekind orphan library
docsdependenciesstructsconstsfunctions

about

nx_mmapbal.nx -- STATIC mmap/munmap BALANCE SCANNER over the .nx corpus. WHY THIS EXISTS (measured 2026-07-31, debt 1785515861): the ecosystem's ONLY leak instrument, nx_leak_check, is DYNAMIC and daemon-only -- its `fleet` verb certifies organs that are RUNNING and a LEAK verdict requires LK_MIN_AGE_S=120s of process age. One-shot organs (5365 gates + every CLI organ) live for milliseconds and exit, so they can NEVER appear in a fleet scan and can NEVER reach the age floor. That hole is why nx_gate_verdict.nx -- the D001 base class 150+ organs import -- leaked on EVERY call for ~13 days while every instrument reported healthy. Dynamic and static detection are not redundant here; they cover disjoint halves of the fleet. THE RULE, stated once so it cannot drift: within ONE function, every sys_mmap must be matched by a sys_munmap, UNLESS the signature RETURNS A POINTER -- that is ownership TRANSFER to the caller, not a leak (gv_ctr is the correct exemplar: it returns the counter, which must outlive the call). A function that allocates and returns i64 has no way to hand the memory back, so an unmatched mmap there is a leak BY CONSTRUCTION, provable from the source alone with no runtime sampling. main() IS REPORTED SEPARATELY, NOT CONVICTED: it runs once per process and the kernel reclaims at exit, so an unbalanced mmap in main is a style note, not a defect. Convicting it would bury the real signal (helpers called in loops) under noise -- the ranked-wrong-thing failure. COMMENT LINES ARE SKIPPED so a header that merely MENTIONS sys_mmap cannot fabricate a finding. ⚠ KNOWN LIMITATION -- OWNERSHIP VIA STRUCT FIELD IS INVISIBLE (measured 2026-07-31). mb_owns detects ownership transfer ONLY through a `-> *` RETURN TYPE. A function that hands its allocation to the caller by STORING IT IN AN OUT-PARAM STRUCT FIELD is reported as a leak even though it is correct. PROVEN CASE: nx_opaque_login.olg_ctx_setup_ttl mmaps 5 key buffers and passes 3 to nx_auth_context_init, which does `ctx.opaque_skS_32 = ...` / `ctx.server_ed25519_priv_32 = ...` -- storing the POINTERS, not copying the bytes. Those live for the whole daemon lifetime; freeing them would be a use-after-free on every login and would corrupt the server signing key. CONSEQUENCE: *_init / *_ctx_setup / *_new functions are the FALSE-POSITIVE class of this scanner. A headline count that does not exclude them OVERSTATES the defect. Treat any finding in an initializer as UNPROVEN until the callee is read. Fixing this properly needs callee-aware escape analysis (does the callee store the pointer?), which is a real rung, not a tweak. FAIL-LOUD ON COVERAGE (law L011): prints files_scanned / files_skipped / coverage_complete. A corpus scanner that hides partial coverage presents partial-as-complete, which is the self-ceiling defect. WHY `deep` EXISTS AND `scan` CANNOT SUBSTITUTE FOR IT (measured 2026-08-14): `scan` streams every finding and its summary block sits BELOW that stream, so BOTH transports truncate before the totals are reached -- the MCP capture cap at 163,840 B and the async job capture cap at 1 MiB, both hit on

dependencies 2 imports · 0 importers

nx_syscalls.nx nx_gate_verdict.nx nx_mmapbal.nx

imports: nx_syscalls.nxnx_gate_verdict.nx

imported by: nobody (leaf or entry point)

structs

none

consts

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
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
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
102const MB_QCAP: i64 = 512
103const MB_DOT_ENT: *u8 = "."
104const MB_DOTDOT_ENT: *u8 = ".."
177const MB_QUOTE_C: i64 = 34
178const MB_BSLASH_C: i64 = 92
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
279const MB_S_ARENAONLY: i64 = 21
280const MB_S_ARENAONLYSITES: i64 = 22
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
313const MB_RATCHET_PATH: *u8 = "knowledge/status/mmapbal_pagebacked.conf"
314const MB_RATCHET_CAP: i64 = 4096
322const MBC_MAX: i64 = 1024
323const MBC_NAMELEN: i64 = 48
579const MB_LOOPDEPTH_MAX: i64 = 64
591const MB_BRACE_OPEN: i64 = 123
592const MB_BRACE_CLOSE: i64 = 125

functions

106func mb_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
108func mb_streq(a: *u8, b: *u8) -> i64
120func mb_slurp(path: *u8, buf: *u8, cap: i64) -> i64
134func mb_line_end(buf: *u8, s: i64, e: i64) -> i64
143func mb_starts(buf: *u8, s: i64, e: i64, needle: *u8) -> i64
called by 1: mb_is_comment
154func mb_is_comment(buf: *u8, s: i64, e: i64) -> i64
179func mb_count_range(buf: *u8, s: i64, e: i64, needle: *u8) -> i64
207func mb_owns(buf: *u8, s: i64, e: i64) -> i64
216func mb_put_range(buf: *u8, s: i64, e: i64) -> i64
221func mb_is_nx(nm: *u8, n: i64) -> i64
229func mb_join(dir: *u8, nm: *u8, out: *u8) -> i64
329func mbc_init() -> i64
338func mbc_isident(c: i64) -> i64
348func mbc_scan(buf: *u8, n: i64) -> i64
413func mbc_lookup(buf: *u8, s: i64, e: i64) -> i64
443func mb_weigh_range(buf: *u8, s: i64, e: i64, st: *i64) -> i64
508func mb_read_floor() -> i64
528func mb_write_floor(v: i64) -> i64
581func mb_while_at(buf: *u8, i: i64, e: i64) -> i64
598func mb_loop_allocs(buf: *u8, s: i64, e: i64) -> i64
655func mb_ends_at(buf: *u8, s: i64, e: i64, needle: *u8) -> i64
673func mb_is_initclass(buf: *u8, s: i64, e: i64) -> i64
690func mb_scan_buf(path: *u8, buf: *u8, n: i64, st: *i64, mode: i64) -> i64
882func mb_walk(root: *u8, st: *i64, mode: i64) -> i64
954func main(argc: i64, argv: *i64) -> i64