nx_boundscheck_gate.nx source
↩ module page · 255 lines · 11939 B
1// nx_boundscheck_gate.nx -- GATE for LN3 raw-pointer provenance (lang.plan rung LN3, symbol
2// bck_ptr_provenance). The rung's done-rule: the 2026-07-08 READ-TO-NUL over-read class TRAPS
3// under the --ptrprov declared mode, in-range programs stay accepted, the write leg (CWE-787)
4// traps too, and a provably-out-of-range CONSTANT index is refused at compile time naming the
5// provenance rule. Subjects: the toolchain compiler (argv[1], default the live builder) +
6// runtime/nx_boundscheck.nx + runtime/nx_boundscheck_constidx.nx.
7//
8// ARTIFACT PLACEMENT follows nx_cc_equiv_gate's measured convention: .s and stderr captures in
9// /tmp/nxbchk/ (data), but every ELF THAT MUST RUN lives under _build/ -- the NAS mounts /tmp
10// noexec (measured 2026-08-19: 0755 binary, execve EACCES 126), so a gate that stages runnables
11// in /tmp passes its compile teeth and fails every run tooth with no diagnostic. Names carry
12// the gate's own pid, because two concurrent runs racing one rename source is a documented
13// incident class in the incumbent (its line ~87).
14//
15// Every A/B pair runs through ONE build helper and ONE runner (no second ruler). Exit carries
16// the verdict via gv_verdict (D001). Phase exit codes are PRINTED before the teeth -- a
17// FAIL-only tooth cannot say why, and that blindness cost this gate's first NAS run its
18// diagnosis (the noexec above was found by hand-running the leftover binary).
19//
20// Usage: nx_boundscheck_gate [compiler_elf] (CWD = the tree root, like nx_cc_equiv_gate)
21// license_tier: ORIGINAL No hw writes (Rule 26).
22import "nx_syscalls.nx"
23import "nx_gate_verdict.nx"
24
25const BG_TRAP: i64 = 71 // NX_TRAP_BOUNDS -- the injected trap's exit_group code
26
27// fork + redirects + execve; parent waits; returns RAW wait status (the equiv-gate shape).
28func bg_run(path: *u8, argv: *i64, redir_out: i64, redir_err: i64) -> i64 {
29 let pid: i64 = sys_fork()
30 if pid == 0 {
31 if redir_out >= 0 { sys_dup3(redir_out, 1, 0) }
32 if redir_err >= 0 { sys_dup3(redir_err, 2, 0) }
33 let envp: *i64 = sys_mmap(16) as *i64
34 envp[0] = 0
35 sys_execve(path, argv, envp)
36 sys_exit(127)
37 }
38 let st: *i64 = sys_mmap(16) as *i64
39 sys_wait4(pid, st, 0)
40 return st[0]
41}
42
43// <pfx><pid><sfx> into dst (NUL-terminated). Per-run-unique paths, the incumbent's discipline.
44func bg_path(dst: *u8, pfx: *u8, pid: i64, sfx: *u8) -> *u8 {
45 var o: i64 = 0
46 while pfx[o] != (0 as u8) { dst[o] = pfx[o]; o = o + 1 }
47 let t: *u8 = sys_mmap(24)
48 var m: i64 = pid
49 var k: i64 = 0
50 if m <= 0 { t[0] = 48 as u8; k = 1 }
51 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
52 var i: i64 = 0
53 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 }
54 o = o + k
55 var j: i64 = 0
56 while sfx[j] != (0 as u8) { dst[o + j] = sfx[j]; j = j + 1 }
57 dst[o + j] = 0 as u8
58 return dst
59}
60
61// Compile src with cc (mode: 0 = default, 1 = --ptrprov), stderr CAPTURED to err_out (the
62// refusal-reason tooth reads it), then nxasm .s -> elf_out (via the per-run asm temp).
63// 0 ok, 1 compile-fail, 2 asm-fail, 3 rename-fail.
64func bg_build(cc: *u8, mode: i64, src: *u8, s_tmp: *u8, elf_out: *u8, err_out: *u8, asm_tmp: *u8) -> i64 {
65 let a: *i64 = sys_mmap(8 * 4) as *i64
66 var n: i64 = 0
67 a[n] = cc as i64; n = n + 1
68 if mode == 1 { a[n] = "--ptrprov" as *u8 as i64; n = n + 1 }
69 a[n] = src as i64; n = n + 1
70 a[n] = 0
71 let sfd: i64 = sys_openat_wr(s_tmp, 0x1a4)
72 let efd: i64 = sys_openat_wr(err_out, 0x1a4)
73 let st: i64 = bg_run(cc, a, sfd, efd)
74 sys_close(sfd)
75 sys_close(efd)
76 if st != 0 { return 1 }
77 let nxasm: *u8 = "_offc/nxasm_x86_main.elf\x00"
78 let a2: *i64 = sys_mmap(8 * 4) as *i64
79 a2[0] = nxasm as i64
80 a2[1] = s_tmp as i64
81 a2[2] = asm_tmp as i64
82 a2[3] = 0
83 if bg_run(nxasm, a2, 0 - 1, 0 - 1) != 0 { return 2 }
84 if sys_renameat(asm_tmp, elf_out) != 0 { return 3 }
85 nx_chmod(elf_out, 0x1ed)
86 return 0
87}
88
89// Run a built fixture with one phase argument; returns the child's EXIT CODE (not raw status).
90func bg_phase(elf: *u8, phase: *u8) -> i64 {
91 let a: *i64 = sys_mmap(8 * 3) as *i64
92 a[0] = elf as i64
93 a[1] = phase as i64
94 a[2] = 0
95 let st: i64 = bg_run(elf, a, 0 - 1, 0 - 1)
96 return wait_status_rc(st)
97}
98
99// Does file at path contain needle? 1 yes, 0 no/unreadable. Auto-sizing reader (no cap to
100// guess); explicit go-flag loops -- never a sentinel written into the cursor (nx_srclint class).
101func bg_file_has(path: *u8, needle: *u8) -> i64 {
102 let szp: *i64 = sys_mmap(16) as *i64
103 let b: *u8 = sys_read_file(path, szp)
104 if (b as i64) == 0 { return 0 }
105 let n: i64 = szp[0]
106 var nl: i64 = 0
107 while needle[nl] != (0 as u8) { nl = nl + 1 }
108 if nl == 0 { return 0 }
109 var i: i64 = 0
110 var found: i64 = 0
111 var go: i64 = 1
112 var j: i64 = 0
113 var ok: i64 = 0
114 var jg: i64 = 0
115 while go == 1 {
116 if i + nl > n { go = 0 } else {
117 j = 0
118 ok = 1
119 jg = 1
120 while jg == 1 {
121 if j >= nl { jg = 0 } else {
122 if b[i + j] != needle[j] { ok = 0; jg = 0 }
123 j = j + 1
124 }
125 }
126 if ok == 1 { found = 1; go = 0 }
127 i = i + 1
128 }
129 }
130 return found
131}
132
133func bg_val(name: *u8, v: i64) -> i64 {
134 gv_puts(" " as *u8)
135 gv_puts(name)
136 gv_puts("=" as *u8)
137 gv_num(v)
138 gv_puts("\n" as *u8)
139 return 0
140}
141
142func main(argc: i64, argv: *i64) -> i64 {
143 // Default subject = the PRODUCTION builder, resolvable from both trees' gate CWD:
144 // NAS tools-API runs gates with CWD=buildroot (buildroot/_offc/nx_cc_sovereign.elf is the
145 // binary /api/build forks); the laptop lane runs from nxc2/ where the same relative path is
146 // the live local compiler.
147 var cc: *u8 = "_offc/nx_cc_sovereign.elf\x00"
148 if argc >= 2 { cc = argv[1] as *u8 }
149 // CWD ANCHOR (the sov_build_run / equiv-gate convention, measured 2026-08-19): the tools-API
150 // exec lane starts organs in the SERVING ROOT, whose _offc carries no compiler -- the source
151 // tree and the builder live under buildroot/. The probe keys on THE BUILDER ITSELF, not the
152 // source tree: the laptop tree also carries a buildroot/runtime mirror (measured -- a
153 // source-keyed probe anchored the laptop run into a dir with no _offc and went 1/11 RED), but
154 // only the NAS serving root has buildroot/_offc/nx_cc_sovereign.elf. Already-anchored and
155 // laptop lanes miss the probe and stay put. Without this the gate's first MCP run failed
156 // every build tooth with an EMPTY stderr capture: execve of an absent compiler says nothing.
157 let bfd: i64 = sys_openat_rd("buildroot/_offc/nx_cc_sovereign.elf\x00" as *u8)
158 if bfd >= 0 { sys_close(bfd); sys_chdir("buildroot\x00" as *u8) }
159 sys_mkdir("/tmp/nxbchk\x00" as *u8, 0x1ed)
160 let pid: i64 = __syscall(172, 0, 0, 0, 0, 0, 0) // rv64 getpid (translated const path)
161
162 // per-run-unique artifact paths: data in /tmp/nxbchk, RUNNABLES under _build (exec-capable)
163 let p_ds: *u8 = bg_path(sys_mmap(128), "/tmp/nxbchk/d_\x00" as *u8, pid, ".s\x00" as *u8)
164 let p_de: *u8 = bg_path(sys_mmap(128), "/tmp/nxbchk/d_\x00" as *u8, pid, ".err\x00" as *u8)
165 let p_delf: *u8 = bg_path(sys_mmap(128), "_build/nxbchk_d_\x00" as *u8, pid, ".elf\x00" as *u8)
166 let p_ps: *u8 = bg_path(sys_mmap(128), "/tmp/nxbchk/p_\x00" as *u8, pid, ".s\x00" as *u8)
167 let p_pe: *u8 = bg_path(sys_mmap(128), "/tmp/nxbchk/p_\x00" as *u8, pid, ".err\x00" as *u8)
168 let p_pelf: *u8 = bg_path(sys_mmap(128), "_build/nxbchk_p_\x00" as *u8, pid, ".elf\x00" as *u8)
169 let p_cds: *u8 = bg_path(sys_mmap(128), "/tmp/nxbchk/cd_\x00" as *u8, pid, ".s\x00" as *u8)
170 let p_cde: *u8 = bg_path(sys_mmap(128), "/tmp/nxbchk/cd_\x00" as *u8, pid, ".err\x00" as *u8)
171 let p_cdelf: *u8 = bg_path(sys_mmap(128), "_build/nxbchk_cd_\x00" as *u8, pid, ".elf\x00" as *u8)
172 let p_cps: *u8 = bg_path(sys_mmap(128), "/tmp/nxbchk/cp_\x00" as *u8, pid, ".s\x00" as *u8)
173 let p_cpe: *u8 = bg_path(sys_mmap(128), "/tmp/nxbchk/cp_\x00" as *u8, pid, ".err\x00" as *u8)
174 let p_cpelf: *u8 = bg_path(sys_mmap(128), "_build/nxbchk_cp_\x00" as *u8, pid, ".elf\x00" as *u8)
175 let p_asm: *u8 = bg_path(sys_mmap(128), "_build/nxbchk_asm_\x00" as *u8, pid, ".elf\x00" as *u8)
176
177 let ctr: *i64 = gv_ctr()
178 gv_head("=== nx_boundscheck_gate -- LN3 raw-pointer provenance: --ptrprov traps the READ-TO-NUL class, accepts in-range, refuses const OOB at compile time ===" as *u8)
179
180 let fix: *u8 = "runtime/nx_boundscheck.nx\x00"
181 let cfx: *u8 = "runtime/nx_boundscheck_constidx.nx\x00"
182
183 // -- default-mode builds --
184 let bd: i64 = bg_build(cc, 0, fix, p_ds, p_delf, p_de, p_asm)
185 var t1: i64 = 0
186 if bd == 0 { t1 = 1 }
187 gv_check("T1 fixture-compiles-under-default (organ is a normal citizen of the tree)" as *u8, t1, ctr)
188 var d_ok: i64 = 0 - 1
189 var d_rd: i64 = 0 - 1
190 var d_wr: i64 = 0 - 1
191 if bd == 0 {
192 d_ok = bg_phase(p_delf, "ok\x00" as *u8)
193 d_rd = bg_phase(p_delf, "read\x00" as *u8)
194 d_wr = bg_phase(p_delf, "write\x00" as *u8)
195 }
196 bg_val("d_build_rc" as *u8, bd)
197 bg_val("d_ok_exit" as *u8, d_ok)
198 bg_val("d_read_exit" as *u8, d_rd)
199 bg_val("d_write_exit" as *u8, d_wr)
200 var t2: i64 = 0
201 if d_ok == 0 { t2 = 1 }
202 gv_check("T2 default-inrange-control-exit0" as *u8, t2, ctr)
203 var t3: i64 = 0
204 if d_rd == 0 { t3 = 1 }
205 gv_check("T3 neg-control-default-READ-overrun-survives-unchecked (anti-vacuity: the access T7 must trap demonstrably executes and escapes the allocation)" as *u8, t3, ctr)
206 var t4: i64 = 0
207 if d_wr == 0 { t4 = 1 }
208 gv_check("T4 neg-control-default-WRITE-overrun-survives-unchecked" as *u8, t4, ctr)
209
210 // -- ptrprov-mode builds --
211 let bp: i64 = bg_build(cc, 1, fix, p_ps, p_pelf, p_pe, p_asm)
212 var t5: i64 = 0
213 if bp == 0 { t5 = 1 }
214 gv_check("T5 fixture-compiles-under-ptrprov (runtime legs are legal source either mode)" as *u8, t5, ctr)
215 var p_ok: i64 = 0 - 1
216 var p_rd: i64 = 0 - 1
217 var p_wr: i64 = 0 - 1
218 if bp == 0 {
219 p_ok = bg_phase(p_pelf, "ok\x00" as *u8)
220 p_rd = bg_phase(p_pelf, "read\x00" as *u8)
221 p_wr = bg_phase(p_pelf, "write\x00" as *u8)
222 }
223 bg_val("p_build_rc" as *u8, bp)
224 bg_val("p_ok_exit" as *u8, p_ok)
225 bg_val("p_read_exit" as *u8, p_rd)
226 bg_val("p_write_exit" as *u8, p_wr)
227 var t6: i64 = 0
228 if p_ok == 0 { t6 = 1 }
229 gv_check("T6 ptrprov-inrange-control-exit0 (a deny mode that refuses everything fails HERE; checks armed AND in-range in one program)" as *u8, t6, ctr)
230 var t7: i64 = 0
231 if p_rd == BG_TRAP { t7 = 1 }
232 gv_check("T7 ptrprov-READ-overrun-traps-71 (CWE-125, the 2026-07-08 READ-TO-NUL class -- the rung's done-rule)" as *u8, t7, ctr)
233 var t8: i64 = 0
234 if p_wr == BG_TRAP { t8 = 1 }
235 gv_check("T8 ptrprov-WRITE-overrun-traps-71 (CWE-787, the corruption primitive)" as *u8, t8, ctr)
236
237 // -- compile-time leg --
238 let cd: i64 = bg_build(cc, 0, cfx, p_cds, p_cdelf, p_cde, p_asm)
239 var t9: i64 = 0
240 if cd == 0 { t9 = 1 }
241 gv_check("T9 neg-control-constidx-compiles-under-default (the hazard record still builds)" as *u8, t9, ctr)
242 let cp: i64 = bg_build(cc, 1, cfx, p_cps, p_cpelf, p_cpe, p_asm)
243 bg_val("cd_build_rc" as *u8, cd)
244 bg_val("cp_build_rc" as *u8, cp)
245 var t10: i64 = 0
246 if cp != 0 { t10 = 1 }
247 gv_check("T10 ptrprov-constidx-refused-at-compile-time" as *u8, t10, ctr)
248 let t11: i64 = bg_file_has(p_cpe,
249 "outside the allocation this pointer provably refers to\x00" as *u8)
250 gv_check("T11 refusal-names-the-provenance-rule (not some other error wearing the refusal)" as *u8, t11, ctr)
251
252 let rc: i64 = gv_verdict("BOUNDSCHECK-PTRPROV" as *u8, ctr, "ptrprov declared mode" as *u8)
253 sys_exit(rc)
254 return rc
255}