code wiki / (root) / nx_osattest.nx

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}