code wiki / _hdl_build / nx_vc_fecrx_fuzz_gate.nx

nx_vc_fecrx_fuzz_gate.nx source

↩ module page · 245 lines · 12966 B

1// nx_vc_fecrx_fuzz_gate.nx -- ADVERSARIAL memory-safety proof for vc_fecrx_add, the FEC reassembly parser 2// on the LIVE family-video path (operator: "s class exceed ... permanent gains require pain"). Every shard 3// header field (block_id/idx/k/m/flen) is ATTACKER-CONTROLLED (a peer's bytes over the content-blind relay). 4// A green build shipping an exploitable parse is worse than none -> this PROVES no hostile shard can write 5// outside the caller regions. Method: allocate st/scratchA/scratchSh/out at the EXACT deployed-client sizes, 6// wrap each in 0xAA CANARY guard bands, throw 60000 hostile inputs + boundary cases + a real accumulate, 7// and assert EVERY guard byte is pristine after EVERY call. NEG-control proves the detector isn't tautological 8// (a deliberate guard poke MUST read as breached). Evidence -> knowledge/status/vc_fecrx_fuzz.log. 9// expect_exit: 0 license_tier: ORIGINAL 10import "nx_syscalls.nx" 11import "nx_video_client_wasm.nx" 12 13const GUARD: i64 = 8192 14const USABLE: i64 = 65536 // the deployed client's per-region size (0x10000 slots) 15const OUTCAP: i64 = 65280 // the client passes B(0xFF00); n*S bound -> S<=6528, flen<=52224 16 17func fp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 18func fn(v: i64) -> i64 { 19 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m} 20 let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} 21 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 22func lcg(s: *i64) -> i64 { s[0]=(s[0]*1103515245+12345) & 0x7fffffff; return s[0] } 23 24// allocate GUARD | USABLE | GUARD; fill both guards with 0xAA; g[0]=lo-guard addr, g[1]=hi-guard addr. 25// returns the USABLE base address. 26func galloc(usable: i64, g: *i64) -> i64 { 27 let total: i64 = GUARD + usable + GUARD 28 let base: i64 = sys_mmap(total) as i64 29 let lo: *u8 = base as *u8 30 var i: i64 = 0 31 while i < GUARD { lo[i] = 0xAA as u8; i = i + 1 } 32 let hi: *u8 = (base + GUARD + usable) as *u8 33 i = 0 34 while i < GUARD { hi[i] = 0xAA as u8; i = i + 1 } 35 g[0] = base 36 g[1] = base + GUARD + usable 37 return base + GUARD 38} 39// 1 iff both guard bands are still all-0xAA. 40func gok(g: *i64) -> i64 { 41 let lo: *u8 = g[0] as *u8 42 let hi: *u8 = g[1] as *u8 43 var i: i64 = 0 44 while i < GUARD { if (lo[i] as i64) != 0xAA { return 0 } if (hi[i] as i64) != 0xAA { return 0 } i = i + 1 } 45 return 1 46} 47 48func main() -> i64 { 49 fp("=== nx_vc_fecrx_fuzz_gate: adversarial memory-safety of the LIVE FEC reassembly parser ===\n" as *u8) 50 let tbl: *i64 = sys_mmap(8 * 900) as *i64 51 vc_fec_init(tbl) 52 53 let g_st: *i64 = sys_mmap(16) as *i64 54 let g_sa: *i64 = sys_mmap(16) as *i64 55 let g_ss: *i64 = sys_mmap(16) as *i64 56 let g_out: *i64 = sys_mmap(16) as *i64 57 let st: *i64 = galloc(USABLE, g_st) as *i64 58 let scratchA: *i64 = galloc(USABLE, g_sa) as *i64 59 let scratchSh: *u8 = galloc(USABLE, g_ss) as *u8 60 let out: *u8 = galloc(USABLE, g_out) as *u8 61 let payload: *u8 = sys_mmap(70000) 62 let seed: *i64 = sys_mmap(16) as *i64 63 seed[0] = 20260702 64 65 var breaches: i64 = 0 66 var wild_rc: i64 = 0 67 var accepts: i64 = 0 68 var rejects: i64 = 0 69 var completes: i64 = 0 70 var iters: i64 = 0 71 vc_fecrx_reset(st) 72 while iters < 60000 { 73 if (lcg(seed) % 13) == 0 { vc_fecrx_reset(st) } 74 var i: i64 = 0 75 while i < 16 { payload[i] = (lcg(seed) & 0xff) as u8; i = i + 1 } 76 if (lcg(seed) % 3) == 0 { payload[6] = 8 as u8; payload[7] = 2 as u8 } // sometimes force k/m valid -> deeper paths 77 var plen: i64 = lcg(seed) % 4300 78 i = 16 79 while i < plen { if i < 70000 { payload[i] = (lcg(seed) & 0xff) as u8 } i = i + 1 } 80 let rc: i64 = vc_fecrx_add(st, tbl, payload, plen, scratchA, scratchSh, out, OUTCAP) 81 if rc == (0 - 1) { rejects = rejects + 1 } else { if rc == 0 { accepts = accepts + 1 } else { completes = completes + 1 } } 82 if rc < (0 - 1) { wild_rc = wild_rc + 1 } 83 if rc > 65536 { wild_rc = wild_rc + 1 } 84 if gok(g_st) == 0 { breaches = breaches + 1 } 85 if gok(g_sa) == 0 { breaches = breaches + 1 } 86 if gok(g_ss) == 0 { breaches = breaches + 1 } 87 if gok(g_out) == 0 { breaches = breaches + 1 } 88 iters = iters + 1 89 } 90 fp(" rand-fuzz: iters="); fn(iters); fp(" rejects="); fn(rejects); fp(" pending="); fn(accepts); fp(" complete="); fn(completes) 91 fp(" wild_rc="); fn(wild_rc); fp(" guard_breaches="); fn(breaches); fp("\n" as *u8) 92 93 // ---- STORE-PATH fuzz: VALID headers with adversarial idx/S pushed to the bounds, so store[idx*S+c] is 94 // actually exercised at every (idx in 0..9) x (S in 1..4096) incl the max corner. idx sometimes >=n 95 // (tests that reject too). This is the write the random fuzz never reached. ---- 96 var s_accepts: i64 = 0 97 var s_iters: i64 = 0 98 seed[0] = 424242 99 vc_fecrx_reset(st) 100 while s_iters < 40000 { 101 if (lcg(seed) % 7) == 0 { vc_fecrx_reset(st) } 102 let SS: i64 = 1 + (lcg(seed) % 6528) // target S in 1..6528 (the new n*S<=65280 ceiling) 103 let flen2: i64 = 8 * SS - (lcg(seed) % 8) // flen s.t. (flen+7)/8 == SS (>0 for SS>=1) 104 let idx2: i64 = lcg(seed) % 12 // 0..11 -> exercises idx>=n reject too 105 var i2: i64 = 0 106 while i2 < 4 { payload[i2] = (lcg(seed) & 0xff) as u8; i2 = i2 + 1 } // random block_id 107 payload[4] = idx2 as u8 108 payload[5] = 0 as u8 109 payload[6] = 8 as u8 110 payload[7] = 2 as u8 111 payload[8] = (flen2 & 0xff) as u8 112 payload[9] = ((flen2 >> 8) & 0xff) as u8 113 payload[10] = ((flen2 >> 16) & 0xff) as u8 114 payload[11] = ((flen2 >> 24) & 0xff) as u8 115 payload[12] = 0 as u8; payload[13] = 0 as u8; payload[14] = 0 as u8; payload[15] = 0 as u8 116 let pl2: i64 = 16 + SS 117 i2 = 16 118 while i2 < pl2 { if i2 < 70000 { payload[i2] = (lcg(seed) & 0xff) as u8 } i2 = i2 + 1 } 119 let rc2: i64 = vc_fecrx_add(st, tbl, payload, pl2, scratchA, scratchSh, out, OUTCAP) 120 if rc2 != (0 - 1) { s_accepts = s_accepts + 1 } 121 if rc2 < (0 - 1) { wild_rc = wild_rc + 1 } 122 if gok(g_st) == 0 { breaches = breaches + 1 } 123 if gok(g_sa) == 0 { breaches = breaches + 1 } 124 if gok(g_ss) == 0 { breaches = breaches + 1 } 125 if gok(g_out) == 0 { breaches = breaches + 1 } 126 s_iters = s_iters + 1 127 } 128 fp(" store-fuzz: iters="); fn(s_iters); fp(" accepted(store-written)="); fn(s_accepts); fp(" guard_breaches="); fn(breaches); fp("\n" as *u8) 129 130 // ---- MAX-FRAME decode at the exact deployed ceiling: flen=32768 -> S=4096 -> k*S==outcap, n*S=40960. 131 // Feeds 8 shards -> the decode writes scratchSh up to 40959 + out up to 32767. If a guard survives here, 132 // the deployed 64KB scratch is safe at the largest possible frame (the corner an undersized caller breaks). ---- 133 let mflen: i64 = 52224 134 let mS: i64 = vc_fecs_shard_size(mflen) // 6528 (n*S=65280==outcap, the exact ceiling) 135 let mframe: *u8 = sys_mmap(mflen + 64) 136 var mf: i64 = 0 137 while mf < mflen { mframe[mf] = ((mf * 97 + 13) & 255) as u8; mf = mf + 1 } 138 let msd: *u8 = sys_mmap(8 * mS + 64) 139 let mss: *u8 = sys_mmap(10 * mS + 64) 140 let mshards: *u8 = sys_mmap(10 * (16 + mS) + 128) 141 let mslot: i64 = vc_fecs_pack(tbl, mframe, mflen, 9001, msd, mss, mshards) 142 vc_fecrx_reset(st) 143 var mdone: i64 = 0 144 var msi: i64 = 0 145 while msi < 8 { 146 let mr: i64 = vc_fecrx_add(st, tbl, ((mshards as i64) + msi * mslot) as *u8, mslot, scratchA, scratchSh, out, OUTCAP) 147 if mr > 0 { mdone = mr } 148 if gok(g_st) == 0 { breaches = breaches + 1 } 149 if gok(g_ss) == 0 { breaches = breaches + 1 } 150 if gok(g_out) == 0 { breaches = breaches + 1 } 151 msi = msi + 1 152 } 153 var mexact: i64 = 0 154 if mdone == mflen { mexact = 1; var mq: i64 = 0; while mq < mflen { if out[mq] != mframe[mq] { mexact = 0; mq = mflen } else { mq = mq + 1 } } } 155 fp(" max-frame(S=4096): reconstructed="); fn(mdone); fp(" byte_exact="); fn(mexact); fp(" guard_breaches="); fn(breaches); fp("\n" as *u8) 156 157 // ---- boundary cases (deterministic) ---- 158 var bad: i64 = 0 159 // helper inline via repeated pattern: set a clean header then tweak 160 // S=4096 boundary (flen 32761..32768 -> S=4096; k*S=32768==outcap, allowed) needs 8 valid shards to reach decode. 161 // here we only check single-shard SAFETY + return code, then the accumulate test proves the decode path. 162 // idx out of range 163 var j: i64 = 0 164 while j < 16 { payload[j] = 0 as u8; j = j + 1 } 165 payload[6] = 8 as u8; payload[7] = 2 as u8 166 payload[8] = 80 as u8 // flen = 80 -> S=10, plen must be 26 167 payload[4] = 255 as u8 // idx = 255 168 vc_fecrx_reset(st) 169 if vc_fecrx_add(st, tbl, payload, 26, scratchA, scratchSh, out, OUTCAP) != (0 - 1) { bad = bad + 1 } 170 if gok(g_st) == 0 { breaches = breaches + 1 } 171 // flen=0 172 payload[4] = 0 as u8; payload[8] = 0 as u8 173 vc_fecrx_reset(st) 174 if vc_fecrx_add(st, tbl, payload, 16, scratchA, scratchSh, out, OUTCAP) != (0 - 1) { bad = bad + 1 } 175 // S too big (flen=56000 -> S=7000 -> n*S=70000 > 65280 -> reject); plen matches 16+7000 176 payload[8] = (56000 & 0xff) as u8; payload[9] = ((56000 >> 8) & 0xff) as u8 // flen=56000 -> S=7000 177 vc_fecrx_reset(st) 178 if vc_fecrx_add(st, tbl, payload, 16 + 7000, scratchA, scratchSh, out, OUTCAP) != (0 - 1) { bad = bad + 1 } 179 if gok(g_ss) == 0 { breaches = breaches + 1 } 180 // plen mismatch (flen=80 -> S=10 -> need plen 26, send 40) 181 payload[8] = 80 as u8; payload[9] = 0 as u8 182 vc_fecrx_reset(st) 183 if vc_fecrx_add(st, tbl, payload, 40, scratchA, scratchSh, out, OUTCAP) != (0 - 1) { bad = bad + 1 } 184 fp(" boundary: unexpected_accepts="); fn(bad); fp("\n" as *u8) 185 186 // ---- happy path: real frame -> 8 of 10 shards -> byte-exact reconstruct (proves decode path safe + correct) ---- 187 let flen: i64 = 11731 188 let frame: *u8 = sys_mmap(flen + 64) 189 var f: i64 = 0 190 while f < flen { frame[f] = ((f * 131 + 55) & 255) as u8; f = f + 1 } 191 let S: i64 = vc_fecs_shard_size(flen) 192 let sd: *u8 = sys_mmap(8 * S + 64) 193 let ss: *u8 = sys_mmap(10 * S + 64) 194 let shards: *u8 = sys_mmap(10 * (16 + S) + 64) 195 let slot: i64 = vc_fecs_pack(tbl, frame, flen, 7001, sd, ss, shards) 196 vc_fecrx_reset(st) 197 var done_flen: i64 = 0 198 var si: i64 = 0 199 while si < 8 { // feed first 8 shards (0..7) -> exactly k, reconstructs 200 let r: i64 = vc_fecrx_add(st, tbl, ((shards as i64) + si * slot) as *u8, slot, scratchA, scratchSh, out, OUTCAP) 201 if r > 0 { done_flen = r } 202 if gok(g_st) == 0 { breaches = breaches + 1 } 203 if gok(g_ss) == 0 { breaches = breaches + 1 } 204 if gok(g_out) == 0 { breaches = breaches + 1 } 205 si = si + 1 206 } 207 var exact: i64 = 0 208 if done_flen == flen { 209 exact = 1 210 var q: i64 = 0 211 while q < flen { if out[q] != frame[q] { exact = 0; q = flen } else { q = q + 1 } } 212 } 213 fp(" happy-path: reconstructed_flen="); fn(done_flen); fp(" byte_exact="); fn(exact); fp("\n" as *u8) 214 215 // ---- NEG-control: the canary detector MUST catch a real corruption (not tautological) ---- 216 let lo: *u8 = g_st[0] as *u8 217 lo[10] = 0 as u8 218 var neg: i64 = 0 219 if gok(g_st) == 0 { neg = 1 } 220 lo[10] = 0xAA as u8 // restore 221 fp(" neg-control: detector_fires_on_poke="); fn(neg); fp("\n" as *u8) 222 223 var green: i64 = 1 224 if breaches != 0 { green = 0 } 225 if wild_rc != 0 { green = 0 } 226 if bad != 0 { green = 0 } 227 if exact != 1 { green = 0 } 228 if mexact != 1 { green = 0 } // max-frame corner reconstructed 229 if s_accepts < 1000 { green = 0 } // the store-path fuzz MUST have actually exercised the write path 230 if neg != 1 { green = 0 } 231 232 let lf: i64 = sys_openat_append("knowledge/status/vc_fecrx_fuzz.log" as *u8, 420) 233 if lf >= 0 { 234 fp("VCFECRXFUZZ iters=60000 breaches="); // also to log 235 let w: i64 = lf 236 sys_write(w, "VCFECRXFUZZ iters=60000 breaches=" as *u8, 33) 237 let nb: *u8=sys_mmap(28); var mm: i64=breaches; var kk: i64=0; if mm==0{nb[0]=48 as u8;kk=1} while mm>0{nb[kk]=(48+(mm%10)) as u8;mm=mm/10;kk=kk+1} var zz: i64=0; let ob: *u8=sys_mmap(28); while zz<kk{ob[zz]=nb[kk-1-zz];zz=zz+1} sys_write(w, ob, kk) 238 if green==1 { sys_write(w, " exact=1 neg=1 verdict=GREEN\n" as *u8, 28) } else { sys_write(w, " verdict=RED\n" as *u8, 12) } 239 sys_close(lf) 240 } 241 fp("VC-FECRX-FUZZ-GATE breaches="); fn(breaches); fp(" wild="); fn(wild_rc); fp(" boundary_bad="); fn(bad) 242 if green==1 { fp(" verdict=GREEN -- no hostile shard escaped the caller regions; deployed config memory-safe\n" as *u8); return 0 } 243 fp(" verdict=RED -- a hostile input breached a guard OR the happy path broke\n" as *u8) 244 return 1 245}