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}