code wiki / (root) / nx_boundscheck_gate.nx

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}