nx_osattest.nx source
↩ module page · 708 lines · 33728 B
1// nx_osattest.nx -- LR6: NISHIOS + NISHI BROWSER LANGUAGE-RING INHERITANCE ATTESTATION.
2//
3// WHAT IT ANSWERS. Chromium's Rule of 2 forbids combining untrustworthy input, an unsafe language and
4// high privilege; the incumbents discharge it by SANDBOXING AROUND their language. A kernel-up
5// single-language stack can discharge the unsafe-language leg directly -- but only if that is MEASURED,
6// not asserted. This organ rebuilds the NishiOS kernel closure and the Nishi Browser closure under each
7// DECLARED safety mode of a named compiler and writes a receipt both compare pages cite.
8//
9// IT IS A MEASUREMENT ORGAN. It never edits the compiler, the kernel or the browser; it never
10// assembles, never runs a produced binary, and never promotes anything. Its only writes are the receipt
11// and its own scratch under the conf's scratch_dir.
12//
13// A REFUSAL IS A FINDING, NOT A FAILURE OF THIS ORGAN. A closure that will not compile under an
14// enforcing mode is exactly the Rule-of-2 evidence the rung exists to produce, so REFUSED cells are DATA
15// and leave the exit code at 0. What DOES move the exit code is an attestation that cannot be trusted:
16// a subject whose source does not resolve (3), or one that will not build under the DEFAULT mode (1), in
17// which case nothing measured under a mode is attributable to that mode.
18//
19// THE HAZARD THAT SHAPES THE WHOLE DESIGN. nx_compile_x86's long-flag dispatch keys on ONE BYTE
20// (arg[2]). An unrecognised long flag is therefore SILENTLY IGNORED and the compile still returns 0 --
21// so an attester that trusted a flag NAME would publish ATTESTED for a mode enforcing nothing, and its
22// green would be indistinguishable from a real one. Every axis is therefore proven LIVE first, from
23// BOTH sides, against the compiler under test: for a refuse-kind mode the hazard fixture must be REFUSED
24// with the DECLARED rule slug in stderr while a clean fixture is still ACCEPTED; for the emit-kind crash
25// guard the marker must appear by default and vanish under the opt-out. An axis that cannot be proven
26// live is UNOBSERVABLE -- neither clean nor refused -- and is NAMED in the receipt beside the
27// denominator, because an abstention nobody reads is a lie nobody told.
28//
29// COMPOSED, NOT RE-IMPLEMENTED: nx_ccgate_lib (the ONE compiler-spawn driver), nx_incclosure_lib (the
30// ONE name->path resolver and hex encoder), nx_lineconf_lib (the ONE line-anchored conf reader),
31// nx_closurehash (the ONE closure enumerator, FORKED for provenance). Every path, flag, fixture and
32// rule slug is a row in knowledge/lang_attest.conf (rule 11).
33//
34// RESOURCE ENVELOPE. Allocations are mmap'd once and live for the process (arena doctrine, no free):
35// tables sized from the conf's own counts, one receipt buffer DERIVED from those counts, one digit
36// scratch. Per run it spawns 4 compiles per refuse-axis and 2 per emit-axis (all on sub-2 KB fixtures),
37// plus 1 baseline + one-per-live-refuse-axis compiles per closure and one nx_closurehash fork per
38// closure. The emit axis costs NO extra compile: it reads the baseline assembly already produced.
39// Scratch .s files are REUSED in place (sys_openat_wr is O_TRUNC), so peak scratch is one baseline .s
40// plus one mode .s, never one per cell.
41//
42// usage: nx_osattest <compiler_elf> [conf_path] [receipt_path]
43// The compiler is MANDATORY and there is deliberately NO DEFAULT: a silent default once produced a
44// fully-formed verdict about a stale leftover binary, and an attestation that cannot name what it
45// measured is worse than none. Paths resolve after anchoring to the buildroot.
46// license_tier: ORIGINAL No hw writes (Rule 26).
47import "nx_syscalls.nx"
48import "nx_sha256.nx"
49import "nx_ccgate_lib.nx"
50import "nx_lineconf_lib.nx"
51import "nx_incclosure_lib.nx"
52
53// Table bounds. These REFUSE past themselves rather than truncating: a receipt cut short at a cap would
54// publish a confident partition for a set it never saw (the nx_closurehash contract, adopted verbatim).
55const OA_MAXC: i64 = 16
56const OA_MAXA: i64 = 16
57const OA_FW: i64 = 256 // per-field slot width (the longest declared rule slug is 52 bytes)
58const OA_PW: i64 = 1024 // path width; mirrors ICL_PATHW so a resolved path can never overflow
59const OA_KEYW: i64 = 64
60const OA_ROWW: i64 = 640 // receipt bytes budgeted per emitted row; the TOTAL cap is derived below
61const OA_HDRW: i64 = 8192 // header + footer allowance in the same derivation
62const OA_DIGW: i64 = 32
63const OA_WORD: i64 = 8
64const OA_DIG64: i64 = 64
65
66const OA_SP: i64 = 32
67const OA_NL: i64 = 10
68const OA_MINUS: i64 = 45
69const OA_ZERO: i64 = 48
70const OA_NINE: i64 = 57
71const OA_DEC: i64 = 10
72const OA_SHA_HEXW: i64 = 80
73
74// cell states -- the partition. Every (closure, axis) cell carries exactly one, and they SUM.
75const OA_ATTESTED: i64 = 0
76const OA_REFUSED: i64 = 1
77const OA_GUARD_ABSENT: i64 = 2
78const OA_UNOBSERVABLE: i64 = 3
79const OA_BASELINE_FAIL: i64 = 4
80const OA_SUBJECT_ABSENT: i64 = 5
81
82const OA_EXIT_OK: i64 = 0
83const OA_EXIT_BASELINE: i64 = 1
84const OA_EXIT_USAGE: i64 = 2
85const OA_EXIT_UNPROVEN: i64 = 3
86
87// subject states
88const OA_SUB_OK: i64 = 0
89const OA_SUB_BASE: i64 = 1
90const OA_SUB_ABSENT: i64 = 2
91
92static oa_dbuf: i64
93func oa_digits() -> *u8 {
94 if oa_dbuf == 0 { oa_dbuf = sys_mmap(OA_DIGW) as i64 }
95 return oa_dbuf as *u8
96}
97
98func oa_slot(b: *u8, i: i64, w: i64) -> *u8 { return ((b as i64) + i * w) as *u8 }
99
100// EVERY write to stderr goes through here. A hand-counted length beside a string literal is a second
101// copy of that literal's shape and the two drift silently -- derive it, never count it.
102func oa_err(s: *u8) -> i64 { sys_write(2, s, icl_len(s)); return 0 }
103
104// copy s into dst and NUL-TERMINATE. icl_cat deliberately does not terminate, so every field slot goes
105// through this: relying on mmap's zero fill for the terminator is an unstated dependency, not a design.
106func oa_set(dst: *u8, s: *u8) -> i64 {
107 let o: i64 = icl_cat(dst, 0, s)
108 dst[o] = 0 as u8
109 return o
110}
111
112// append a decimal integer at rb[ro]; returns the new offset
113func oa_addn(rb: *u8, ro: i64, v: i64) -> i64 {
114 let t: *u8 = oa_digits()
115 var m: i64 = v
116 var o: i64 = ro
117 if m < 0 { rb[o] = OA_MINUS as u8; o = o + 1; m = 0 - m }
118 var k: i64 = 0
119 if m == 0 { t[0] = OA_ZERO as u8; k = 1 }
120 while m > 0 { t[k] = (OA_ZERO + (m % OA_DEC)) as u8; m = m / OA_DEC; k = k + 1 }
121 var i: i64 = 0
122 while i < k { rb[o + i] = t[k - 1 - i]; i = i + 1 }
123 return o + k
124}
125
126// build "<pfx><idx><sfx>" NUL-terminated into dst -- the conf's indexed keys (closure0_name, axis2_rule)
127func oa_key(dst: *u8, pfx: *u8, idx: i64, sfx: *u8) -> i64 {
128 var o: i64 = icl_cat(dst, 0, pfx)
129 o = oa_addn(dst, o, idx)
130 o = icl_cat(dst, o, sfx)
131 dst[o] = 0 as u8
132 return o
133}
134
135// "<a><b>" NUL-terminated into a fresh buffer -- scratch-dir prefixes for ccg_path
136func oa_join(a: *u8, b: *u8) -> *u8 {
137 let d: *u8 = sys_mmap(OA_PW)
138 var o: i64 = icl_cat(d, 0, a)
139 o = icl_cat(d, o, b)
140 d[o] = 0 as u8
141 return d
142}
143
144func oa_is_undeclared(s: *u8) -> i64 { return icl_seq(s, "_UNDECLARED_" as *u8) }
145
146// compile src with cc under an optional flag; returns the compiler's own EXIT CODE (not raw status), so
147// a caller can tell a refusal from a signal death. Composes ccg_compile: the estate keeps exactly ONE
148// place that spawns the compiler.
149func oa_cc(cc: *u8, flag: *u8, src: *u8, sout: *u8, eout: *u8) -> i64 {
150 let st: i64 = ccg_compile(cc, flag, src, sout, eout)
151 return wait_status_rc(st)
152}
153
154// fork the ONE closure enumerator. Returns its exit code; 127 means the elf was not there at all, which
155// is an ABSENT ARTIFACT and not a broken closure -- named, never folded into a real result.
156func oa_closurehash(elf: *u8, target: *u8, root: *u8, outp: *u8, errp: *u8) -> i64 {
157 let a: *i64 = sys_mmap(OA_WORD * 4) as *i64
158 a[0] = elf as i64
159 a[1] = target as i64
160 a[2] = root as i64
161 a[3] = 0
162 let ofd: i64 = sys_openat_wr(outp, CCG_MODE_RW)
163 let efd: i64 = sys_openat_wr(errp, CCG_MODE_RW)
164 let st: i64 = ccg_run(elf, a, ofd, efd)
165 sys_close(ofd)
166 sys_close(efd)
167 return wait_status_rc(st)
168}
169
170// copy b[o..] into out until a space or newline. Explicit go-flag: the cursor is never the sentinel.
171func oa_word_at(b: *u8, n: i64, o: i64, out: *u8, cap: i64) -> i64 {
172 var i: i64 = o
173 var w: i64 = 0
174 var go: i64 = 1
175 while go == 1 {
176 if i >= n { go = 0 } else {
177 let c: i64 = b[i] as i64
178 if c == OA_SP { go = 0 } else {
179 if c == OA_NL { go = 0 } else {
180 if w < cap - 1 { out[w] = b[i]; w = w + 1 }
181 i = i + 1
182 }
183 }
184 }
185 }
186 out[w] = 0 as u8
187 return w
188}
189
190// first decimal integer following the literal `key` at or after `from`; -1 when the key never appears.
191// `key` carries its own leading space and trailing '=' so it cannot match a prose word, and the scan
192// starts AFTER the anchored closure_sha= field so the report's own header line is out of reach.
193func oa_num_after(b: *u8, n: i64, from: i64, key: *u8) -> i64 {
194 let kl: i64 = icl_len(key)
195 if kl == 0 { return 0 - 1 }
196 var i: i64 = from
197 var found: i64 = 0 - 1
198 var go: i64 = 1
199 while go == 1 {
200 if i + kl > n { go = 0 } else {
201 var m: i64 = 0
202 var ok: i64 = 1
203 var mg: i64 = 1
204 while mg == 1 {
205 if m >= kl { mg = 0 } else {
206 if b[i + m] != key[m] { ok = 0; mg = 0 }
207 m = m + 1
208 }
209 }
210 if ok == 1 {
211 var p: i64 = i + kl
212 var v: i64 = 0
213 var d: i64 = 0
214 var pg: i64 = 1
215 while pg == 1 {
216 if p >= n { pg = 0 } else {
217 let c: i64 = b[p] as i64
218 if c < OA_ZERO { pg = 0 } else {
219 if c > OA_NINE { pg = 0 } else {
220 v = v * OA_DEC + (c - OA_ZERO)
221 d = d + 1
222 p = p + 1
223 }
224 }
225 }
226 }
227 if d > 0 { found = v }
228 go = 0
229 }
230 i = i + 1
231 }
232 }
233 return found
234}
235
236func oa_state_name(s: i64) -> *u8 {
237 if s == OA_ATTESTED { return "ATTESTED" as *u8 }
238 if s == OA_REFUSED { return "REFUSED" as *u8 }
239 if s == OA_GUARD_ABSENT { return "GUARD-ABSENT" as *u8 }
240 if s == OA_UNOBSERVABLE { return "UNOBSERVABLE" as *u8 }
241 if s == OA_BASELINE_FAIL { return "BASELINE-FAIL" as *u8 }
242 return "SUBJECT-ABSENT" as *u8
243}
244
245func main(argc: i64, argv: *i64) -> i64 {
246 if argc < 2 {
247 oa_err("usage: nx_osattest <compiler_elf> [conf_path] [receipt_path]\n" as *u8)
248 oa_err("REFUSED: no compiler given. This organ has NO default compiler on purpose -- a silent default produces a fully-formed verdict about whatever binary happened to be lying there.\n" as *u8)
249 sys_exit(OA_EXIT_USAGE)
250 return OA_EXIT_USAGE
251 }
252 ccg_anchor_root()
253 let cc: *u8 = argv[1] as *u8
254 var confp: *u8 = "knowledge/lang_attest.conf"
255 if argc >= 3 { confp = argv[2] as *u8 }
256 var rcptp: *u8 = "knowledge/status/lang_ring_attest.receipt"
257 if argc >= 4 { rcptp = argv[3] as *u8 }
258
259 let t0: *i64 = sys_mmap(OA_WORD * 4) as *i64
260 sys_clock_gettime_mono(t0)
261 let wall0: i64 = t0[0]
262 let tr: *i64 = sys_mmap(OA_WORD * 4) as *i64
263 sys_clock_gettime_real(tr)
264
265 // ---- conf, read ONCE through the one line-anchored reader ----
266 let clp: *i64 = sys_mmap(OA_WORD * 2) as *i64
267 let cbuf: *u8 = sys_read_file(confp, clp)
268 if (cbuf as i64) == 0 {
269 oa_err("REFUSED: cannot read conf: " as *u8)
270 oa_err(confp)
271 oa_err("\n" as *u8)
272 sys_exit(OA_EXIT_USAGE)
273 return OA_EXIT_USAGE
274 }
275 let cn: i64 = clp[0]
276 let key: *u8 = sys_mmap(OA_KEYW)
277 let scratch: *u8 = sys_mmap(OA_PW)
278 if lcf_str(cbuf, cn, "scratch_dir" as *u8, scratch, OA_PW) < 0 {
279 oa_err("REFUSED: conf has no scratch_dir row\n" as *u8)
280 sys_exit(OA_EXIT_USAGE)
281 return OA_EXIT_USAGE
282 }
283 sys_mkdir(scratch, CCG_MODE_X)
284 let chelf: *u8 = sys_mmap(OA_PW)
285 lcf_str(cbuf, cn, "closurehash_elf" as *u8, chelf, OA_PW)
286 let chroot: *u8 = sys_mmap(OA_PW)
287 lcf_str(cbuf, cn, "closurehash_root" as *u8, chroot, OA_PW)
288
289 let nclo: i64 = lcf_int(cbuf, cn, "closures" as *u8)
290 let naxe: i64 = lcf_int(cbuf, cn, "axes" as *u8)
291 if nclo == LCF_MISS {
292 oa_err("REFUSED: conf has no closures= count\n" as *u8)
293 sys_exit(OA_EXIT_USAGE)
294 return OA_EXIT_USAGE
295 }
296 if naxe == LCF_MISS {
297 oa_err("REFUSED: conf has no axes= count\n" as *u8)
298 sys_exit(OA_EXIT_USAGE)
299 return OA_EXIT_USAGE
300 }
301 if nclo < 1 {
302 oa_err("REFUSED: closures=0 -- an attestation over the empty set is not an attestation\n" as *u8)
303 sys_exit(OA_EXIT_USAGE)
304 return OA_EXIT_USAGE
305 }
306 if naxe < 1 {
307 oa_err("REFUSED: axes=0 -- an attestation over the empty set is not an attestation\n" as *u8)
308 sys_exit(OA_EXIT_USAGE)
309 return OA_EXIT_USAGE
310 }
311 if nclo > OA_MAXC {
312 oa_err("REFUSED: closures exceeds OA_MAXC -- raise the bound deliberately rather than publish a partial attestation\n" as *u8)
313 sys_exit(OA_EXIT_USAGE)
314 return OA_EXIT_USAGE
315 }
316 if naxe > OA_MAXA {
317 oa_err("REFUSED: axes exceeds OA_MAXA -- raise the bound deliberately rather than publish a partial attestation\n" as *u8)
318 sys_exit(OA_EXIT_USAGE)
319 return OA_EXIT_USAGE
320 }
321
322 // ---- tables, sized from the conf's own counts ----
323 let c_name: *u8 = sys_mmap(nclo * OA_FW)
324 let c_targ: *u8 = sys_mmap(nclo * OA_FW)
325 let c_src: *u8 = sys_mmap(nclo * OA_PW)
326 let c_sha: *u8 = sys_mmap(nclo * OA_SHA_HEXW)
327 let c_files: *i64 = sys_mmap(nclo * OA_WORD) as *i64
328 let c_bytes: *i64 = sys_mmap(nclo * OA_WORD) as *i64
329 let c_unres: *i64 = sys_mmap(nclo * OA_WORD) as *i64
330 let c_state: *i64 = sys_mmap(nclo * OA_WORD) as *i64
331 let c_baserc: *i64 = sys_mmap(nclo * OA_WORD) as *i64
332
333 let a_id: *u8 = sys_mmap(naxe * OA_FW)
334 let a_kind: *u8 = sys_mmap(naxe * OA_FW)
335 let a_flag: *u8 = sys_mmap(naxe * OA_FW)
336 let a_probe: *u8 = sys_mmap(naxe * OA_PW)
337 let a_acc: *u8 = sys_mmap(naxe * OA_PW)
338 let a_rule: *u8 = sys_mmap(naxe * OA_FW)
339 let a_why: *u8 = sys_mmap(naxe * OA_FW)
340 let a_live: *i64 = sys_mmap(naxe * OA_WORD) as *i64
341
342 let cell: *i64 = sys_mmap(nclo * naxe * OA_WORD) as *i64
343 let cellrc: *i64 = sys_mmap(nclo * naxe * OA_WORD) as *i64
344 let cellwhy: *u8 = sys_mmap(nclo * naxe * OA_FW)
345
346 var i: i64 = 0
347 while i < nclo {
348 oa_key(key, "closure" as *u8, i, "_name" as *u8)
349 lcf_str(cbuf, cn, key, oa_slot(c_name, i, OA_FW), OA_FW)
350 oa_key(key, "closure" as *u8, i, "_target" as *u8)
351 lcf_str(cbuf, cn, key, oa_slot(c_targ, i, OA_FW), OA_FW)
352 c_files[i] = 0 - 1
353 c_bytes[i] = 0 - 1
354 c_unres[i] = 0 - 1
355 c_state[i] = OA_SUB_OK
356 c_baserc[i] = 0 - 1
357 i = i + 1
358 }
359 i = 0
360 while i < naxe {
361 oa_key(key, "axis" as *u8, i, "_id" as *u8)
362 lcf_str(cbuf, cn, key, oa_slot(a_id, i, OA_FW), OA_FW)
363 oa_key(key, "axis" as *u8, i, "_kind" as *u8)
364 lcf_str(cbuf, cn, key, oa_slot(a_kind, i, OA_FW), OA_FW)
365 oa_key(key, "axis" as *u8, i, "_flag" as *u8)
366 lcf_str(cbuf, cn, key, oa_slot(a_flag, i, OA_FW), OA_FW)
367 oa_key(key, "axis" as *u8, i, "_probe" as *u8)
368 lcf_str(cbuf, cn, key, oa_slot(a_probe, i, OA_PW), OA_PW)
369 oa_key(key, "axis" as *u8, i, "_accept" as *u8)
370 lcf_str(cbuf, cn, key, oa_slot(a_acc, i, OA_PW), OA_PW)
371 oa_key(key, "axis" as *u8, i, "_rule" as *u8)
372 lcf_str(cbuf, cn, key, oa_slot(a_rule, i, OA_FW), OA_FW)
373 a_live[i] = 0
374 oa_set(oa_slot(a_why, i, OA_FW), "not-yet-probed" as *u8)
375 i = i + 1
376 }
377
378 // ---- the compiler under test: its IDENTITY before any verdict about it ----
379 let ccl: *i64 = sys_mmap(OA_WORD * 2) as *i64
380 let ccb: *u8 = sys_read_file(cc, ccl)
381 if (ccb as i64) == 0 {
382 oa_err("REFUSED: compiler elf unreadable: " as *u8)
383 oa_err(cc)
384 oa_err("\n" as *u8)
385 sys_exit(OA_EXIT_USAGE)
386 return OA_EXIT_USAGE
387 }
388 let cchex: *u8 = sys_mmap(OA_SHA_HEXW)
389 let ccdig: *u8 = sys_mmap(OA_DIG64)
390 sha256_digest(ccb, ccl[0], ccdig)
391 icl_hex(ccdig, cchex)
392 let ccbytes: i64 = ccl[0]
393
394 // ---- scratch paths (per-pid: two concurrent runs racing one path is a documented incident class) --
395 let pid: i64 = ccg_pid()
396 let p_s: *u8 = ccg_path(oa_join(scratch, "/p_" as *u8), pid, ".s" as *u8)
397 let p_e: *u8 = ccg_path(oa_join(scratch, "/p_" as *u8), pid, ".err" as *u8)
398 let q_s: *u8 = ccg_path(oa_join(scratch, "/q_" as *u8), pid, ".s" as *u8)
399 let q_e: *u8 = ccg_path(oa_join(scratch, "/q_" as *u8), pid, ".err" as *u8)
400 let b_s: *u8 = ccg_path(oa_join(scratch, "/base_" as *u8), pid, ".s" as *u8)
401 let b_e: *u8 = ccg_path(oa_join(scratch, "/base_" as *u8), pid, ".err" as *u8)
402 let m_s: *u8 = ccg_path(oa_join(scratch, "/mode_" as *u8), pid, ".s" as *u8)
403 let m_e: *u8 = ccg_path(oa_join(scratch, "/mode_" as *u8), pid, ".err" as *u8)
404 let h_o: *u8 = ccg_path(oa_join(scratch, "/clo_" as *u8), pid, ".txt" as *u8)
405 let h_e: *u8 = ccg_path(oa_join(scratch, "/clo_" as *u8), pid, ".err" as *u8)
406
407 // ---- AXIS LIVENESS: prove each mode ENFORCES on THIS compiler, from both sides ----
408 var live_axes: i64 = 0
409 i = 0
410 while i < naxe {
411 let fl: *u8 = oa_slot(a_flag, i, OA_FW)
412 let pb: *u8 = oa_slot(a_probe, i, OA_PW)
413 let ac: *u8 = oa_slot(a_acc, i, OA_PW)
414 let ru: *u8 = oa_slot(a_rule, i, OA_FW)
415 let kd: *u8 = oa_slot(a_kind, i, OA_FW)
416 let wy: *u8 = oa_slot(a_why, i, OA_FW)
417 if oa_is_undeclared(fl) == 1 {
418 oa_set(wy, "flag-undeclared" as *u8)
419 } else {
420 if icl_exists(pb) == 0 {
421 oa_set(wy, "probe-fixture-absent" as *u8)
422 } else {
423 if icl_seq(kd, "emit" as *u8) == 1 {
424 // emit-kind: the marker must be there by DEFAULT and GONE under the opt-out flag.
425 // Presence alone proves nothing -- only the pair proves the marker tracks the guard.
426 let e1rc: i64 = oa_cc(cc, 0 as *u8, pb, p_s, p_e)
427 var e1: i64 = 0
428 if e1rc == 0 { e1 = ccg_file_has(p_s, ru) }
429 let e2rc: i64 = oa_cc(cc, fl, pb, q_s, q_e)
430 var e2: i64 = 0
431 if e2rc == 0 { if ccg_file_has(q_s, ru) == 0 { e2 = 1 } }
432 if e1 == 0 {
433 oa_set(wy, "guard-marker-absent-by-default" as *u8)
434 } else {
435 if e2 == 0 {
436 oa_set(wy, "optout-flag-not-honoured" as *u8)
437 } else {
438 a_live[i] = 1
439 live_axes = live_axes + 1
440 oa_set(wy, "two-sided-emit-probe-passed" as *u8)
441 }
442 }
443 } else {
444 // refuse-kind, four conditions, each with its OWN named failure so the receipt says
445 // WHICH one did not hold instead of a bare "not live".
446 let l0: i64 = oa_cc(cc, 0 as *u8, pb, p_s, p_e)
447 let l1: i64 = oa_cc(cc, fl, pb, q_s, q_e)
448 var l2: i64 = 0
449 if l1 != 0 { l2 = ccg_file_has(q_e, ru) }
450 var l3: i64 = 0 - 1
451 if icl_exists(ac) == 1 { l3 = oa_cc(cc, fl, ac, p_s, p_e) }
452 if l0 != 0 {
453 oa_set(wy, "probe-fixture-broken-under-default" as *u8)
454 } else {
455 if l1 == 0 {
456 oa_set(wy, "flag-not-enforcing" as *u8)
457 } else {
458 if l2 == 0 {
459 oa_set(wy, "refusal-rule-not-named" as *u8)
460 } else {
461 if l3 != 0 {
462 oa_set(wy, "mode-refuses-everything" as *u8)
463 } else {
464 a_live[i] = 1
465 live_axes = live_axes + 1
466 oa_set(wy, "four-sided-refuse-probe-passed" as *u8)
467 }
468 }
469 }
470 }
471 }
472 }
473 }
474 i = i + 1
475 }
476
477 // ---- SUBJECTS: resolve, take provenance, baseline-build, then measure each live axis ----
478 let nmb: *u8 = sys_mmap(OA_FW)
479 let hlp: *i64 = sys_mmap(OA_WORD * 2) as *i64
480 var ci: i64 = 0
481 while ci < nclo {
482 let tg: *u8 = oa_slot(c_targ, ci, OA_FW)
483 let sp: *u8 = oa_slot(c_src, ci, OA_PW)
484 let shx: *u8 = oa_slot(c_sha, ci, OA_SHA_HEXW)
485 oa_set(shx, "UNAVAILABLE" as *u8)
486 // the target's own source, resolved through the ONE resolver the builder itself uses --
487 // _hdl_build FIRST, then runtime/, then a looped subdirectory sweep. A second resolver that
488 // disagreed by one path would attest the wrong file.
489 var no: i64 = icl_cat(nmb, 0, tg)
490 no = icl_cat(nmb, no, ".nx" as *u8)
491 nmb[no] = 0 as u8
492 if icl_resolve("." as *u8, nmb, sp) == 0 {
493 c_state[ci] = OA_SUB_ABSENT
494 sp[0] = 0 as u8
495 } else {
496 // provenance: fork nx_closurehash. An unavailable ruler ABSTAINS on this leg ONLY -- the
497 // attestation leg is independent and must not go dark because a provenance tool is missing.
498 let hrc: i64 = oa_closurehash(chelf, tg, chroot, h_o, h_e)
499 let hb: *u8 = sys_read_file(h_o, hlp)
500 if (hb as i64) != 0 {
501 let hn: i64 = hlp[0]
502 let off: i64 = lcf_find(hb, hn, "closure_sha" as *u8)
503 if off >= 0 {
504 oa_word_at(hb, hn, off, shx, OA_SHA_HEXW)
505 c_files[ci] = oa_num_after(hb, hn, off, " files=" as *u8)
506 c_unres[ci] = oa_num_after(hb, hn, off, " unresolved=" as *u8)
507 c_bytes[ci] = oa_num_after(hb, hn, off, " bytes=" as *u8)
508 }
509 }
510 if hrc != 0 { oa_set(shx, "UNAVAILABLE" as *u8) }
511 // baseline: the closure must build under the DEFAULT mode, or nothing measured under a mode
512 // is attributable to that mode.
513 let brc: i64 = oa_cc(cc, 0 as *u8, sp, b_s, b_e)
514 c_baserc[ci] = brc
515 if brc != 0 { c_state[ci] = OA_SUB_BASE }
516 }
517 var ai: i64 = 0
518 while ai < naxe {
519 let idx: i64 = ci * naxe + ai
520 let wc: *u8 = oa_slot(cellwhy, idx, OA_FW)
521 cellrc[idx] = 0 - 1
522 if c_state[ci] == OA_SUB_ABSENT {
523 cell[idx] = OA_SUBJECT_ABSENT
524 oa_set(wc, "target-source-does-not-resolve" as *u8)
525 } else {
526 if a_live[ai] == 0 {
527 cell[idx] = OA_UNOBSERVABLE
528 oa_set(wc, oa_slot(a_why, ai, OA_FW))
529 } else {
530 if c_state[ci] == OA_SUB_BASE {
531 cell[idx] = OA_BASELINE_FAIL
532 oa_set(wc, "closure-does-not-build-under-default" as *u8)
533 } else {
534 if icl_seq(oa_slot(a_kind, ai, OA_FW), "emit" as *u8) == 1 {
535 // read out of the BASELINE assembly already produced -- no extra compile.
536 let has: i64 = ccg_file_has(b_s, oa_slot(a_rule, ai, OA_FW))
537 cellrc[idx] = has
538 if has == 1 {
539 cell[idx] = OA_ATTESTED
540 oa_set(wc, "guard-marker-present-in-emitted-assembly" as *u8)
541 } else {
542 cell[idx] = OA_GUARD_ABSENT
543 oa_set(wc, "guard-marker-absent-from-emitted-assembly" as *u8)
544 }
545 } else {
546 let rc2: i64 = oa_cc(cc, oa_slot(a_flag, ai, OA_FW), sp, m_s, m_e)
547 cellrc[idx] = rc2
548 if rc2 == 0 {
549 cell[idx] = OA_ATTESTED
550 oa_set(wc, "closure-compiles-under-the-declared-mode" as *u8)
551 } else {
552 cell[idx] = OA_REFUSED
553 // WHICH rule fired, never merely THAT it was refused: a refusal for an
554 // unrelated reason is a different finding and must not wear the mode's name.
555 if ccg_file_has(m_e, oa_slot(a_rule, ai, OA_FW)) == 1 {
556 oa_set(wc, oa_slot(a_rule, ai, OA_FW))
557 } else {
558 oa_set(wc, "OTHER-ERROR-NOT-THE-DECLARED-RULE" as *u8)
559 }
560 }
561 }
562 }
563 }
564 }
565 ai = ai + 1
566 }
567 ci = ci + 1
568 }
569
570 // ---- receipt: buffer DERIVED from the row count the conf itself declared ----
571 let rows: i64 = nclo + naxe + (nclo * naxe)
572 let rcap: i64 = OA_HDRW + OA_ROWW * rows
573 let rb: *u8 = sys_mmap(rcap)
574 var ro: i64 = 0
575 ro = icl_cat(rb, ro, "# NX-OSATTEST RECEIPT -- NishiOS + Nishi Browser language-ring inheritance attestation (lang LR6)\n" as *u8)
576 ro = icl_cat(rb, ro, "# Chromium Rule of 2: untrustworthy input + an unsafe language + high privilege may never combine.\n" as *u8)
577 ro = icl_cat(rb, ro, "# This receipt is the unsafe-language leg DISCHARGED BY MEASUREMENT rather than asserted: each\n" as *u8)
578 ro = icl_cat(rb, ro, "# closure is rebuilt under each DECLARED safety mode of the named compiler, and each mode is first\n" as *u8)
579 ro = icl_cat(rb, ro, "# proven to ENFORCE on that compiler from both sides. A REFUSED cell is a FINDING, not a failure\n" as *u8)
580 ro = icl_cat(rb, ro, "# of the attester. Regenerate: nx_osattest <compiler_elf> [conf] [receipt]\n" as *u8)
581 ro = icl_cat(rb, ro, "ts=" as *u8); ro = oa_addn(rb, ro, tr[0]); ro = icl_cat(rb, ro, "\n" as *u8)
582 ro = icl_cat(rb, ro, "conf=" as *u8); ro = icl_cat(rb, ro, confp); ro = icl_cat(rb, ro, "\n" as *u8)
583 ro = icl_cat(rb, ro, "compiler=" as *u8); ro = icl_cat(rb, ro, cc); ro = icl_cat(rb, ro, "\n" as *u8)
584 ro = icl_cat(rb, ro, "compiler_sha256=" as *u8); ro = icl_cat(rb, ro, cchex); ro = icl_cat(rb, ro, "\n" as *u8)
585 ro = icl_cat(rb, ro, "compiler_bytes=" as *u8); ro = oa_addn(rb, ro, ccbytes); ro = icl_cat(rb, ro, "\n" as *u8)
586
587 i = 0
588 while i < naxe {
589 ro = icl_cat(rb, ro, "axis=" as *u8); ro = icl_cat(rb, ro, oa_slot(a_id, i, OA_FW))
590 ro = icl_cat(rb, ro, " kind=" as *u8); ro = icl_cat(rb, ro, oa_slot(a_kind, i, OA_FW))
591 ro = icl_cat(rb, ro, " flag=" as *u8); ro = icl_cat(rb, ro, oa_slot(a_flag, i, OA_FW))
592 ro = icl_cat(rb, ro, " state=" as *u8)
593 if a_live[i] == 1 { ro = icl_cat(rb, ro, "LIVE" as *u8) } else { ro = icl_cat(rb, ro, "UNOBSERVABLE" as *u8) }
594 ro = icl_cat(rb, ro, " why=" as *u8); ro = icl_cat(rb, ro, oa_slot(a_why, i, OA_FW))
595 ro = icl_cat(rb, ro, "\n" as *u8)
596 i = i + 1
597 }
598 ci = 0
599 while ci < nclo {
600 ro = icl_cat(rb, ro, "closure=" as *u8); ro = icl_cat(rb, ro, oa_slot(c_name, ci, OA_FW))
601 ro = icl_cat(rb, ro, " target=" as *u8); ro = icl_cat(rb, ro, oa_slot(c_targ, ci, OA_FW))
602 ro = icl_cat(rb, ro, " source=" as *u8)
603 let spx: *u8 = oa_slot(c_src, ci, OA_PW)
604 if spx[0] == (0 as u8) { ro = icl_cat(rb, ro, "UNRESOLVED" as *u8) } else { ro = icl_cat(rb, ro, spx) }
605 ro = icl_cat(rb, ro, " sources=" as *u8); ro = oa_addn(rb, ro, c_files[ci])
606 ro = icl_cat(rb, ro, " bytes=" as *u8); ro = oa_addn(rb, ro, c_bytes[ci])
607 ro = icl_cat(rb, ro, " unresolved=" as *u8); ro = oa_addn(rb, ro, c_unres[ci])
608 ro = icl_cat(rb, ro, " closure_sha=" as *u8); ro = icl_cat(rb, ro, oa_slot(c_sha, ci, OA_SHA_HEXW))
609 ro = icl_cat(rb, ro, " default_build_rc=" as *u8); ro = oa_addn(rb, ro, c_baserc[ci])
610 ro = icl_cat(rb, ro, "\n" as *u8)
611 ci = ci + 1
612 }
613
614 var n_att: i64 = 0
615 var n_ref: i64 = 0
616 var n_gab: i64 = 0
617 var n_uno: i64 = 0
618 var n_bfl: i64 = 0
619 var n_sab: i64 = 0
620 ci = 0
621 while ci < nclo {
622 var aj: i64 = 0
623 while aj < naxe {
624 let idx: i64 = ci * naxe + aj
625 let st: i64 = cell[idx]
626 if st == OA_ATTESTED { n_att = n_att + 1 }
627 if st == OA_REFUSED { n_ref = n_ref + 1 }
628 if st == OA_GUARD_ABSENT { n_gab = n_gab + 1 }
629 if st == OA_UNOBSERVABLE { n_uno = n_uno + 1 }
630 if st == OA_BASELINE_FAIL { n_bfl = n_bfl + 1 }
631 if st == OA_SUBJECT_ABSENT { n_sab = n_sab + 1 }
632 ro = icl_cat(rb, ro, "cell=" as *u8); ro = icl_cat(rb, ro, oa_slot(c_name, ci, OA_FW))
633 ro = icl_cat(rb, ro, "/" as *u8); ro = icl_cat(rb, ro, oa_slot(a_id, aj, OA_FW))
634 ro = icl_cat(rb, ro, " verdict=" as *u8); ro = icl_cat(rb, ro, oa_state_name(st))
635 ro = icl_cat(rb, ro, " rc=" as *u8); ro = oa_addn(rb, ro, cellrc[idx])
636 ro = icl_cat(rb, ro, " reason=" as *u8); ro = icl_cat(rb, ro, oa_slot(cellwhy, idx, OA_FW))
637 ro = icl_cat(rb, ro, "\n" as *u8)
638 aj = aj + 1
639 }
640 ci = ci + 1
641 }
642
643 let pop: i64 = nclo * naxe
644 let sum: i64 = n_att + n_ref + n_gab + n_uno + n_bfl + n_sab
645 ro = icl_cat(rb, ro, "population=" as *u8); ro = oa_addn(rb, ro, pop)
646 ro = icl_cat(rb, ro, " closures=" as *u8); ro = oa_addn(rb, ro, nclo)
647 ro = icl_cat(rb, ro, " axes=" as *u8); ro = oa_addn(rb, ro, naxe)
648 ro = icl_cat(rb, ro, "\n" as *u8)
649 ro = icl_cat(rb, ro, "partition attested=" as *u8); ro = oa_addn(rb, ro, n_att)
650 ro = icl_cat(rb, ro, " refused=" as *u8); ro = oa_addn(rb, ro, n_ref)
651 ro = icl_cat(rb, ro, " guard_absent=" as *u8); ro = oa_addn(rb, ro, n_gab)
652 ro = icl_cat(rb, ro, " unobservable=" as *u8); ro = oa_addn(rb, ro, n_uno)
653 ro = icl_cat(rb, ro, " baseline_fail=" as *u8); ro = oa_addn(rb, ro, n_bfl)
654 ro = icl_cat(rb, ro, " subject_absent=" as *u8); ro = oa_addn(rb, ro, n_sab)
655 ro = icl_cat(rb, ro, " sum=" as *u8); ro = oa_addn(rb, ro, sum)
656 ro = icl_cat(rb, ro, " reconciles=" as *u8)
657 if sum == pop { ro = icl_cat(rb, ro, "1" as *u8) } else { ro = icl_cat(rb, ro, "0" as *u8) }
658 ro = icl_cat(rb, ro, "\n" as *u8)
659 // THE DENOMINATOR TRAVELS WITH THE SCORE. Dropping an axis that cannot be seen would inflate the
660 // headline; naming it keeps the safest verdict from becoming the most flattering one.
661 ro = icl_cat(rb, ro, "modes_live=" as *u8); ro = oa_addn(rb, ro, live_axes)
662 ro = icl_cat(rb, ro, " of=" as *u8); ro = oa_addn(rb, ro, naxe)
663 ro = icl_cat(rb, ro, " unobservable_modes=" as *u8)
664 var first: i64 = 1
665 i = 0
666 while i < naxe {
667 if a_live[i] == 0 {
668 if first == 0 { ro = icl_cat(rb, ro, "," as *u8) }
669 ro = icl_cat(rb, ro, oa_slot(a_id, i, OA_FW))
670 first = 0
671 }
672 i = i + 1
673 }
674 if first == 1 { ro = icl_cat(rb, ro, "none" as *u8) }
675 ro = icl_cat(rb, ro, "\n" as *u8)
676
677 sys_clock_gettime_mono(t0)
678 ro = icl_cat(rb, ro, "elapsed_s=" as *u8); ro = oa_addn(rb, ro, t0[0] - wall0)
679 ro = icl_cat(rb, ro, " receipt_cap=" as *u8); ro = oa_addn(rb, ro, rcap)
680 ro = icl_cat(rb, ro, " receipt_used_before_footer=" as *u8); ro = oa_addn(rb, ro, ro)
681 ro = icl_cat(rb, ro, "\n" as *u8)
682
683 // Exit code, most-serious last-wins: a subject we could not even find outranks one that would not build.
684 var rc: i64 = OA_EXIT_OK
685 if n_bfl > 0 { rc = OA_EXIT_BASELINE }
686 if n_sab > 0 { rc = OA_EXIT_UNPROVEN }
687 if ro > rcap - OA_ROWW {
688 rc = OA_EXIT_BASELINE
689 ro = icl_cat(rb, ro, "TRUNCATED=1 -- the receipt buffer filled; this attestation is PARTIAL and must not be cited\n" as *u8)
690 }
691 // canonical LAST line, positionally anchored -- gv_last_line and every downstream reader take the
692 // final line, so nothing above it can be mistaken for the verdict.
693 ro = icl_cat(rb, ro, "NX-OSATTEST verdict=" as *u8)
694 if rc == OA_EXIT_OK { ro = icl_cat(rb, ro, "GREEN" as *u8) } else { ro = icl_cat(rb, ro, "RED" as *u8) }
695 ro = icl_cat(rb, ro, " attested=" as *u8); ro = oa_addn(rb, ro, n_att)
696 ro = icl_cat(rb, ro, "/" as *u8); ro = oa_addn(rb, ro, pop)
697 ro = icl_cat(rb, ro, " modes=" as *u8); ro = oa_addn(rb, ro, live_axes)
698 ro = icl_cat(rb, ro, "/" as *u8); ro = oa_addn(rb, ro, naxe)
699 ro = icl_cat(rb, ro, " rc=" as *u8); ro = oa_addn(rb, ro, rc)
700 ro = icl_cat(rb, ro, "\n" as *u8)
701
702 // ONE truncate-write, then the same bytes to stdout: the file and the console cannot disagree.
703 let fd: i64 = sys_openat_wr(rcptp, CCG_MODE_RW)
704 if fd >= 0 { sys_write(fd, rb, ro); sys_close(fd) }
705 sys_write(1, rb, ro)
706 sys_exit(rc)
707 return rc
708}