code wiki / _hdl_build / nx_url_scrub.nx

nx_url_scrub.nx source

↩ module page · 93 lines · 4921 B

1// nx_url_scrub.nx -- ADDITIVE repair for poisoned url:<cid> rows (the 2026-07-03 empty-literal bug stamped 2// every library doc with an unterminated-pool href). Append-only store law: never delete -- SHADOW each bad 3// row with a 0-length value in a fresh segment; the serve layer treats ul==0 as "no url" and falls back to 4// the /doc view. usage: nx_url_scrub <domain> <bad-prefix> (scrubs rows whose CURRENT value starts with 5// bad-prefix; idempotent -- an already-shadowed row has length 0 and never matches). license_tier: ORIGINAL 6import "nx_docportal_search_seg.nx" 7import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 8 9func us_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 10// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 11// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 12// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 13// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 14func us_num(v: i64) -> i64 { nxi_out(v); return 0 } 15func us_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 16 17func main(argc: i64, argv: *i64) -> i64 { 18 if argc < 3 { us_puts("usage: nx_url_scrub <domain> <bad-prefix>\n" as *u8); return 1 } 19 let domain: *u8 = argv[1] as *u8 20 let bad: *u8 = argv[2] as *u8 21 let badn: i64 = us_len(bad) 22 let prefix: *u8 = sys_mmap(512) 23 dss_prefix(domain, prefix) 24 let h: *i64 = ss_open(prefix) 25 if (h as i64) == 0 { us_puts("no shard for domain\n" as *u8); return 2 } 26 // fresh segid for the shadow segment 27 let segs: *i64 = sys_mmap(256 * 8) as *i64 28 let nseg: i64 = ss_manifest_file(prefix, "manifest.txt" as *u8, segs) 29 var segid: i64 = 1 30 var si: i64 = 0 31 // ROOT FIX seq1730 (id 1785450987): segs[] holds POINTERS to seg-<id> name strings 32 // (nx_seg_store.nx:1234 stores segs[cnt] = name as i64), NOT ids -- so segs[si] + 1 produced 33 // MMAP_ADDRESS+1 (~1.4e14), the pointer-shaped poison that PINS a plane forever: every later 34 // epoch id sorts BELOW it in supersede order and its rows are silently shadowed while rc=0. 35 // Parse the DIGITS, as nx_web_shard_compact.nx:55-58 already does on this SAME array. 36 while si < nseg { 37 let sg_nm: *u8 = segs[si] as *u8 38 var sg_v: i64 = 0 39 var sg_ci: i64 = 0 40 while sg_nm[sg_ci] != (0 as u8) { let sg_c: i64 = sg_nm[sg_ci] as i64; if sg_c >= 48 { if sg_c <= 57 { sg_v = sg_v * 10 + (sg_c - 48) } } sg_ci = sg_ci + 1 } 41 if sg_v >= segid { segid = sg_v + 1 } 42 si = si + 1 43 } 44 // walk every segment's key index; url:-keys whose CURRENT value starts with bad-prefix get shadowed 45 let ns: i64 = h[0] 46 let keybuf: *u8 = sys_mmap(600) 47 let vp: *i64 = sys_mmap(16) as *i64 48 let vl: *i64 = sys_mmap(16) as *i64 49 let w: *i64 = ss_begin() 50 var scrubbed: i64 = 0 51 var s: i64 = 0 52 while s < ns { 53 let kb: *u8 = h[1 + 8 * s] as *u8 54 if h[2 + 8 * s] >= 8 { 55 let m9: i64 = ss_r32(kb, 4) 56 var e9: i64 = 0 57 while e9 < m9 { 58 let eo: i64 = 8 + 4 * m9 + ss_r32(kb, 8 + 4 * e9) 59 if (kb[eo] as i64) == 1 { 60 let kl9: i64 = ss_r32(kb, eo + 1) 61 if kl9 >= 4 { if kl9 < 590 { 62 if kb[eo + 5] == (117 as u8) { if kb[eo + 6] == (114 as u8) { if kb[eo + 7] == (108 as u8) { if kb[eo + 8] == (58 as u8) { 63 var c: i64 = 0 64 while c < kl9 { keybuf[c] = kb[eo + 5 + c]; c = c + 1 } 65 keybuf[kl9] = 0 as u8 66 if ss_hget(h, keybuf, vp, vl) == 1 { 67 if vl[0] >= badn { if badn > 0 { 68 var mm: i64 = 1 69 var x: i64 = 0 70 let vb: *u8 = vp[0] as *u8 71 while x < badn { if vb[x] != bad[x] { mm = 0; x = badn } else { x = x + 1 } } 72 if mm == 1 { 73 ss_add(w, 1, keybuf, keybuf, 0) 74 scrubbed = scrubbed + 1 75 } 76 } } 77 } 78 } } } } 79 } } 80 } 81 e9 = e9 + 1 82 } 83 } 84 s = s + 1 85 } 86 if scrubbed > 0 { 87 if ss_commit(prefix, w, segid) != 0 { us_puts("COMMIT-FAIL\n" as *u8); return 3 } 88 } 89 us_puts("url-scrub: domain=" as *u8); us_puts(domain) 90 us_puts(" scrubbed=" as *u8); us_num(scrubbed) 91 us_puts(" (shadowed with empty values; serve falls back to /doc)\n" as *u8) 92 return 0 93}