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

12961 B268 linesdepth 3pulls 3 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

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

nx_syscalls.nx nx_itoa_lib.nx nx_dup_source_check_v3.nx

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

main dp_w sys_write selftest sys_mkdir wfile sys_openat_wr sys_write ↻ sys_close scan sys_openat_rd sys_mmap sys_getdents64 dirent_type dirent_name ends_nx exists join_path sys_openat_rd ↻ sys_close ↻ dp_w ↻ dirent_reclen sys_close ↻ dp_w ↻ dp_wn nxi_out nxi_fd sys_mmap ↻ ccz_cat_num sys_write ↻ sys_munmap sys_mmap ↻ discover sys_mmap ↻ str_copy sys_openat_rd ↻ sys_getdents64 ↻ dirent_type ↻ dirent_name ↻ is_dot

structs

none

consts

23const K_MAGIC_131072: i64 = 131072
24const K_MAGIC_4096: i64 = 4096

functions

26func 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 }
called by 3: scanselftestmain calls 1: sys_write
31func dp_wn(v: i64) -> i64 { nxi_out(v); return 0 }
called by 2: selftestmain calls 1: nxi_out
33func ends_nx(nm: *u8) -> i64
called by 2: scandiscover
40func join_path(buf: *u8, dir: *u8, name: *u8) -> i64
called by 2: existsdiscover
50func exists(dir: *u8, name: *u8, scratch: *u8) -> i64
57func is_dot(nm: *u8) -> i64
called by 1: discover
65func 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
69func scan(dirA: *u8, dirB: *u8, verbose: i64) -> i64
100func 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 }
103func discover(root: *u8, out: *u8, maxd: i64, maxdepth: i64, cap: *i64) -> i64
158func selftest() -> i64
225func main() -> i64