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}