nx_ctrlbyte_gate.nx source
↩ module page · 259 lines · 10041 B
1// nx_ctrlbyte_gate.nx -- CONTROL-BYTE CORRUPTION TOOTH (debt 1785520719 / 1785521381, 2026-07-31).
2// THE WOUND: 210 .nx sources had ONE LINE INSIDE putn overwritten by a run of 0x0E (Shift-Out) bytes.
3// Every organ so hit printed EMPTY status=, bytes= and counts -- a fleet of blind instruments.
4// It survived because BOTH existing rulers called it healthy: nx_srcguard rules on SYMBOLS (a line
5// replaced inside a function changes no top-level symbol, so it can never fire) and nx_fs read
6// RENDERS A 0x0E RUN AS AN INNOCENT BLANK LINE. A symbol ruler and a byte ruler answer DIFFERENT
7// questions; the corpus had only the first.
8// This is the byte ruler: any byte outside {tab, LF, CR, printable, UTF-8 >=0x80} in a .nx file is RED.
9// A rule nothing must remember beats a list nobody maintains.
10// verbs: scan [dir] | selftest <writable-scratch-dir>
11// expect_exit: 0 license_tier: ORIGINAL
12import "nx_syscalls.nx"
13
14const CB_DIRBUF: i64 = 131072
15const CB_FCAP: i64 = 4194304
16const CB_PATH: i64 = 1024
17const CB_DT_DIR: i64 = 4
18const CB_MODE_644: i64 = 420
19const CB_MAX_REPORT: i64 = 40
20const CB_SIG_RUN: i64 = 119
21
22func cb_vlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
23func cb_puts(s: *u8) -> i64 { sys_write(1, s, cb_vlen(s)); return 0 }
24func cb_putn(v: i64) -> i64 {
25 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
26 var m: i64 = v
27 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
28 let d: *u8 = sys_mmap(24)
29 var k: i64 = 0
30 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
31 var j: i64 = k - 1
32 while j >= 0 { sys_write(1, ((d as i64)+j) as *u8, 1); j = j - 1 }
33 return 0
34}
35func cb_hex2(v: i64) -> i64 {
36 let tbl: *u8 = "0123456789abcdef" as *u8
37 let hi: i64 = v / 16
38 let lo: i64 = v % 16
39 sys_write(1, ((tbl as i64)+hi) as *u8, 1)
40 sys_write(1, ((tbl as i64)+lo) as *u8, 1)
41 return 0
42}
43
44// Is this byte illegal inside a .nx source file? tab/LF/CR are legal whitespace; >=0x80 is UTF-8 in
45// comments and must NOT be flagged, or the gate cries wolf and gets ignored.
46func cb_is_bad(c: i64) -> i64 {
47 if c == 9 { return 0 }
48 if c == 10 { return 0 }
49 if c == 13 { return 0 }
50 if c == 127 { return 1 }
51 if c < 32 { return 1 }
52 return 0
53}
54
55func cb_ends_nx(nm: *u8) -> i64 {
56 let n: i64 = cb_vlen(nm)
57 if n < 4 { return 0 }
58 if nm[n-3] != (46 as u8) { return 0 }
59 if nm[n-2] != (110 as u8) { return 0 }
60 if nm[n-1] != (120 as u8) { return 0 }
61 return 1
62}
63
64func cb_join(dst: *u8, dir: *u8, nm: *u8) -> i64 {
65 var k: i64 = 0
66 var i: i64 = 0
67 let dl: i64 = cb_vlen(dir)
68 while i < dl { dst[k] = dir[i]; k = k + 1; i = i + 1 }
69 if k > 0 { if dst[k-1] != (47 as u8) { dst[k] = 47 as u8; k = k + 1 } }
70 i = 0
71 while nm[i] != (0 as u8) { dst[k] = nm[i]; k = k + 1; i = i + 1 }
72 dst[k] = 0 as u8
73 return k
74}
75
76func cb_scan_file(path: *u8, buf: *u8, reported: *i64) -> i64 {
77 let fd: i64 = sys_openat_rd(path)
78 if fd < 0 { return 0 }
79 var n: i64 = 0
80 var go: i64 = 1
81 while go == 1 { let r: i64 = sys_read(fd, ((buf as i64)+n) as *u8, CB_FCAP-n); if r <= 0 { go = 0 } else { n = n + r } if n >= CB_FCAP { go = 0 } }
82 sys_close(fd)
83 var bad: i64 = 0
84 var line: i64 = 1
85 var firstline: i64 = 0
86 var firstbyte: i64 = 0
87 var i: i64 = 0
88 while i < n {
89 let c: i64 = buf[i] as i64
90 if c == 10 { line = line + 1 }
91 if cb_is_bad(c) == 1 {
92 bad = bad + 1
93 if firstline == 0 { firstline = line; firstbyte = c }
94 }
95 i = i + 1
96 }
97 if bad > 0 { if reported[0] < CB_MAX_REPORT {
98 cb_puts(" RED " as *u8)
99 cb_puts(path)
100 cb_puts(" line=" as *u8)
101 cb_putn(firstline)
102 cb_puts(" byte=0x" as *u8)
103 cb_hex2(firstbyte)
104 cb_puts(" count=" as *u8)
105 cb_putn(bad)
106 cb_puts("\n" as *u8)
107 reported[0] = reported[0] + 1
108 } }
109 return bad
110}
111
112func cb_scan_dir(dir: *u8, recurse: i64, buf: *u8, stats: *i64, reported: *i64) -> i64 {
113 let fd: i64 = sys_openat_rd(dir)
114 if fd < 0 { return 0 }
115 let dbuf: *u8 = sys_mmap(CB_DIRBUF)
116 var go: i64 = 1
117 while go == 1 {
118 let nb: i64 = sys_getdents64(fd, dbuf, CB_DIRBUF)
119 if nb <= 0 { go = 0 } else {
120 var off: i64 = 0
121 while off < nb {
122 let rec: *u8 = (dbuf as i64 + off) as *u8
123 let rl: i64 = dirent_reclen(rec)
124 if rl <= 0 { off = nb } else {
125 let nm: *u8 = dirent_name(rec)
126 let ty: i64 = dirent_type(rec)
127 var skip: i64 = 0
128 if nm[0] == (46 as u8) { skip = 1 }
129 if skip == 0 { if ty == CB_DT_DIR {
130 if recurse == 1 {
131 let sub: *u8 = sys_mmap(CB_PATH)
132 cb_join(sub, dir, nm)
133 cb_scan_dir(sub, 0, buf, stats, reported)
134 }
135 } else {
136 if cb_ends_nx(nm) == 1 {
137 let p: *u8 = sys_mmap(CB_PATH)
138 cb_join(p, dir, nm)
139 stats[0] = stats[0] + 1
140 let bad: i64 = cb_scan_file(p, buf, reported)
141 if bad > 0 { stats[1] = stats[1] + 1; stats[2] = stats[2] + bad }
142 }
143 } }
144 off = off + rl
145 }
146 }
147 }
148 }
149 sys_close(fd)
150 return 0
151}
152
153func cb_write_file(path: *u8, data: *u8, n: i64) -> i64 {
154 let fd: i64 = sys_openat_wr(path, CB_MODE_644)
155 if fd < 0 { return 0 - 1 }
156 sys_write(fd, data, n)
157 sys_close(fd)
158 return n
159}
160
161func main(argc: i64, argv: *i64) -> i64 {
162 if argc < 2 {
163 cb_puts("usage: nx_ctrlbyte_gate {scan [dir] | selftest <writable-scratch-dir>}\n" as *u8)
164 sys_exit(2)
165 return 2
166 }
167 let verb: *u8 = argv[1] as *u8
168 let buf: *u8 = sys_mmap(CB_FCAP)
169 let stats: *i64 = sys_mmap(64) as *i64
170 let reported: *i64 = sys_mmap(16) as *i64
171
172 if verb[1] == (99 as u8) {
173 var dir: *u8 = "buildroot/runtime" as *u8
174 if argc >= 3 { dir = argv[2] as *u8 }
175 cb_puts("NX-CTRLBYTE-GATE scan dir=" as *u8)
176 cb_puts(dir)
177 cb_puts("\n" as *u8)
178 cb_scan_dir(dir, 1, buf, stats, reported)
179 cb_puts("scanned=" as *u8)
180 cb_putn(stats[0])
181 cb_puts(" corrupt_files=" as *u8)
182 cb_putn(stats[1])
183 cb_puts(" bad_bytes=" as *u8)
184 cb_putn(stats[2])
185 // FAIL-CLOSED ON AN EMPTY SCAN: a missing or empty directory used to report GREEN, which is
186 // a vacuous pass -- a scan that examined NOTHING proves nothing, and would silently bless a
187 // typo in the path forever. Caught by this gate scoring buildroot/nxasm as scanned=0 GREEN.
188 if stats[0] == 0 { cb_puts(" VERDICT=RED scanned 0 files -- vacuous scan, check the path" as *u8); cb_puts("\n" as *u8); sys_exit(1); return 1 }
189 if stats[1] == 0 { cb_puts(" VERDICT=GREEN\n" as *u8); sys_exit(0); return 0 }
190 cb_puts(" VERDICT=RED\n" as *u8)
191 sys_exit(1)
192 return 1
193 }
194
195 if verb[1] == (101 as u8) {
196 if argc < 3 { cb_puts("selftest needs <writable-scratch-dir>\n" as *u8); sys_exit(2); return 2 }
197 let scratch: *u8 = argv[2] as *u8
198 var pass: i64 = 0
199 var tot: i64 = 0
200
201 let clean: *u8 = sys_mmap(CB_PATH)
202 cb_join(clean, scratch, "cbg_clean.nx" as *u8)
203 let cdata: *u8 = "func f() -> i64 { return 0 }\n" as *u8
204 cb_write_file(clean, cdata, cb_vlen(cdata))
205 reported[0] = 0
206 let cbad: i64 = cb_scan_file(clean, buf, reported)
207 tot = tot + 1
208 if cbad == 0 { pass = pass + 1; cb_puts("T1 PASS clean file scores 0 bad bytes\n" as *u8) }
209 if cbad != 0 { cb_puts("T1 FAIL clean file flagged -- gate over-fires\n" as *u8) }
210
211 // NON-VACUITY: reproduce the EXACT 119-byte 0x0E signature and require the gate to catch it.
212 // A gate that cannot fail on the defect it names is not evidence.
213 let dirty: *u8 = sys_mmap(CB_PATH)
214 cb_join(dirty, scratch, "cbg_dirty.nx" as *u8)
215 let dd: *u8 = sys_mmap(512)
216 var w: i64 = 0
217 let pre: *u8 = "func f() -> i64 {\n" as *u8
218 var q: i64 = 0
219 while pre[q] != (0 as u8) { dd[w] = pre[q]; w = w + 1; q = q + 1 }
220 var z: i64 = 0
221 while z < CB_SIG_RUN { dd[w] = 14 as u8; w = w + 1; z = z + 1 }
222 dd[w] = 10 as u8
223 w = w + 1
224 let post: *u8 = " return 0\n}\n" as *u8
225 q = 0
226 while post[q] != (0 as u8) { dd[w] = post[q]; w = w + 1; q = q + 1 }
227 cb_write_file(dirty, dd, w)
228 reported[0] = 0
229 let dbad: i64 = cb_scan_file(dirty, buf, reported)
230 tot = tot + 1
231 if dbad == CB_SIG_RUN { pass = pass + 1; cb_puts("T2 PASS non-vacuous: caught all 119 0x0E bytes\n" as *u8) }
232 if dbad != CB_SIG_RUN { cb_puts("T2 FAIL expected 119 bad bytes, got " as *u8); cb_putn(dbad); cb_puts(" -- GATE IS VACUOUS\n" as *u8) }
233
234 tot = tot + 1
235 var cls: i64 = 1
236 if cb_is_bad(9) != 0 { cls = 0 }
237 if cb_is_bad(10) != 0 { cls = 0 }
238 if cb_is_bad(13) != 0 { cls = 0 }
239 if cb_is_bad(65) != 0 { cls = 0 }
240 if cb_is_bad(14) != 1 { cls = 0 }
241 if cb_is_bad(0) != 1 { cls = 0 }
242 if cb_is_bad(127) != 1 { cls = 0 }
243 if cls == 1 { pass = pass + 1; cb_puts("T3 PASS classifier accepts tab LF CR alpha, rejects 0x0E NUL DEL\n" as *u8) }
244 if cls == 0 { cb_puts("T3 FAIL classifier wrong\n" as *u8) }
245
246 cb_puts("NX-CTRLBYTE-GATE SELFTEST " as *u8)
247 cb_putn(pass)
248 cb_puts("/" as *u8)
249 cb_putn(tot)
250 if pass == tot { cb_puts(" GREEN\n" as *u8); sys_exit(0); return 0 }
251 cb_puts(" RED\n" as *u8)
252 sys_exit(1)
253 return 1
254 }
255
256 cb_puts("unknown verb\n" as *u8)
257 sys_exit(2)
258 return 2
259}