code wiki / (root) / nx_backend_audit_test.nx

nx_backend_audit_test.nx source

↩ module page · 179 lines · 7265 B

1// nx_backend_audit.nx -- self-host substrate audit, written IN 2// NishiLang. Per user 2026-05-20: "we arent building lints off of 3// nishi we are nishi from bits up". The substrate audits itself 4// using its own file I/O + byte-search primitives. No bash, no 5// grep, no awk -- the lint runs as a NishiLang program compiled by 6// the same compiler it audits. 7// 8// Three substrate-honest audits in one program: 9// A. Sub-word load zero-extend audit (was bash). Scans backend 10// ctx files for `_emit_load_(byte|word|dword)_signed`. 11// B. Syscall translation table audit (was bash). Asserts every 12// SYS_X used in nx_syscalls.nx has a corresponding `if num ==` 13// entry in the x86_64 translation table. 14// C. Call-arity overflow audit (was bash). Asserts parser 15// parse_primary_call has a die path for n_args overflow. 16// 17// Each audit emits one line to stderr: name + PASS/FAIL. Exit code 18// = sum of (1 per failure). Exit 0 == all PASS. 19// 20// license_tier: ORIGINAL 21// lineage_id: nishi_backend_audit_q10 22 23import "nx_syscalls.nx" 24 25// ---- byte buffer helpers -------------------------------------------- 26 27func _audit_write(s: *u8, n: i64) -> i64 { 28 return sys_write(2, s, n) 29} 30 31func _audit_strlen(s: *u8) -> i64 { 32 var n: i64 = 0 33 while s[n] != 0 { n = n + 1 } 34 return n 35} 36 37func _audit_say(s: *u8) -> i64 { 38 return _audit_write(s, _audit_strlen(s)) 39} 40 41// Substring search: returns offset of `needle` in `[buf, buf+n)`, or 42// -1 if not found. Naive O(n*m); fine for the audit's small files. 43func _audit_find(buf: *u8, n: i64, needle: *u8) -> i64 { 44 let m: i64 = _audit_strlen(needle) 45 if m == 0 { return 0 } 46 if m > n { return 0 - 1 } 47 var i: i64 = 0 48 let last: i64 = n - m 49 while i <= last { 50 var k: i64 = 0 51 var eq: i64 = 1 52 while k < m { 53 if (buf[i + k] & 0xff) != (needle[k] & 0xff) { eq = 0; k = m } 54 else { k = k + 1 } 55 } 56 if eq == 1 { return i } 57 i = i + 1 58 } 59 return 0 - 1 60} 61 62func _audit_contains(buf: *u8, n: i64, needle: *u8) -> i64 { 63 if _audit_find(buf, n, needle) >= 0 { return 1 } 64 return 0 65} 66 67// ---- Audit A: subword load zero-extend ------------------------------- 68 69func _audit_a_subword(failures: *i64) -> i64 { 70 let path_a: *u8 = "runtime/nx_x86_64_ctx.nx" as *u8 71 let len_p: *i64 = sys_mmap(16) as *i64 72 let buf: *u8 = sys_read_file(path_a, len_p) 73 let n: i64 = *len_p 74 if (buf as i64) == 0 { 75 _audit_say("A subword-load: SKIP (no nx_x86_64_ctx.nx)\n" as *u8) 76 return 0 77 } 78 // 2026-07-10 UPDATE (subword sign-extend debt fix): signed subword loads are now ALLOWED but MUST 79 // be gated on the sext bit (x86ctx_type_sext), so `*u8` still ZERO-extends (the x509 0xA0->160 80 // witness). The invariant flips: the sext GATE must be PRESENT -- its absence would mean signed 81 // loads are either impossible again (old debt) or unconditional (the sign-extend-corrupts-u8 bug). 82 var bad: i64 = 0 83 if _audit_contains(buf, n, "x86ctx_type_sext" as *u8) == 0 { bad = 1 } 84 if bad == 1 { 85 _audit_say("A subword-load: FAIL -- sext gate x86ctx_type_sext MISSING from nx_x86_64_ctx.nx (signed subword loads must be sext-gated)\n" as *u8) 86 *failures = *failures + 1 87 return 1 88 } 89 _audit_say("A subword-load: PASS (sext-gated: *i8/*i16/*i32 sign-extend, *u8 zero-extends)\n" as *u8) 90 return 0 91} 92 93// ---- Audit B: syscall translation table ------------------------------ 94// 95// Strategy: assert nx_syscalls.nx and nx_x86_64_ctx.nx both exist; 96// for every `__syscall(SYS_X, ...)` reference, the const value of 97// SYS_X must appear in the translation table. Parsing arbitrary 98// NishiLang from inside NishiLang is too much; we cover the common 99// case by checking that key SYS_X values are translated. 100 101func _audit_b_syscall_translation(failures: *i64) -> i64 { 102 let path_x: *u8 = "runtime/nx_x86_64_ctx.nx" as *u8 103 let len_p: *i64 = sys_mmap(16) as *i64 104 let buf: *u8 = sys_read_file(path_x, len_p) 105 let n: i64 = *len_p 106 if (buf as i64) == 0 { 107 _audit_say("B syscall-xlate: SKIP (no nx_x86_64_ctx.nx)\n" as *u8) 108 return 0 109 } 110 // Spot-check the syscalls that have bitten us: sendto(206), 111 // recvfrom(207), lseek(62), getdents64(61), socket(198). 112 var missing: i64 = 0 113 if _audit_contains(buf, n, "if num == 198" as *u8) != 1 { missing = missing + 1 } 114 if _audit_contains(buf, n, "if num == 206" as *u8) != 1 { missing = missing + 1 } 115 if _audit_contains(buf, n, "if num == 207" as *u8) != 1 { missing = missing + 1 } 116 if _audit_contains(buf, n, "if num == 62" as *u8) != 1 { missing = missing + 1 } 117 if _audit_contains(buf, n, "if num == 61" as *u8) != 1 { missing = missing + 1 } 118 if missing > 0 { 119 _audit_say("B syscall-xlate: FAIL -- one or more critical SYS_* numbers missing from x86ctx_rv64_to_x86_64_syscall\n" as *u8) 120 *failures = *failures + 1 121 return 1 122 } 123 _audit_say("B syscall-xlate: PASS\n" as *u8) 124 return 0 125} 126 127// ---- Audit C: call-arity overflow ------------------------------------ 128// 129// parse_primary_call must die when n_args exceeds the IR cap. We 130// look for a `parse_die` call near `n_args >=` in nx_parse.nx -- the 131// substrate-honest signature of an explicit-die ladder ceiling. 132 133func _audit_c_call_arity(failures: *i64) -> i64 { 134 let path_p: *u8 = "runtime/nx_parse.nx" as *u8 135 let len_p: *i64 = sys_mmap(16) as *i64 136 let buf: *u8 = sys_read_file(path_p, len_p) 137 let n: i64 = *len_p 138 if (buf as i64) == 0 { 139 _audit_say("C call-arity: SKIP (no nx_parse.nx)\n" as *u8) 140 return 0 141 } 142 var has_cap: i64 = 0 143 if _audit_contains(buf, n, "n_args >= 8" as *u8) == 1 { has_cap = 1 } 144 if _audit_contains(buf, n, "n_args >= 16" as *u8) == 1 { has_cap = 1 } 145 // 24 = the current cap (op16-23 bump, 2026-06-18). The audit went STALE when the cap moved 16->24 146 // and silently FAILED ever since -- caught 2026-07-10 during the sext debt sweep. Keep every 147 // historical cap value accepted so the audit checks "a die-guarded cap EXISTS", not a magic number. 148 if _audit_contains(buf, n, "n_args >= 24" as *u8) == 1 { has_cap = 1 } 149 if _audit_contains(buf, n, "n_args >= 32" as *u8) == 1 { has_cap = 1 } 150 var has_die: i64 = 0 151 if _audit_contains(buf, n, "parse_die" as *u8) == 1 { has_die = 1 } 152 if has_cap != 1 { 153 _audit_say("C call-arity: FAIL -- no n_args overflow cap in nx_parse.nx\n" as *u8) 154 *failures = *failures + 1 155 return 1 156 } 157 if has_die != 1 { 158 _audit_say("C call-arity: FAIL -- no parse_die call in nx_parse.nx (silent overflow)\n" as *u8) 159 *failures = *failures + 1 160 return 1 161 } 162 _audit_say("C call-arity: PASS\n" as *u8) 163 return 0 164} 165 166func main() -> i64 { 167 _audit_say("nx_backend_audit: bits-up self-host substrate audit\n" as *u8) 168 let failures_p: *i64 = sys_mmap(16) as *i64 169 *failures_p = 0 170 _audit_a_subword(failures_p) 171 _audit_b_syscall_translation(failures_p) 172 _audit_c_call_arity(failures_p) 173 if *failures_p == 0 { 174 _audit_say("nx_backend_audit: ALL PASS\n" as *u8) 175 return 0 176 } 177 _audit_say("nx_backend_audit: FAIL\n" as *u8) 178 return *failures_p 179}