code wiki / _hdl_build / _fetch_bounded_gate.nx

_fetch_bounded_gate.nx source

↩ module page · 268 lines · 13019 B

1// _fetch_bounded_gate.nx -- RES-R7 gate: bounded fetch (timeout + no 2// silent truncation). 3// 4// Proves the nx_fetch_bounded capability with a SOVEREIGN, network-free 5// harness: a sys_pipe2 stands in for any readable fd (socket/TLS/file). 6// Write a body into the pipe's write end, drain it through the new 7// bounded consumer, and assert the contract. 8// 9// REQUIRED CHECKS (each computed from a REAL drain, no fabrication): 10// POS-1 small body fully consumed -> FB_OK, counted==1000 11// POS-2 >512KB body, NO SILENT TRUNCATION -> FB_OVERFLOW, counted==600000 12// (explicit code + FULL counted length, never clamped to cap) 13// TIMEOUT stalled read returns in budget -> FB_TIMEOUT, elapsed<1000ms 14// NEG-CTL OLD br_drain on same >512KB body truncates at cap with NO 15// signal -> proves the test can actually detect truncation 16// TAMPER tiny cap on a 1000-byte body -> FB_OVERFLOW, never FB_OK 17// 18// A GREEN requires BOTH (old path truncates, == cap, silently) AND (new 19// path returns FB_OVERFLOW with the full count) AND (timeout returns 20// within budget). If the new consumer ever silently clamped like the 21// old one, POS-2 / TAMPER fail RED. 22// 23// build/placement: harness resolves `_fetch_bounded_gate` -> 24// runtime/_hdl_build/_fetch_bounded_gate.nx (1st match). 25// expect_exit: 0 26// license_tier: ORIGINAL 27 28import "../nx_fetch_bounded.nx" 29 30// ===== tiny test I/O (st_* idiom, nx_h2_stream.nx:185-230) ========== 31func st_puts(s: *u8) -> i64 { 32 var n: i64 = 0 33 while s[n] != (0 as u8) { n = n + 1 } 34 sys_write(1, s, n) 35 return 0 36} 37func st_putn(v: i64) -> i64 { 38 let bb: *u8 = sys_mmap(28) 39 var m: i64 = v 40 if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } 41 let t: *u8 = sys_mmap(28) 42 var k: i64 = 0 43 if m == 0 { t[0] = 48 as u8; k = 1 } 44 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 45 var i: i64 = 0 46 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 47 sys_write(1, bb, k) 48 return 0 49} 50// fd-aware decimal writer for the durable status line (st_fdn idiom). 51func st_fdn(fd: i64, v: i64) -> i64 { 52 let bb: *u8 = sys_mmap(28) 53 let t: *u8 = sys_mmap(28) 54 var m: i64 = v 55 if m < 0 { m = 0 - m } 56 var k: i64 = 0 57 if m == 0 { t[0] = 48 as u8; k = 1 } 58 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 59 var i: i64 = 0 60 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 61 sys_write(fd, bb, k) 62 return 0 63} 64 65// ===== NEGATIVE CONTROL: the OLD br_drain, re-implemented EXACTLY ===== 66// This is the byte-for-byte logic of nx_browse_text.nx:55-66 -- it 67// stops at `cap`, returns `off`, and emits NO overflow signal. Feeding 68// the same >512KB body through this MUST return exactly `cap` (silent 69// truncation), proving the >512KB feed genuinely overflows the buffer 70// and that POS-2's FB_OVERFLOW is a real catch, not a no-op. 71func gate_old_drain(fd: i64, buf: *u8, cap: i64) -> i64 { 72 var off: i64 = 0 73 var keep: i64 = 1 74 while keep == 1 { 75 if off >= cap { keep = 0 } 76 if keep == 1 { 77 let r: i64 = sys_read(fd, buf + off, cap - off) 78 if r <= 0 { keep = 0 } else { off = off + r } 79 } 80 } 81 return off 82} 83 84// Create a pipe and unpack its TWO 32-bit fds correctly. pipe2(2) 85// writes int[2] (two i32s = 8 bytes); reading them as i64 would pack 86// both fds into one slot and leave the second garbled. Returns 0 on 87// success (rfd[0] <- read end, wfd[0] <- write end), <0 on failure. 88func gate_pipe(rfd: *i64, wfd: *i64) -> i64 { 89 let raw: *i32 = sys_mmap(16) as *i32 90 let rc: i64 = sys_pipe2(raw as *i64, 0) 91 if rc < 0 { return rc } 92 rfd[0] = raw[0] as i64 93 wfd[0] = raw[1] as i64 94 return 0 95} 96 97// Write `n` bytes into pipe write-end `wfd` in FB_READ_SLICE chunks, 98// then close it so the reader sees EOF. Returns total bytes written. 99// (Bytes are a deterministic pattern so a future content check is 100// possible; the gate only needs counts here.) 101func gate_fill_pipe(wfd: i64, n: i64) -> i64 { 102 let chunk: *u8 = sys_mmap(FB_READ_SLICE) 103 var j: i64 = 0 104 while j < FB_READ_SLICE { chunk[j] = (j & 0xff) as u8; j = j + 1 } 105 var sent: i64 = 0 106 while sent < n { 107 var want: i64 = n - sent 108 if want > FB_READ_SLICE { want = FB_READ_SLICE } 109 let w: i64 = sys_write(wfd, chunk, want) 110 if w <= 0 { sent = n } else { sent = sent + w } 111 } 112 return sent 113} 114 115func main() -> i64 { 116 var pass: i64 = 0 117 var tot: i64 = 0 118 st_puts("RES-R7 gate: bounded fetch (timeout + no silent truncation)\n" as *u8) 119 120 let CAP: i64 = 524288 // 512 KiB buffer, matches FU_CAP 121 let BIG: i64 = 600000 // > 512 KiB body (overflows CAP by 75712) 122 let SMALL: i64 = 1000 // small body 123 124 let out: *u8 = sys_mmap(CAP) 125 let cnt: *i64 = sys_mmap(16) as *i64 126 127 // --------------------------------------------------------------- 128 // POS-1: small body fully consumed -> FB_OK, counted==SMALL. 129 // --------------------------------------------------------------- 130 let r1: *i64 = sys_mmap(16) as *i64 131 let w1: *i64 = sys_mmap(16) as *i64 132 if gate_pipe(r1, w1) < 0 { st_puts(" FATAL pipe2 POS-1 failed\n" as *u8); sys_exit(2) } 133 gate_fill_pipe(w1[0], SMALL) 134 sys_close(w1[0]) // EOF for reader 135 cnt[0] = 0 - 1 136 let rc1: i64 = fb_drain_bounded(r1[0], out, CAP, 30000, cnt) 137 sys_close(r1[0]) 138 if rc1 == FB_OK { st_puts(" PASS POS-1 small rc==FB_OK\n" as *u8); pass = pass + 1 } 139 if rc1 != FB_OK { st_puts(" FAIL POS-1 small rc=" as *u8); st_putn(rc1); st_puts(" exp=FB_OK(0)\n" as *u8) } 140 tot = tot + 1 141 if cnt[0] == SMALL { st_puts(" PASS POS-1 counted==" as *u8); st_putn(SMALL); st_puts("\n" as *u8); pass = pass + 1 } 142 if cnt[0] != SMALL { st_puts(" FAIL POS-1 counted=" as *u8); st_putn(cnt[0]); st_puts(" exp=" as *u8); st_putn(SMALL); st_puts("\n" as *u8) } 143 tot = tot + 1 144 145 // --------------------------------------------------------------- 146 // POS-2 / NO SILENT TRUNCATION: >512KB body -> FB_OVERFLOW AND the 147 // FULL counted length (600000), NOT clamped to CAP. Headline check. 148 // NOTE on pipe capacity: a Linux pipe buffers ~64KiB, so a blind 149 // 600KB write before reading would block. We fork a writer child 150 // so the parent drains concurrently -- a faithful streaming proof. 151 // --------------------------------------------------------------- 152 let r2: *i64 = sys_mmap(16) as *i64 153 let w2: *i64 = sys_mmap(16) as *i64 154 if gate_pipe(r2, w2) < 0 { st_puts(" FATAL pipe2 POS-2 failed\n" as *u8); sys_exit(2) } 155 let kid2: i64 = sys_fork() 156 if kid2 == 0 { 157 sys_close(r2[0]) // child: writer only 158 gate_fill_pipe(w2[0], BIG) 159 sys_close(w2[0]) // EOF 160 sys_exit(0) 161 } 162 sys_close(w2[0]) // parent: reader only 163 cnt[0] = 0 - 1 164 let rc2: i64 = fb_drain_bounded(r2[0], out, CAP, 30000, cnt) 165 sys_close(r2[0]) 166 let wst2: *i64 = sys_mmap(16) as *i64 167 sys_wait4(kid2, wst2, 0) 168 if rc2 == FB_OVERFLOW { st_puts(" PASS POS-2 >512KB rc==FB_OVERFLOW\n" as *u8); pass = pass + 1 } 169 if rc2 != FB_OVERFLOW { st_puts(" FAIL POS-2 rc=" as *u8); st_putn(rc2); st_puts(" exp=FB_OVERFLOW(-70)\n" as *u8) } 170 tot = tot + 1 171 if cnt[0] == BIG { st_puts(" PASS POS-2 NO-SILENT-TRUNC counted==" as *u8); st_putn(BIG); st_puts(" (full length, not clamped to " as *u8); st_putn(CAP); st_puts(")\n" as *u8); pass = pass + 1 } 172 if cnt[0] != BIG { st_puts(" FAIL POS-2 counted=" as *u8); st_putn(cnt[0]); st_puts(" exp=" as *u8); st_putn(BIG); st_puts("\n" as *u8) } 173 tot = tot + 1 174 175 // --------------------------------------------------------------- 176 // TIMEOUT: write nothing, do NOT close the write end (read would 177 // block forever) -> FB_TIMEOUT and wall-clock elapsed < 1000ms 178 // (proves it returned within the 200ms budget, did NOT hang). 179 // --------------------------------------------------------------- 180 let r3: *i64 = sys_mmap(16) as *i64 181 let w3: *i64 = sys_mmap(16) as *i64 182 if gate_pipe(r3, w3) < 0 { st_puts(" FATAL pipe2 TIMEOUT failed\n" as *u8); sys_exit(2) } 183 cnt[0] = 0 - 1 184 let t_start: i64 = sys_now_ms() 185 let rc3: i64 = fb_drain_bounded(r3[0], out, CAP, 200, cnt) // 200ms budget, never-closed pipe 186 let t_end: i64 = sys_now_ms() 187 sys_close(r3[0]); sys_close(w3[0]) 188 let elapsed: i64 = t_end - t_start 189 if rc3 == FB_TIMEOUT { st_puts(" PASS TIMEOUT rc==FB_TIMEOUT\n" as *u8); pass = pass + 1 } 190 if rc3 != FB_TIMEOUT { st_puts(" FAIL TIMEOUT rc=" as *u8); st_putn(rc3); st_puts(" exp=FB_TIMEOUT(-71)\n" as *u8) } 191 tot = tot + 1 192 if elapsed < 1000 { st_puts(" PASS TIMEOUT returned in " as *u8); st_putn(elapsed); st_puts("ms (<1000, did NOT hang)\n" as *u8); pass = pass + 1 } 193 if elapsed >= 1000 { st_puts(" FAIL TIMEOUT elapsed=" as *u8); st_putn(elapsed); st_puts("ms exp<1000\n" as *u8) } 194 tot = tot + 1 195 196 // --------------------------------------------------------------- 197 // NEGATIVE CONTROL: same >512KB body through the OLD br_drain -> 198 // returns exactly CAP with NO overflow signal (silent truncation). 199 // This confirms the >512KB feed genuinely overflows the buffer, so 200 // POS-2's catch is real. PASS iff old == CAP (it DID truncate). 201 // --------------------------------------------------------------- 202 let r4: *i64 = sys_mmap(16) as *i64 203 let w4: *i64 = sys_mmap(16) as *i64 204 if gate_pipe(r4, w4) < 0 { st_puts(" FATAL pipe2 NEG-CTL failed\n" as *u8); sys_exit(2) } 205 let kid4: i64 = sys_fork() 206 if kid4 == 0 { 207 sys_close(r4[0]) 208 gate_fill_pipe(w4[0], BIG) 209 sys_close(w4[0]) 210 sys_exit(0) 211 } 212 sys_close(w4[0]) 213 let old_got: i64 = gate_old_drain(r4[0], out, CAP) 214 sys_close(r4[0]) 215 let wst4: *i64 = sys_mmap(16) as *i64 216 sys_wait4(kid4, wst4, 0) 217 if old_got == CAP { st_puts(" PASS NEG-CTL old br_drain truncated at CAP=" as *u8); st_putn(CAP); st_puts(" (silent, lost " as *u8); st_putn(BIG - CAP); st_puts(" bytes) -> test CAN detect truncation\n" as *u8); pass = pass + 1 } 218 if old_got != CAP { st_puts(" FAIL NEG-CTL old_got=" as *u8); st_putn(old_got); st_puts(" exp=" as *u8); st_putn(CAP); st_puts(" (neg-control did not truncate -> test is inert)\n" as *u8) } 219 tot = tot + 1 220 221 // --------------------------------------------------------------- 222 // TAMPER: deliberately tiny cap (10) on the 1000-byte body -> MUST 223 // return FB_OVERFLOW with counted==1000, never FB_OK. Confirms the 224 // cap boundary itself is enforced (not just the 512KB case). 225 // --------------------------------------------------------------- 226 let r5: *i64 = sys_mmap(16) as *i64 227 let w5: *i64 = sys_mmap(16) as *i64 228 if gate_pipe(r5, w5) < 0 { st_puts(" FATAL pipe2 TAMPER failed\n" as *u8); sys_exit(2) } 229 gate_fill_pipe(w5[0], SMALL) 230 sys_close(w5[0]) 231 cnt[0] = 0 - 1 232 let tiny: *u8 = sys_mmap(64) 233 let rc5: i64 = fb_drain_bounded(r5[0], tiny, 10, 30000, cnt) 234 sys_close(r5[0]) 235 if rc5 == FB_OVERFLOW { st_puts(" PASS TAMPER tiny-cap rc==FB_OVERFLOW\n" as *u8); pass = pass + 1 } 236 if rc5 != FB_OVERFLOW { st_puts(" FAIL TAMPER rc=" as *u8); st_putn(rc5); st_puts(" exp=FB_OVERFLOW(-70)\n" as *u8) } 237 tot = tot + 1 238 if cnt[0] == SMALL { st_puts(" PASS TAMPER counted==" as *u8); st_putn(SMALL); st_puts(" (full length even with cap=10)\n" as *u8); pass = pass + 1 } 239 if cnt[0] != SMALL { st_puts(" FAIL TAMPER counted=" as *u8); st_putn(cnt[0]); st_puts(" exp=" as *u8); st_putn(SMALL); st_puts("\n" as *u8) } 240 tot = tot + 1 241 242 // ===== verdict + durable evidence (nx_h2_stream.nx:356-379) ===== 243 st_puts("---- RES-R7 gate: passed " as *u8); st_putn(pass); st_puts(" / " as *u8); st_putn(tot); st_puts("\n" as *u8) 244 if pass == tot { 245 let lfd: i64 = sys_openat_append("knowledge/status/res_fetch_bounded.log" as *u8, 0x1a4) 246 if lfd >= 0 { 247 sys_write(lfd, "RES-R7-GATE organ=nx_fetch_bounded pass=" as *u8, 41) 248 st_fdn(lfd, pass); sys_write(lfd, "/" as *u8, 1); st_fdn(lfd, tot) 249 sys_write(lfd, " overflow_counted=" as *u8, 18); st_fdn(lfd, BIG) 250 sys_write(lfd, " timeout_ms=" as *u8, 12); st_fdn(lfd, elapsed) 251 sys_write(lfd, " negctl=old-trunc@" as *u8, 18); st_fdn(lfd, CAP) 252 sys_write(lfd, " verdict=GREEN\n" as *u8, 15) 253 sys_close(lfd) 254 } 255 st_puts("VERDICT=GREEN\n" as *u8) 256 sys_exit(0) 257 } 258 let rfd: i64 = sys_openat_append("knowledge/status/res_fetch_bounded.log" as *u8, 0x1a4) 259 if rfd >= 0 { 260 sys_write(rfd, "RES-R7-GATE organ=nx_fetch_bounded pass=" as *u8, 41) 261 st_fdn(rfd, pass); sys_write(rfd, "/" as *u8, 1); st_fdn(rfd, tot) 262 sys_write(rfd, " verdict=RED\n" as *u8, 13) 263 sys_close(rfd) 264 } 265 st_puts("VERDICT=RED\n" as *u8) 266 sys_exit(1) 267 return 0 268}