code wiki / _hdl_build / nx_dup_source_check_v3.nx
nx_dup_source_check_v3.nx
buildroot/runtime/_hdl_build/nx_dup_source_check_v3.nx
about
nx_dup_source_check.nx v3 -- ANTI-CLOBBER cross-tree source-dup detector.
🚨v3 EXISTS BECAUSE v2 WAS AN OOM BOMB THAT TOOK THE NAS DOWN (2026-07-20). Root cause, stated plainly:
v2 widened the scan from 2 hardcoded dirs to ALL discovered source trees (78 pairs) -- a good change -- but
REUSED v1's `exists()` helper UNCHANGED, and that helper did `sys_mmap(4096)` PER CALL and never unmapped.
v1 called it ~15k times (ONE pair) = ~60MB = survivable. v2 called it ~250k times = ~1GB LEAKED PER RUN, on a
cron. Result: NAS userspace starved -- kernel still SYN-ACKs but sshd gives no banner, HTTPS returns status=0,
even DSM stops answering.
★THE LESSON, so it is never repeated: CHANGING A CALLER'S FAN-OUT RE-COSTS EVERY CALLEE. A 2->78 pair change
is a 17x amplifier on an O(files) helper. Reviewing only the code you WROTE is not enough -- you must re-cost
the code you INHERITED AND AMPLIFIED. And note what the v2 selftest could NOT do: it was GREEN 4/4 because
fixture dirs hold 2-3 files, so it tested the ALGORITHM at scale and never the RESOURCE at scale. A scale-law
tooth that asserts only correctness-on-big-input is HALF a tooth; the other half is a RESOURCE bound.
v3 FIX: every path buffer is allocated ONCE PER PAIR (or once per walk) and passed down, never per file.
Allocation profile is now O(pairs + dirs), not O(files), and it is DECLARED in the output envelope so the
property is inspectable rather than assumed. Detection semantics and the emitted " DUP basename: " line are
UNCHANGED (rule 19) -- nx_favela_census counts that exact substring out of knowledge/status/dup_source.log.
expect_exit:0 when clean.
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_itoa_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 23 | const K_MAGIC_131072: i64 = 131072 |
| 24 | const K_MAGIC_4096: i64 = 4096 |
functions
| 26 | func dp_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } |
| 31 | func dp_wn(v: i64) -> i64 { nxi_out(v); return 0 } |
| 33 | func ends_nx(nm: *u8) -> i64 |
| 40 | func join_path(buf: *u8, dir: *u8, name: *u8) -> i64 |
| 50 | func exists(dir: *u8, name: *u8, scratch: *u8) -> i64 |
| 57 | func is_dot(nm: *u8) -> i64 called by 1: discover |
| 65 | func str_copy(dst: *u8, src: *u8) -> i64 { var i: i64=0; while src[i]!=(0 as u8) { dst[i]=src[i]; i=i+1 } dst[i]=0 as u8; return i } called by 1: discover |
| 69 | func scan(dirA: *u8, dirB: *u8, verbose: i64) -> i64 called by 2: selftestmain calls 10: sys_openat_rdsys_mmapsys_getdents64dirent_typedirent_nameends_nx+4 |
| 100 | func wfile(path: *u8) -> i64 { let fd: i64=sys_openat_wr(path, 420); if fd>=0 { sys_write(fd, "x" as *u8, 1); sys_close(fd) } return 0 } |
| 103 | func discover(root: *u8, out: *u8, maxd: i64, maxdepth: i64, cap: *i64) -> i64 called by 2: selftestmain calls 11: sys_mmapstr_copysys_openat_rdsys_getdents64dirent_typedirent_name+5 |
| 158 | func selftest() -> i64 |
| 225 | func main() -> i64 |