code wiki / _hdl_build / nx_wasmpage_census.nx

nx_wasmpage_census.nx source

↩ module page · 314 lines · 19780 B

1// nx_wasmpage_census.nx -- EVERY SHIPPED PAGE THAT CARRIES A WASM MODULE, VERIFIED. NO SAMPLING. 2// 3// WHY IT EXISTS. On 2026-08-14 /craft shipped black twice because its framebuffer had outgrown the 4// module's own declared linear memory, and nothing in the pipeline could see it: size floors and 5// magic bytes cannot tell a module that RUNS from one that TRAPS. The fit guard now lives in the 6// shared emitter, so no NEW page can ship with that defect -- but every page emitted BEFORE the 7// guard existed is still out there unmeasured, and I could not enumerate them: a grep across the 8// docroot's 79,966 files times out. 9// ★"TOO SLOW TO MEASURE" IS A TOOLING GAP, NEVER A LICENCE TO SAMPLE. This is the tool. 10// 11// WHAT IT DOES: walks the docroot, and for every .html that carries an embedded module (the emitter 12// writes it as const B="<base64>") forks nx_wasm_vm_verify on that page and classifies the verdict. 13// ★IT COMPOSES THE RULER RATHER THAN RE-IMPLEMENTING IT. The decode, the parse, the declared-memory 14// read and the fit arithmetic all live in nx_wasm_vm_verify; duplicating any of that here would give 15// the estate two answers to one question and no way to tell which had rotted. 16// 17// The partition is printed and RECONCILED against the file count -- a census whose parts do not sum 18// is a leak, and every OVERSIZE page is NAMED, because a count without a worklist is not actionable. 19// license_tier: ORIGINAL expect_exit: 0 20import "nx_syscalls.nx" 21import "nx_tool_run.nx" 22import "nx_gate_verdict.nx" 23 24const WC_VERIFY: *u8 = "nx_wasm_vm_verify.elf" 25// ★★★★★★ANCHOR ON THE PAYLOAD, NEVER ON THE EMITTER'S SPELLING. This was `const B="` and it made 26// the census measure ONE emitter's output: MEASURED 2026-08-15 it found 9 pages while an independent 27// grep found 67 calling WebAssembly.instantiate -- racing, pong, explorer, adventure, td, city, shmup, 28// voxelworld, sudoku and viz-zoom embed via `var b64='...'`. The census then reported 4/4 GREEN over 29// 13% of its subject, which is exactly the shape of failure it was built to prevent. 30// "AGFzbQEAAAA" is the base64 of a wasm module's own magic + version, so it matches what is CARRIED 31// rather than what wrapped it, and no future emitter can hide a module from this walk by renaming a 32// variable. ★TWO INSTRUMENTS THAT DISAGREE LOCATE A DEFECT NEITHER COULD FIND ALONE. 33const WC_ANCHOR: *u8 = "AGFzbQEAAAA" 34const WC_DIRBUF: i64 = 131072 35const WC_PATHBUF: i64 = 4096 36const WC_OUTCAP: i64 = 262144 37// a page that carries a module is at least this big; below it there is no base64 blob to find and 38// reading the file is wasted work. The smallest emitted game page measured on 2026-08-14 was over 39// 200,000 bytes, so this is two orders of magnitude below the real floor and cannot exclude one. 40const WC_MIN_PAGE: i64 = 4096 41// the child parses and runs a module; craft's is 131,675 bytes and returns in about a second. Ten 42// seconds is the point past which something is wrong rather than slow, and the row says TIMEOUT 43// instead of silently counting as a pass. 44const WC_TIMEOUT_MS: i64 = 10000 45// depth bound for the walk. The docroot is a site tree, not a filesystem -- 12 is far past its real 46// nesting and exists only so a symlink cycle cannot spin forever. Reaching it is REPORTED. 47const WC_MAXDEPTH: i64 = 12 48 49// The call any page must make to run a module, embedded or fetched. Deliberately the full call and 50// not the bare word "WebAssembly": prose on documentation pages says the word constantly, and a 51// detector with false positives is worse than none because everyone learns to ignore it. 52const WC_JSMARK: *u8 = "WebAssembly.instantiate" 53 54// argv handed to the forked verifier: elf, path, noinit, nopaint, NULL terminator. 55const WC_ARGV_SLOTS: i64 = 5 56 57// ★★★★★DELETE THE DUPLICATE, DO NOT BALANCE IT. nx_mmapbal -- the estate's static mmap/munmap 58// scanner, which I should have run BEFORE reasoning about my own frees -- flagged the hand-rolled 59// formatter here as `mmap=2 munmap=0`, a leak per number printed. My first fix made its two scratch 60// buffers into singletons so the scanner would pass, which is treating the symptom. This file already 61// imports nx_gate_verdict, whose gv_puts/gv_num the same scanner measures CLEAN, so the honest fix is 62// that there is ONE formatter and these are names for it. I had hand-rolled the identical pair in 63// nx_wasm_vm_verify and again in nx_wasmfit -- three copies in one session. 64// ★A BUG YOU FIX BY REWRITING THE LINE, RATHER THAN BY EXTRACTING THE FIX, IS A BUG YOU WILL WRITE 65// AGAIN, and the proof is that I wrote it twice more before an existing ruler told me. 66func wc_puts(s: *u8) -> i64 { return gv_puts(s) } 67func wc_num(v: i64) -> i64 { return gv_num(v) } 68func wc_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 69func wc_is_html(nm: *u8) -> i64 { 70 let n: i64 = wc_slen(nm) 71 if n < 6 { return 0 } 72 if nm[n-5] != (46 as u8) { return 0 } 73 if nm[n-4] != (104 as u8) { return 0 } 74 if nm[n-3] != (116 as u8) { return 0 } 75 if nm[n-2] != (109 as u8) { return 0 } 76 if nm[n-1] != (108 as u8) { return 0 } 77 return 1 78} 79func wc_find(buf: *u8, len: i64, needle: *u8) -> i64 { 80 let nl: i64 = wc_slen(needle) 81 if nl == 0 { return 0 - 1 } 82 var i: i64 = 0 83 while i + nl <= len { 84 var k: i64 = 0 85 var m: i64 = 1 86 while k < nl { if buf[i+k] != needle[k] { m = 0; k = nl } else { k = k + 1 } } 87 if m == 1 { return i } 88 i = i + 1 89 } 90 return 0 - 1 91} 92 93// tallies: [0]=html seen [1]=carries a module [2]=painted [3]=REFUSED [4]=fits-paint-not-claimed 94// [5]=no answer [6]=depth-capped dirs [7]=answered by the structural fallback 95// ⚠SIZED FROM THE SLOTS IT HOLDS. 64 bytes is EIGHT i64 slots (0..7); adding the two coverage 96// counters below would have written past the end exactly as the argv array did earlier this session. 97const WC_TAL_SLOTS: i64 = 16 98static WC_TAL: i64 99func wc_tal() -> *i64 { if WC_TAL == 0 { WC_TAL = sys_mmap(WC_TAL_SLOTS * 8) as i64 } return WC_TAL as *i64 } 100 101// ★NEVER ALLOCATE IN A HOT LOOP. wc_check runs once per html file in a 79,966-file tree, so a 102// fixed-size scratch allocation inside it is not "small" -- mmap rounds up to a 4 KB page, making a 103// 16-byte length box cost a page per file, ~180 MB across the docroot for two integers. These live 104// once and are reused, which is also why the buffers are singletons rather than parameters: there is 105// exactly one walk in flight, and threading four buffers through the recursion would buy nothing. 106static WC_LENBOX: i64 107func wc_lenbox() -> *i64 { if WC_LENBOX == 0 { WC_LENBOX = sys_mmap(16) as i64 } return WC_LENBOX as *i64 } 108static WC_ARGVBOX: i64 109func wc_argvbox() -> *i64 { if WC_ARGVBOX == 0 { WC_ARGVBOX = sys_mmap(WC_ARGV_SLOTS * 8) as i64 } return WC_ARGVBOX as *i64 } 110static WC_OUTBOX: i64 111func wc_outbox() -> *u8 { if WC_OUTBOX == 0 { WC_OUTBOX = sys_mmap(WC_OUTCAP) as i64 } return WC_OUTBOX as *u8 } 112static WC_OLENBOX: i64 113func wc_olenbox() -> *i64 { if WC_OLENBOX == 0 { WC_OLENBOX = sys_mmap(16) as i64 } return WC_OLENBOX as *i64 } 114 115func wc_check(path: *u8) -> i64 { 116 let tal: *i64 = wc_tal() 117 let lp: *i64 = wc_lenbox() 118 // ⚠⚠RELEASE THE MAPPING ON EVERY PATH OUT. sys_read_file mmaps the whole file; this walk visits 119 // tens of thousands of pages, so a mapping retained per file is an unbounded leak, and MEASURED 120 // live it was exactly that -- 537 -> 556 -> 570 MB resident across three samples of one run. 121 // ★RESOURCE EXCELLENCE IS A SHIPPING CRITERION: a census that answers correctly while growing 122 // without bound is still a bug. The three early returns below are the whole hazard -- a fix that 123 // only frees on the path the author happened to be thinking about leaks on the common case, which 124 // here is the 44,000 pages that carry no module at all and return at the anchor test. 125 let buf: *u8 = sys_read_file(path, lp) 126 if (buf as i64) == 0 { return 0 } 127 if lp[0] < WC_MIN_PAGE { sys_munmap(buf, lp[0]); return 0 } 128 129 // ★MEASURE THE FILTER'S OWN COVERAGE, DO NOT ASSERT IT. The anchor above finds an EMBEDDED module; 130 // a page can also fetch a .wasm at runtime, and such a page is outside this walk's reach entirely. 131 // Counting the pages that instantiate WebAssembly gives a second, independent census of the same 132 // population, and the two numbers reconcile or they name a gap. Without this the walk reports a 133 // confident total for whatever its anchor happens to match -- which is precisely how the first 134 // version reported 9 of 67. 135 let hasmod: i64 = (wc_find(buf, lp[0], WC_ANCHOR) >= 0) as i64 136 let hasjs: i64 = (wc_find(buf, lp[0], WC_JSMARK) >= 0) as i64 137 if hasjs == 1 { 138 tal[8] = tal[8] + 1 139 if hasmod == 0 { 140 tal[9] = tal[9] + 1 141 // ★★★★★NAME A BUCKET FOR WHAT WAS MEASURED, NEVER FOR THE CONCLUSION YOU IMAGINE FOLLOWS. 142 // This said "instantiates wasm it does not carry", which is a FALSE CLAIM for most of the 143 // class: MEASURED, the bulk are code/src_*.html source-VIEWER pages that merely DISPLAY an 144 // organ's text, and the marker cannot tell a call from a rendering of a call -- the same 145 // law as "a scanner that does not skip comments measures the documentation", wearing a new 146 // costume. Only the two video/ pages genuinely fetch a module at runtime. The honest row 147 // states the observation and leaves the two readings open. 148 wc_puts(" NO-EMBEDDED-MODULE " as *u8); wc_puts(path) 149 wc_puts(" -- names WebAssembly.instantiate but carries no module: it either fetches one\n" as *u8) 150 wc_puts(" at runtime or is a page displaying source. Outside this walk's reach either way.\n" as *u8) 151 } 152 } 153 if hasmod == 0 { sys_munmap(buf, lp[0]); return 0 } 154 sys_munmap(buf, lp[0]) 155 tal[1] = tal[1] + 1 156 157 // ★FORK THE RULER. One implementation of "does this module run in its declared memory", and this 158 // census is only its bookkeeper. 159 // ★ASK THE CHEAP QUESTION AT CENSUS SCALE. "noinit" tells the verifier to skip init(), and that is 160 // not a shortcut -- it is what this census actually claims. World-scale modules generate their 161 // terrain in init (craft: 786,432 voxels of noise), which an interpreter cannot finish; MEASURED 162 // here on the first run, every world page timed out at 10s and the census learned nothing at all. 163 // Without init the quality slot reads zero, which clamps to the FINEST setting, so ww/hh report 164 // the worst case the adaptive controller can ever reach -- precisely the case that has to fit, 165 // and precisely the case that took /craft down. Frame CONTENT stays each module's own gate's job. 166 // ⚠SIZE THE ALLOCATION FROM THE SLOTS IT HOLDS, NOT FROM A BYTE COUNT THAT HAPPENED TO FIT. 167 // sys_mmap takes BYTES; this array holds i64 SLOTS. The first cut asked for 32 -- exactly the four 168 // slots it then used -- and the moment the structural-fallback added a fifth (av[4]=0) it wrote 8 169 // bytes past the end. The arena guard ANNOUNCED it (ARENA-OVERRUN prev_alloc_size=32) instead of 170 // letting it corrupt quietly, which is the whole argument for fail-loud allocation; a silent 171 // overrun here would have surfaced as an unrelated wrong answer somewhere downstream. 172 let av: *i64 = wc_argvbox() 173 av[0] = WC_VERIFY as i64 174 av[1] = path as i64 175 // ★★★★★★ASK ONLY THE QUESTION YOU CAN ANSWER FOR AN ARBITRARY MODULE. A census cannot know any 176 // given module's contract -- whether render() takes camera arguments, whether a world must be 177 // generated first -- so a frame it drives blind proves nothing about the page and, MEASURED on 178 // voxelworld/index.html, actively LIES: fit passed with 1.8 MB of headroom and the blank frame 179 // still landed the page in REFUSED, indicting a healthy page for the one defect this census 180 // exists to find. Structural fit is the question this walk CAN answer for every module, and it is 181 // the question that took /craft down twice. Frame content belongs to each module's own gate, 182 // where the arguments and the init are known. 183 av[2] = ("noinit" as *u8) as i64 184 av[3] = ("nopaint" as *u8) as i64 185 av[4] = 0 186 let out: *u8 = wc_outbox() 187 let olen: *i64 = wc_olenbox() 188 // Asking the structural question only, this cannot hang: no frame is ever rendered, so the 189 // interpreter's per-pixel cost -- which timed out four of four world pages on the first run -- is 190 // out of the path entirely rather than retried around. 191 let rc: i64 = tr_run_capture_to(WC_VERIFY, av, out, WC_OUTCAP, olen, WC_TIMEOUT_MS) 192 193 if rc == 0 { tal[2] = tal[2] + 1 } else { 194 if rc == 3 { tal[4] = tal[4] + 1 } else { 195 if rc < 0 { 196 tal[5] = tal[5] + 1 197 wc_puts(" TIMEOUT-EVEN-STRUCTURAL " as *u8); wc_puts(path); wc_puts("\n" as *u8) 198 } else { 199 tal[3] = tal[3] + 1 200 wc_puts(" REFUSED " as *u8); wc_puts(path) 201 wc_puts(" -- the module does not run in the memory it declares\n" as *u8) 202 } 203 } 204 } 205 return 0 206} 207 208func wc_walk(dir: *u8, depth: i64) -> i64 { 209 let tal: *i64 = wc_tal() 210 if depth > WC_MAXDEPTH { tal[6] = tal[6] + 1; return 0 } 211 let fd: i64 = sys_openat_rd(dir) 212 if fd < 0 { return 0 } 213 let dbuf: *u8 = sys_mmap(WC_DIRBUF) 214 var go: i64 = 1 215 while go == 1 { 216 // ⚠ONE getdents64 CALL IS NOT A DIRECTORY LISTING -- loop until it returns 0, or a big 217 // directory is silently read as a prefix and its total published as fact. 218 let nb: i64 = sys_getdents64(fd, dbuf, WC_DIRBUF) 219 if nb <= 0 { go = 0 } else { 220 var off: i64 = 0 221 while off < nb { 222 let rec: *u8 = ((dbuf as i64) + off) as *u8 223 let rl: i64 = dirent_reclen(rec) 224 if rl <= 0 { off = nb } else { 225 let nm: *u8 = dirent_name(rec) 226 let dt: i64 = dirent_type(rec) 227 var skip: i64 = 0 228 if nm[0] == (46 as u8) { skip = 1 } // . and .. 229 if skip == 0 { 230 let p: *u8 = sys_mmap(WC_PATHBUF) 231 var c: i64 = 0 232 while dir[c] != (0 as u8) { p[c] = dir[c]; c = c + 1 } 233 p[c] = 47 as u8 234 c = c + 1 235 var d: i64 = 0 236 while nm[d] != (0 as u8) { p[c+d] = nm[d]; d = d + 1 } 237 p[c+d] = 0 as u8 238 if dt == 4 { wc_walk(p, depth + 1) } else { 239 if wc_is_html(nm) == 1 { tal[0] = tal[0] + 1; wc_check(p) } 240 } 241 // ⚠FREE THE PATH: this allocation is PER DIRECTORY ENTRY, not per directory, so 242 // across 62,906 files it is ~250 MB of requests. The arena's recycling ring 243 // absorbed most of it, which is precisely why the process READ as flat and why 244 // I described it that way -- ★AN ALLOCATOR THAT HIDES AN IMBALANCE MAKES THE 245 // IMBALANCE A PROPERTY OF THE ALLOCATOR, NOT A PROPERTY OF THE CODE, AND THE 246 // NEXT SIZE CHANGE UNHIDES IT. p is dead once the branch above returns. 247 sys_munmap(p, WC_PATHBUF) 248 } 249 off = off + rl 250 } 251 } 252 } 253 } 254 sys_close(fd) 255 sys_munmap(dbuf, WC_DIRBUF) 256 return 0 257} 258 259func main(argc: i64, argv: *i64) -> i64 { 260 let ctr: *i64 = gv_ctr() 261 var root: *u8 = "sites/nishifamily" as *u8 262 if argc >= 2 { root = argv[1] as *u8 } 263 wc_puts("=== nx_wasmpage_census " as *u8); wc_puts(root); wc_puts(" ===\n" as *u8) 264 265 let tal: *i64 = wc_tal() 266 var z: i64 = 0 267 while z < WC_TAL_SLOTS { tal[z] = 0; z = z + 1 } 268 wc_walk(root, 0) 269 270 wc_puts("\n html_pages=" as *u8); wc_num(tal[0]) 271 wc_puts(" carrying_a_module=" as *u8); wc_num(tal[1]) 272 // ★NAME THE BUCKET FOR WHAT WAS MEASURED. "skipped" said how the reader failed, not what the 273 // subject is: a module whose framebuffer FITS but whose frame this interpreter cannot afford is a 274 // PASS on the only question this census asks. Calling it a skip invites the next reader to treat 275 // the estate's flagship pages as unmeasured when they are the best-measured things here. 276 wc_puts("\n FITS=" as *u8); wc_num(tal[4]) 277 wc_puts(" REFUSED=" as *u8); wc_num(tal[3]) 278 wc_puts(" no_answer=" as *u8); wc_num(tal[5]) 279 // ★AN UNREACHABLE BUCKET STILL GETS A COUNTER, BECAUSE THE DAY IT FIRES IS THE DAY A CONTRACT 280 // CHANGED. The verifier is asked the structural question only, so it can answer 3 (fits) or 1 281 // (does not); a 0 means it painted a frame nobody asked for and this census is reading a tool it 282 // no longer understands. It stays in the partition sum for exactly that reason. 283 if tal[2] > 0 { wc_puts(" ⚠unexpected_painted=" as *u8); wc_num(tal[2]) } 284 let sum: i64 = tal[2] + tal[3] + tal[4] + tal[5] 285 wc_puts("\n partition sum=" as *u8); wc_num(sum) 286 wc_puts(" of carrying_a_module=" as *u8); wc_num(tal[1]) 287 if sum == tal[1] { wc_puts(" RECONCILES\n" as *u8) } else { wc_puts(" LEAKS -- do not trust these numbers\n" as *u8) } 288 289 // ★COVERAGE IS PART OF THE CLAIM. The verdict above is about EMBEDDED modules; this line says how 290 // much of the wasm on this site that is, so nobody reads a GREEN as covering more than it does. 291 wc_puts(" wasm reach: pages instantiating WebAssembly=" as *u8); wc_num(tal[8]) 292 wc_puts(" of which carry their module (verified here)=" as *u8); wc_num(tal[1]) 293 wc_puts(" fetch it at runtime (NOT verified here)=" as *u8); wc_num(tal[9]) 294 let reach: i64 = tal[1] + tal[9] 295 wc_puts("\n reach sum=" as *u8); wc_num(reach) 296 wc_puts(" of instantiating=" as *u8); wc_num(tal[8]) 297 if reach == tal[8] { wc_puts(" RECONCILES\n" as *u8) } else { wc_puts(" LEAKS -- a page instantiates wasm and fell into neither class\n" as *u8) } 298 if tal[6] > 0 { 299 wc_puts(" ⚠depth cap reached in " as *u8); wc_num(tal[6]) 300 wc_puts(" directories -- the walk is a FLOOR, not a total\n" as *u8) 301 } 302 303 gv_check("the walk reconciles: every page carrying a module landed in exactly one bucket" as *u8, (sum == tal[1]) as i64, ctr) 304 gv_check("no shipped page carries a module that refuses to run in the memory it declares" as *u8, (tal[3] == 0) as i64, ctr) 305 // ★A CENSUS THAT CANNOT ANSWER FOR A PAGE HAS NOT MEASURED THE POPULATION, IT HAS MEASURED THE 306 // PAGES THAT WERE EASY. Bound to the denominator on purpose: this cannot pass on an empty walk. 307 gv_check("every page carrying a module produced an answer -- no non-answers left in the population" as *u8, ((tal[5] == 0) && (tal[1] > 0)) as i64, ctr) 308 gv_check("the walk completed without hitting its depth cap, so this is a total and not a floor" as *u8, (tal[6] == 0) as i64, ctr) 309 // ★THE SECOND CENSUS MUST ACCOUNT FOR THE FIRST. Every page that instantiates wasm is either one 310 // this walk verified or one it NAMED as out of reach -- a page in neither class is a page the 311 // anchor silently missed, which is the exact defect that made the first run report 9 of 67. 312 gv_check("every page that instantiates wasm is either verified here or named as fetching it" as *u8, ((reach == tal[8]) && (tal[8] > 0)) as i64, ctr) 313 return gv_verdict("WASMPAGE-CENSUS" as *u8, ctr, "every shipped page carrying an embedded module was run, not sampled" as *u8) 314}