nx_autofix_lib_t138.nx source
↩ module page · 1135 lines · 55142 B
1// nx_autofix_lib.nx -- THE SHARED RULERS OF THE AUTONOMOUS FIX LOOP (autodev M0, 2026-08-27).
2//
3// WHY A LIB: nx_autofix_auto (the loop), nx_autofix_intake_gate (the intake) and nx_swebench_local_gate
4// (the judge) each carried their own copy of "find the failing FNRES row", their own ledger path
5// literal (/home/elderwesto/nx_stage/...) and their own idea of what a proposal row means. Three
6// organs that must agree cannot be kept agreeing by discipline; only one copy holds. This is the
7// one copy, and the contract symbols on the /compare/autodev board (af_episode_ledger,
8// af_sandbox_root, af_null_control, af_admit_untrusted, af_intake_board) are thin composers over it in
9// nx_autofix_auto.nx -- the loop's call sites -- while the gates drive these rulers in-process.
10//
11// THE FIVE RULERS
12// AD5 EPISODE LEDGER afl_ledger_* -- one row per episode, appended AS DECIDED to an estate-relative
13// journal (knowledge/status/autofix_ledger.jrnl) so a run interrupted at 95 percent
14// keeps 95 percent, and the page reads the plane and nothing else. The dev-box
15// stage ledger stays the fallback (nx_stage_path), never a literal.
16// AD7 SANDBOX ROOT afl_sandbox_* -- every candidate builds and runs in /tmp/afx_sb_<ts>_<name>/
17// with its import closure COPIED (not linked: a write through a link would reach
18// the tree), the toolchain linked read-only, a real _build/, RLIMIT_CPU and
19// RLIMIT_AS on the child, a wall deadline that kills the whole process group.
20// The tree copy is never opened for write; afl_bytes_eq is the witness.
21// AD4 NULL CONTROLS afl_null_controls -- the empty patch and a replay of a prior solution are run
22// through the SAME judge; a null that scores GREEN is an oracle defect and the
23// batch is REFUSED, never published. Echo is rejected pre-verify by byte identity
24// (structural) and the revert patch is the empty patch by construction; both are
25// named on the line rather than counted twice.
26// AD6 UNTRUSTED INPUT afl_admit_row -- deny by default: a proposal row is LOCAL (built and run in the
27// sandbox) or EXTERNAL DATA-ONLY (provenance-pinned h<sha> AND maintainer-merged,
28// never executed); an external row missing either is refused BY NAME.
29// AD3 INTAKE PLANE afl_intake_emit -- folds the estate's own boards into one task plane: roster
30// RED gates, the adjudicated drift worklist, the magic ratchet counts, the unwired
31// baseline; every task carries its oracle and a RED-before receipt; the partition
32// prints its sum; debt rows carry no executable oracle and are REFUSED as
33// UNREPRODUCED (counted, never hidden); a source that is absent says ABSENT-SOURCE.
34//
35// CONF (rule 17: env > conf > bootstrap): knowledge/autofix.conf rows sandbox_wall_ms, sandbox_as_bytes,
36// bon_n, bon_temp_pm, bon_topp_pm, bon_topk, null_controls_max_green. The defaults below are the July
37// contract values with the reason each was chosen written beside it.
38// license_tier: ORIGINAL No hw writes (Rule 26).
39import "nx_syscalls.nx"
40import "nx_estate_path.nx"
41import "nx_stage_path.nx"
42import "nx_tool_run.nx"
43import "nx_build_canon_input_lib.nx"
44
45const AFL_PATH: i64 = 4096 // Linux PATH_MAX, the same bound nx_stage_path uses
46const AFL_LINE: i64 = 2048
47const AFL_NAME: i64 = 64
48const AFL_HARNESS: i64 = 128
49const AFL_MODE_0644: i64 = 420
50const AFL_MODE_0755: i64 = 493
51const AFL_ABSENT: i64 = 0 - 999999
52const AFL_CAPTURE: i64 = 1048576 // one grader capture; the July candidates print under 1 KB, the bound announces when it binds
53const AFL_LEDGER_JRNL: *u8 = "knowledge/status/autofix_ledger.jrnl"
54const AFL_SWBL_JRNL: *u8 = "knowledge/status/swebench_local.jrnl"
55const AFL_INTAKE_JRNL: *u8 = "knowledge/status/autodev_intake.jrnl"
56// harness.jrnl is written by every gate's gv_journal, so it exists wherever the estate's status dir is;
57// its directory is therefore the write root for the ledgers above, resolved once and never spelled twice.
58const AFL_STATUS_PROBE: *u8 = "knowledge/status/harness.jrnl"
59const AFL_LEDGER_STAGE: *u8 = "autofix_ledger.log"
60const AFL_SWBL_STAGE: *u8 = "swebench_local_ledger.log"
61const AFL_CLUSTER_MAX: i64 = 256 // distinct located functions in one batch; a batch above this reads UNMEASURED, announced
62const AFL_CONF: *u8 = "knowledge/autofix.conf"
63const AFL_KEY_WALL: *u8 = "sandbox_wall_ms="
64const AFL_KEY_AS: *u8 = "sandbox_as_bytes="
65const AFL_KEY_BON_N: *u8 = "bon_n="
66const AFL_KEY_BON_TEMP: *u8 = "bon_temp_pm="
67const AFL_KEY_BON_TOPP: *u8 = "bon_topp_pm="
68const AFL_KEY_BON_TOPK: *u8 = "bon_topk="
69const AFL_KEY_NULL_MAX: *u8 = "null_controls_max_green="
70// 5 minutes for the WHOLE candidate (build + run): the July episodes build and run in single seconds;
71// nx_sov_build_run's own per-step deadline is 900000 ms, so this is one third of one of its steps.
72const AFL_WALL_MS_DEFAULT: i64 = 300000
73// 4 GiB of address space per sandboxed process: above any measured sovereign-compiler peak (the largest
74// closure .s on record is nx_browser at 9.2 MB) and one eighth of the 36 GB host, so a runaway candidate
75// cannot take the box with it. A bound that must be guessed is announced on every run (afl_sandbox_run).
76const AFL_AS_BYTES_DEFAULT: i64 = 4294967296
77const AFL_BON_N_DEFAULT: i64 = 3 // the A1 best-of-N precedent
78const AFL_BON_TEMP_DEFAULT: i64 = 800 // permil temperature, the A1 precedent
79const AFL_BON_TOPP_DEFAULT: i64 = 950 // permil nucleus mass
80const AFL_BON_TOPK_DEFAULT: i64 = 40 // top-k cutoff (the nsv sampler caps at 256)
81const AFL_NULL_MAX_DEFAULT: i64 = 0 // ANY null control scoring GREEN refuses the batch
82const AFL_POLL_MS: i64 = 100 // wait4(WNOHANG) cadence, the same the build runner polls its children at
83const AFL_SIGKILL: i64 = 9
84const AFL_MS_PER_S: i64 = 1000
85const AFL_CLOSURE_MAX: i64 = 64 // import closure files copied into a sandbox; announces when it binds
86// THE SANDBOX LIVES ON AN EXEC-CAPABLE FILESYSTEM (measured 2026-08-27 on the NAS: /tmp is mounted
87// noexec -- a candidate COMPILED there and its exec returned 127 while the toolchain, symlinked to
88// /volume1, ran fine; the laptop's /tmp execs, which is why the same gate read GREEN there). The build
89// tree's own _build/ scratch is exec-capable BY CONSTRUCTION -- every gate the estate runs execs from
90// it -- so the sandbox roots there, resolved by the same probe the build runner anchors with
91// (runtime/nx_syscalls.nx identifies the real tree); /tmp stays the last fallback for a hermetic host.
92const AFL_SB_NAME: *u8 = "afx_sb_"
93const AFL_SB_TMP: *u8 = "/tmp/"
94const AFL_TREE_PROBE: *u8 = "runtime/nx_syscalls.nx"
95const AFL_RLIM_BYTES: i64 = 16 // struct rlimit64 { cur, max }
96const AFL_EXIT_EXEC_FAIL: i64 = 127
97const AFL_EXIT_CHDIR_FAIL: i64 = 126
98const AFL_BUILDER_REL: *u8 = "_offc/nx_sov_build_run.elf"
99const AFL_CC_REL: *u8 = "_offc/nx_cc_sovereign.elf"
100const AFL_ASM_REL: *u8 = "_offc/nxasm_x86_main.elf"
101const AFL_ADMIT_REL: *u8 = "_build/nx_build_admit.sov.elf"
102// admission verdicts for a proposal row (AD6)
103const AFL_ADMIT_LOCAL: i64 = 1
104const AFL_ADMIT_DATA_ONLY: i64 = 2
105const AFL_REFUSED_UNPINNED: i64 = 0 - 1
106const AFL_REFUSED_UNMERGED: i64 = 0 - 2
107const AFL_REFUSED_MALFORMED: i64 = 0 - 3
108// intake sources (AD3)
109const AFL_ROSTER_JRNL: *u8 = "knowledge/status/gateroster.jrnl"
110const AFL_DRIFT_WORKLIST: *u8 = "knowledge/status/adoption_worklist_laneB.txt"
111const AFL_DRIFT_LOG: *u8 = "knowledge/status/artifactdrift.log"
112const AFL_MAGIC_DIR: *u8 = "knowledge/status/magicbase"
113const AFL_UNWIRED_BASE: *u8 = "nishi-ops/unwired.baseline"
114const AFL_DEBT_ELF: *u8 = "./nx_debt.elf"
115const AFL_ROSTER_MAX: i64 = 4096 // distinct gate names in the roster journal (633 today); announces when it binds
116const AFL_DIRBUF: i64 = 65536
117const AFL_DEBT_TMO_MS: i64 = 60000 // the board's sev verb answers in seconds; a minute is the announce point
118
119func afl_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
120func afl_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p }
121func afl_catn(d: *u8, o: i64, v: i64) -> i64 {
122 var m: i64 = v
123 var p: i64 = o
124 if m < 0 { d[p] = 45 as u8; p = p + 1; m = 0 - m }
125 let t: *u8 = sys_mmap(32)
126 var k: i64 = 0
127 if m == 0 { t[0] = 48 as u8; k = 1 }
128 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
129 var i: i64 = 0
130 while i < k { d[p] = t[k - 1 - i]; p = p + 1; i = i + 1 }
131 d[p] = 0 as u8
132 sys_munmap(t, 32)
133 return p
134}
135func afl_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] == b[i] { if a[i] == (0 as u8) { return 1 } i = i + 1 } return 0 }
136func afl_starts(s: *u8, p: *u8) -> i64 { var i: i64 = 0; while p[i] != (0 as u8) { if s[i] != p[i] { return 0 } i = i + 1 } return 1 }
137func afl_find(hay: *u8, hn: i64, needle: *u8, from: i64) -> i64 {
138 let m: i64 = afl_slen(needle)
139 if m == 0 { return 0 - 1 }
140 var i: i64 = from
141 while i + m <= hn {
142 var j: i64 = 0
143 var ok: i64 = 1
144 while j < m { if hay[i+j] != needle[j] { ok = 0; j = m } else { j = j + 1 } }
145 if ok == 1 { return i }
146 i = i + 1
147 }
148 return 0 - 1
149}
150// end of the line that starts at p (index of the newline or n)
151func afl_eol(buf: *u8, n: i64, p: i64) -> i64 {
152 var e: i64 = p
153 var go: i64 = 1
154 while go == 1 { if e >= n { go = 0 } else { if buf[e] == (10 as u8) { go = 0 } else { e = e + 1 } } }
155 return e
156}
157// token after key within [ls,le): up to space / newline / '|', copied into dst (cap). returns length.
158func afl_tok(buf: *u8, ls: i64, le: i64, key: *u8, dst: *u8, cap: i64) -> i64 {
159 dst[0] = 0 as u8
160 let ki: i64 = afl_find(buf, le, key, ls)
161 if ki < 0 { return 0 }
162 var i: i64 = ki + afl_slen(key)
163 var j: i64 = 0
164 var go: i64 = 1
165 while go == 1 {
166 if i >= le { go = 0 } else {
167 let c: i64 = buf[i] as i64
168 if c == 32 { go = 0 } else { if c == 10 { go = 0 } else { if c == 124 { go = 0 } else { if c == 13 { go = 0 } else {
169 if j >= cap - 1 { go = 0 } else { dst[j] = buf[i]; j = j + 1; i = i + 1 }
170 } } } }
171 }
172 }
173 dst[j] = 0 as u8
174 return j
175}
176// leading unsigned integer at buf[p..n); AFL_ABSENT when there is no digit there
177func afl_uint_at(buf: *u8, p: i64, n: i64) -> i64 {
178 var i: i64 = p
179 var v: i64 = 0
180 var got: i64 = 0
181 var go: i64 = 1
182 while go == 1 {
183 if i >= n { go = 0 } else {
184 let c: i64 = buf[i] as i64
185 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); got = 1; i = i + 1 } else { go = 0 } } else { go = 0 }
186 }
187 }
188 if got == 0 { return AFL_ABSENT }
189 return v
190}
191// integer after key within [ls,le); AFL_ABSENT when the key or the digits are missing
192func afl_int(buf: *u8, ls: i64, le: i64, key: *u8) -> i64 {
193 let ki: i64 = afl_find(buf, le, key, ls)
194 if ki < 0 { return AFL_ABSENT }
195 var p: i64 = ki + afl_slen(key)
196 var neg: i64 = 0
197 if p < le { if buf[p] == (45 as u8) { neg = 1; p = p + 1 } }
198 var v: i64 = 0
199 var got: i64 = 0
200 var go: i64 = 1
201 while go == 1 {
202 if p >= le { go = 0 } else {
203 let c: i64 = buf[p] as i64
204 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); got = 1; p = p + 1 } else { go = 0 } } else { go = 0 }
205 }
206 }
207 if got == 0 { return AFL_ABSENT }
208 if neg == 1 { return 0 - v }
209 return v
210}
211// conf row: PRESENT wins even at 0 (null_controls_max_green=0 is a real value), absent -> dflt
212func afl_conf_int(key: *u8, dflt: i64) -> i64 {
213 let b: *u8 = sys_mmap(AFL_PATH)
214 var v: i64 = dflt
215 if sp_conf_in(AFL_CONF, key, b) > 0 { v = sp_atoi(b) }
216 sys_munmap(b, AFL_PATH)
217 return v
218}
219func afl_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
220func afl_copyfile(srcp: *u8, dstp: *u8) -> i64 {
221 let lb: *i64 = sys_mmap(8) as *i64
222 let buf: *u8 = sys_read_file(srcp, lb)
223 if (buf as i64) == 0 { return 0 - 1 }
224 let fd: i64 = sys_openat_wr(dstp, AFL_MODE_0644)
225 if fd < 0 { return 0 - 2 }
226 sys_write(fd, buf, lb[0])
227 sys_close(fd)
228 return lb[0]
229}
230// byte-equality of two files (the AD7 witness: the tree copy after an episode == the tree copy before)
231func afl_bytes_eq(a: *u8, b: *u8) -> i64 {
232 let la: *i64 = sys_mmap(8) as *i64
233 let lb: *i64 = sys_mmap(8) as *i64
234 let ba: *u8 = sys_read_file(a, la)
235 let bb: *u8 = sys_read_file(b, lb)
236 if (ba as i64) == 0 { return 0 }
237 if (bb as i64) == 0 { return 0 }
238 if la[0] != lb[0] { return 0 }
239 var i: i64 = 0
240 while i < la[0] { if ba[i] != bb[i] { return 0 } i = i + 1 }
241 return 1
242}
243func afl_append(path: *u8, buf: *u8, n: i64) -> i64 {
244 let fd: i64 = sys_openat_append(path, AFL_MODE_0644)
245 if fd < 0 { return 0 - 1 }
246 let w: i64 = sys_write(fd, buf, n)
247 sys_close(fd)
248 return w
249}
250// replace the first occurrence of `finds` in the file at `path` with `repls`; returns new size, <0 on miss
251func afl_apply_fix(path: *u8, finds: *u8, repls: *u8) -> i64 {
252 let lb: *i64 = sys_mmap(8) as *i64
253 let src: *u8 = sys_read_file(path, lb)
254 if (src as i64) == 0 { return 0 - 1 }
255 let n: i64 = lb[0]
256 let fl: i64 = afl_slen(finds)
257 let idx: i64 = afl_find(src, n, finds, 0)
258 if idx < 0 { return 0 - 2 }
259 let rl: i64 = afl_slen(repls)
260 let out: *u8 = sys_mmap(n + rl + 1)
261 var o: i64 = 0
262 var i: i64 = 0
263 while i < idx { out[o] = src[i]; o = o + 1; i = i + 1 }
264 var j: i64 = 0
265 while j < rl { out[o] = repls[j]; o = o + 1; j = j + 1 }
266 i = idx + fl
267 while i < n { out[o] = src[i]; o = o + 1; i = i + 1 }
268 let fd: i64 = sys_openat_wr(path, AFL_MODE_0644)
269 if fd < 0 { return 0 - 3 }
270 sys_write(fd, out, o)
271 sys_close(fd)
272 return o
273}
274
275// ---- the judge's own rows: FNRES <name> <passed> <total> ----------------------------------------
276// FIRST failing row -> 1 and its name (clamped to AFL_NAME-1; fields located from the TRUE end of the
277// name, never the clamped length -- the offset-desync class, debt 1785521219); 0 all pass; -1 no rows.
278func afl_fn_failing(out: *u8, n: i64, namebuf: *u8) -> i64 {
279 var any: i64 = 0
280 var p: i64 = 0
281 var res: i64 = 0
282 var done: i64 = 0
283 namebuf[0] = 0 as u8
284 while done == 0 {
285 let r: i64 = afl_find(out, n, "FNRES " as *u8, p)
286 if r < 0 { done = 1 } else {
287 any = 1
288 var i: i64 = r + 6
289 var nl: i64 = 0
290 var nend: i64 = n
291 var sc: i64 = 1
292 while sc == 1 { if i >= n { sc = 0 } else { if out[i] == (32 as u8) { nend = i; sc = 0 } else { if nl < AFL_NAME - 1 { namebuf[nl] = out[i]; nl = nl + 1 } i = i + 1 } } }
293 namebuf[nl] = 0 as u8
294 let e: i64 = afl_eol(out, n, r)
295 let passed: i64 = afl_int(out, nend, e, " " as *u8)
296 var q: i64 = nend + 1
297 var pd: i64 = 1
298 while pd == 1 { if q >= e { pd = 0 } else { let c: i64 = out[q] as i64; var d: i64 = 0; if c >= 48 { if c <= 57 { d = 1 } } if d == 1 { q = q + 1 } else { pd = 0 } } }
299 let total: i64 = afl_int(out, q, e, " " as *u8)
300 if passed != AFL_ABSENT { if total != AFL_ABSENT { if passed < total { res = 1; done = 1 } } }
301 if done == 0 { namebuf[0] = 0 as u8 }
302 p = r + 6
303 }
304 }
305 if res == 1 { return 1 }
306 if any == 1 { return 0 }
307 return 0 - 1
308}
309
310// ---- AD5: the episode ledger ----------------------------------------------------------------------
311// the estate's status directory (dirname of knowledge/status/harness.jrnl wherever ep_ resolves it),
312// with a trailing slash; 0 when no estate status dir is reachable from this host.
313func afl_status_dir(out: *u8) -> i64 {
314 let p: *u8 = sys_mmap(AFL_PATH)
315 if ep_artifact_path(p, AFL_STATUS_PROBE) == 0 { out[0] = 0 as u8; sys_munmap(p, AFL_PATH); return 0 }
316 var n: i64 = afl_slen(p)
317 while n > 0 { if p[n-1] == (47 as u8) { n = 0 - n } else { n = n - 1 } }
318 n = 0 - n
319 var i: i64 = 0
320 while i < n { out[i] = p[i]; i = i + 1 }
321 out[n] = 0 as u8
322 sys_munmap(p, AFL_PATH)
323 return 1
324}
325// where to READ the fix-loop ledger: 1 = estate journal, 2 = the dev-box stage ledger, 0 = neither exists
326func afl_ledger_read_path(out: *u8) -> i64 {
327 if ep_artifact_path(out, AFL_LEDGER_JRNL) == 1 { return 1 }
328 sp_path(AFL_LEDGER_STAGE, out)
329 if afl_exists(out) == 1 { return 2 }
330 out[0] = 0 as u8
331 return 0
332}
333// where to WRITE an episode row: the estate status dir when reachable, else the stage dir (never a literal)
334func afl_ledger_write_path(out: *u8) -> i64 {
335 if afl_status_dir(out) == 1 { afl_cat(out, afl_slen(out), "autofix_ledger.jrnl" as *u8); return 1 }
336 sp_path(AFL_LEDGER_STAGE, out)
337 return 2
338}
339func afl_swbl_write_path(out: *u8) -> i64 {
340 if afl_status_dir(out) == 1 { afl_cat(out, afl_slen(out), "swebench_local.jrnl" as *u8); return 1 }
341 sp_path(AFL_SWBL_STAGE, out)
342 return 2
343}
344// one episode row, appended as decided; returns bytes written (<0 = the ledger could not be opened)
345func afl_ledger_append(row: *u8, n: i64, where: *u8) -> i64 {
346 let p: *u8 = sys_mmap(AFL_PATH)
347 afl_ledger_write_path(p)
348 var i: i64 = 0
349 while p[i] != (0 as u8) { where[i] = p[i]; i = i + 1 }
350 where[i] = 0 as u8
351 let w: i64 = afl_append(p, row, n)
352 sys_munmap(p, AFL_PATH)
353 return w
354}
355// newest AUTOFIX-AUTO batch of a ledger: st[0]=maxts st[1]=total st[2]=resolved; clusters by located= in
356// ck/cn (successes/outcomes per distinct located name, names at stride AFL_NAME); harness copied from the
357// first row that carries harness= (else empty). Returns the cluster count (0 when the batch is empty).
358func afl_batch_stats(buf: *u8, n: i64, st: *i64, ck: *i64, cn: *i64, names: *u8, harness: *u8) -> i64 {
359 st[0] = 0; st[1] = 0; st[2] = 0
360 harness[0] = 0 as u8
361 var nc: i64 = 0
362 var p: i64 = 0
363 while p < n {
364 let e: i64 = afl_eol(buf, n, p)
365 if afl_starts(((buf as i64) + p) as *u8, "AUTOFIX-AUTO " as *u8) == 1 {
366 let t: i64 = afl_int(buf, p, e, "ts=" as *u8)
367 if t != AFL_ABSENT { if t > st[0] { st[0] = t } }
368 }
369 p = e + 1
370 }
371 if st[0] == 0 { return 0 }
372 let nm: *u8 = sys_mmap(AFL_NAME)
373 p = 0
374 while p < n {
375 let e2: i64 = afl_eol(buf, n, p)
376 if afl_starts(((buf as i64) + p) as *u8, "AUTOFIX-AUTO " as *u8) == 1 {
377 let t2: i64 = afl_int(buf, p, e2, "ts=" as *u8)
378 if t2 == st[0] {
379 st[1] = st[1] + 1
380 var green: i64 = 0
381 if afl_find(buf, e2, " maker=GREEN" as *u8, p) >= 0 { green = 1 }
382 if green == 1 { st[2] = st[2] + 1 }
383 afl_tok(buf, p, e2, "located=" as *u8, nm, AFL_NAME)
384 if nm[0] == (0 as u8) { afl_cat(nm, 0, "unlocated" as *u8) }
385 var ci: i64 = 0 - 1
386 var c: i64 = 0
387 while c < nc { if afl_streq(((names as i64) + c * AFL_NAME) as *u8, nm) == 1 { ci = c; c = nc } else { c = c + 1 } }
388 if ci < 0 { if nc < AFL_CLUSTER_MAX { afl_cat(((names as i64) + nc * AFL_NAME) as *u8, 0, nm); ck[nc] = 0; cn[nc] = 0; ci = nc; nc = nc + 1 } }
389 if ci >= 0 { cn[ci] = cn[ci] + 1; ck[ci] = ck[ci] + green }
390 if harness[0] == (0 as u8) { afl_tok(buf, p, e2, "harness=" as *u8, harness, AFL_HARNESS) }
391 }
392 }
393 p = e2 + 1
394 }
395 sys_munmap(nm, AFL_NAME)
396 return nc
397}
398
399// ---- AD6: untrusted-input admission ----------------------------------------------------------------
400// row grammar: name|path[|origin=local|external][|prov=h<hex>][|merged=yes|no]. Returns the verdict and
401// fills name/path/prov. LOCAL rows are built and run in the sandbox; EXTERNAL rows are admitted as DATA
402// ONLY when pinned and merged, and are NEVER executed; anything else is refused BY NAME.
403func afl_admit_row(row: *u8, rl: i64, name: *u8, path: *u8, prov: *u8) -> i64 {
404 name[0] = 0 as u8; path[0] = 0 as u8; prov[0] = 0 as u8
405 var pipe1: i64 = 0 - 1
406 var i: i64 = 0
407 while i < rl { if row[i] == (124 as u8) { pipe1 = i; i = rl } else { i = i + 1 } }
408 if pipe1 <= 0 { return AFL_REFUSED_MALFORMED }
409 var nl: i64 = 0
410 i = 0
411 while i < pipe1 { if nl < AFL_NAME - 1 { name[nl] = row[i]; nl = nl + 1 } i = i + 1 }
412 name[nl] = 0 as u8
413 var pe: i64 = pipe1 + 1
414 var pl: i64 = 0
415 while pe < rl { if row[pe] == (124 as u8) { pe = rl + 1 } else { if pl < AFL_PATH - 1 { path[pl] = row[pe]; pl = pl + 1 } pe = pe + 1 } }
416 path[pl] = 0 as u8
417 if pl == 0 { return AFL_REFUSED_MALFORMED }
418 let origin: *u8 = sys_mmap(AFL_NAME)
419 let merged: *u8 = sys_mmap(AFL_NAME)
420 afl_tok(row, 0, rl, "origin=" as *u8, origin, AFL_NAME)
421 afl_tok(row, 0, rl, "prov=" as *u8, prov, AFL_HARNESS)
422 afl_tok(row, 0, rl, "merged=" as *u8, merged, AFL_NAME)
423 var ext: i64 = 0
424 if afl_streq(origin, "external" as *u8) == 1 { ext = 1 }
425 sys_munmap(origin, AFL_NAME)
426 if ext == 0 { sys_munmap(merged, AFL_NAME); return AFL_ADMIT_LOCAL }
427 var pinned: i64 = 0
428 if prov[0] == (104 as u8) { if afl_slen(prov) >= 17 { pinned = 1 } }
429 if pinned == 0 { sys_munmap(merged, AFL_NAME); return AFL_REFUSED_UNPINNED }
430 if afl_streq(merged, "yes" as *u8) == 0 { sys_munmap(merged, AFL_NAME); return AFL_REFUSED_UNMERGED }
431 sys_munmap(merged, AFL_NAME)
432 return AFL_ADMIT_DATA_ONLY
433}
434func afl_admit_name(v: i64) -> *u8 {
435 if v == AFL_ADMIT_LOCAL { return "ADMITTED-LOCAL-RUN" as *u8 }
436 if v == AFL_ADMIT_DATA_ONLY { return "ADMITTED-EXTERNAL-DATA-ONLY(never executed)" as *u8 }
437 if v == AFL_REFUSED_UNPINNED { return "REFUSED-EXTERNAL-UNPINNED(no prov=h<sha>)" as *u8 }
438 if v == AFL_REFUSED_UNMERGED { return "REFUSED-EXTERNAL-UNMERGED(merged=yes required)" as *u8 }
439 return "REFUSED-MALFORMED(name|path expected)" as *u8
440}
441
442// ---- AD7: the sandbox root ------------------------------------------------------------------------
443// absolute path of a toolchain file: <cwd>/<rel> when it opens there, else <buildroot>/<rel>, else <root>/<rel>
444func afl_tool_abs(rel: *u8, out: *u8) -> i64 {
445 let cwd: *u8 = sys_mmap(AFL_PATH)
446 if sys_getcwd(cwd, AFL_PATH) > 0 {
447 var o: i64 = afl_cat(out, 0, cwd)
448 if o > 0 { if out[o-1] != (47 as u8) { out[o] = 47 as u8; o = o + 1 } }
449 afl_cat(out, o, rel)
450 if afl_exists(out) == 1 { sys_munmap(cwd, AFL_PATH); return 1 }
451 }
452 sys_munmap(cwd, AFL_PATH)
453 ep_join(out, EP_BUILDROOT, rel)
454 if afl_exists(out) == 1 { return 1 }
455 ep_join(out, EP_ROOT, rel)
456 if afl_exists(out) == 1 { return 1 }
457 out[0] = 0 as u8
458 return 0
459}
460// the directory sandboxes are created under, with a trailing slash. 1=cwd tree _build/, 2=buildroot
461// _build/ (absolute), 3=/tmp (no tree resolves -- exec may fail there and the capture will say so).
462func afl_sandbox_root_dir(out: *u8) -> i64 {
463 if afl_exists(AFL_TREE_PROBE) == 1 {
464 let cwd: *u8 = sys_mmap(AFL_PATH)
465 if sys_getcwd(cwd, AFL_PATH) > 0 {
466 var o: i64 = afl_cat(out, 0, cwd)
467 if o > 0 { if out[o-1] != (47 as u8) { out[o] = 47 as u8; o = o + 1; out[o] = 0 as u8 } }
468 afl_cat(out, afl_slen(out), "_build/" as *u8)
469 sys_munmap(cwd, AFL_PATH)
470 return 1
471 }
472 sys_munmap(cwd, AFL_PATH)
473 }
474 let b: *u8 = sys_mmap(AFL_PATH)
475 ep_join(b, EP_BUILDROOT, AFL_TREE_PROBE)
476 if afl_exists(b) == 1 {
477 var o2: i64 = afl_cat(out, 0, EP_BUILDROOT)
478 afl_cat(out, o2, "_build/" as *u8)
479 sys_munmap(b, AFL_PATH)
480 return 2
481 }
482 sys_munmap(b, AFL_PATH)
483 afl_cat(out, 0, AFL_SB_TMP)
484 return 3
485}
486
487
488func afl_run_examined(killed: i64, rc: i64, fnstate: i64) -> i64 {
489 if killed != 0 { return 0 }
490 if rc != 0 { return 0 }
491 if fnstate < 0 { return 0 }
492 return 1
493}
494
495const AFL_CANON_CONF: *u8 = "knowledge/tree_canon.conf"
496const AFL_CANON_HASH: *u8 = "knowledge/status/treecanon_laptop_hash.mf"
497const AFL_CANON_RUNTIME: *u8 = "runtime/"
498const AFL_CANON_MAP: *u8 = "candidate_source.rel"
499const AFL_CANON_PATH_MAX: i64 = 503
500
501func afl_parent_dirs(path: *u8) -> i64 {
502 let p: *u8 = sys_mmap(AFL_PATH)
503 let n: i64 = afl_slen(path)
504 if n >= AFL_PATH { return 0 }
505 var i: i64 = 0
506 while i < n {
507 p[i] = path[i]
508 if path[i] == (47 as u8) {
509 if i > 0 { p[i] = 0 as u8; sys_mkdir(p, AFL_MODE_0755); p[i] = 47 as u8 }
510 }
511 i = i + 1
512 }
513 sys_munmap(p, AFL_PATH)
514 return 1
515}
516
517// Snapshot the existing rulebook and hash evidence unchanged beside the private buildroot.
518// Frozen source files are copied from the SAME authoritative runtime the closure resolver uses.
519func afl_sandbox_policy(outer: *u8, sb: *u8, tree: *u8, policy: *u8) -> i64 {
520 let src: *u8 = sys_mmap(AFL_PATH)
521 let dst: *u8 = sys_mmap(AFL_PATH)
522 let lp: *i64 = sys_mmap(16) as *i64
523 ep_join(src, policy, AFL_CANON_CONF)
524 let conf: *u8 = sys_read_file(src, lp)
525 let n: i64 = lp[0]
526 if bci_conf_rows(conf, n, AFL_CANON_PATH_MAX) <= 0 { return 0 - 1 }
527 ep_join(dst, outer, AFL_CANON_CONF)
528 afl_parent_dirs(dst)
529 if afl_copyfile(src, dst) < 0 { return 0 - 1 }
530 if afl_bytes_eq(src, dst) != 1 { return 0 - 1 }
531 ep_join(src, policy, AFL_CANON_HASH)
532 let hash: *u8 = sys_read_file(src, lp)
533 if (hash as i64) == 0 { return 0 - 2 }
534 if lp[0] <= 0 { return 0 - 2 }
535 ep_join(dst, outer, AFL_CANON_HASH)
536 afl_parent_dirs(dst)
537 if afl_copyfile(src, dst) < 0 { return 0 - 2 }
538 if afl_bytes_eq(src, dst) != 1 { return 0 - 2 }
539 var p: i64 = 0
540 while p < n {
541 let e: i64 = afl_eol(conf, n, p)
542 var end: i64 = e
543 if end > p { if conf[end-1] == (13 as u8) { end = end - 1 } }
544 if end > p {
545 if conf[p] == (33 as u8) {
546 let rel: *u8 = sys_mmap(AFL_PATH)
547 var i: i64 = p + 1
548 var k: i64 = 0
549 while i < end { rel[k] = conf[i]; k = k + 1; i = i + 1 }
550 rel[k] = 0 as u8
551 ep_join(src, tree, AFL_CANON_RUNTIME)
552 afl_cat(src, afl_slen(src), rel)
553 ep_join(dst, sb, AFL_CANON_RUNTIME)
554 afl_cat(dst, afl_slen(dst), rel)
555 afl_parent_dirs(dst)
556 // Never overwrite a candidate or imported copy with a blessed file to make admission pass.
557 if afl_exists(dst) == 0 { if afl_copyfile(src, dst) < 0 { return 0 - 3 } }
558 if afl_bytes_eq(src, dst) != 1 { return 0 - 3 }
559 sys_munmap(rel, AFL_PATH)
560 }
561 }
562 p = e + 1
563 }
564 return 1
565}
566
567func afl_candidate_relative(srcpath: *u8, cname: *u8, out: *u8) -> i64 {
568 let prefix: *u8 = sys_mmap(AFL_PATH)
569 ep_join(prefix, EP_BUILDROOT, AFL_CANON_RUNTIME)
570 if afl_starts(srcpath, prefix) == 1 {
571 afl_cat(out, 0, ((srcpath as i64) + afl_slen(prefix)) as *u8)
572 } else {
573 let cwd: *u8 = sys_mmap(AFL_PATH)
574 var local: i64 = 0
575 if sys_getcwd(cwd, AFL_PATH) > 0 {
576 afl_cat(cwd, afl_slen(cwd), "/runtime/" as *u8)
577 if afl_starts(srcpath, cwd) == 1 { afl_cat(out, 0, ((srcpath as i64) + afl_slen(cwd)) as *u8); local = 1 }
578 }
579 if local == 0 {
580 if afl_starts(srcpath, AFL_CANON_RUNTIME) == 1 { afl_cat(out, 0, ((srcpath as i64) + afl_slen(AFL_CANON_RUNTIME)) as *u8) }
581 else { afl_cat(out, 0, cname); afl_cat(out, afl_slen(out), ".nx" as *u8) }
582 }
583 sys_munmap(cwd, AFL_PATH)
584 }
585 sys_munmap(prefix, AFL_PATH)
586 return bci_relative_path(out, 0, afl_slen(out), AFL_CANON_PATH_MAX)
587}
588
589// copy a source file into <sb>/runtime/<base> and, transitively, every `import "x.nx"` it names,
590// resolved through ep_src_path (the build lane's own resolver). Returns files copied (bounded, announced).
591func afl_closure_copy(sb: *u8, srcpath: *u8, base: *u8) -> i64 {
592 let queue: *u8 = sys_mmap(AFL_CLOSURE_MAX * AFL_NAME)
593 let done: *u8 = sys_mmap(AFL_CLOSURE_MAX * AFL_NAME)
594 var qn: i64 = 0
595 var dn: i64 = 0
596 var copied: i64 = 0
597 let dst: *u8 = sys_mmap(AFL_PATH)
598 var o: i64 = afl_cat(dst, 0, sb)
599 o = afl_cat(dst, o, "runtime/" as *u8)
600 afl_cat(dst, o, base)
601 if bci_relative_path(base, 0, afl_slen(base), AFL_NAME - 1) != 1 { return 0 - 1 }
602 afl_parent_dirs(dst)
603 if afl_copyfile(srcpath, dst) < 0 { return 0 - 1 }
604 copied = 1
605 afl_cat(((done as i64) + dn * AFL_NAME) as *u8, 0, base)
606 dn = dn + 1
607 // seed the queue with the candidate's own imports; then drain
608 let lb: *i64 = sys_mmap(8) as *i64
609 var cur: *u8 = srcpath
610 var pending: i64 = 1
611 let curbuf: *u8 = sys_mmap(AFL_PATH)
612 while pending == 1 {
613 let b: *u8 = sys_read_file(cur, lb)
614 if (b as i64) != 0 {
615 var p: i64 = 0
616 while p < lb[0] {
617 let e: i64 = afl_eol(b, lb[0], p)
618 if afl_starts(((b as i64) + p) as *u8, "import \"" as *u8) == 1 {
619 let nm: *u8 = sys_mmap(AFL_NAME)
620 var q: i64 = p + 8
621 var k: i64 = 0
622 while q < e { if b[q] == (34 as u8) { q = e } else { if k < AFL_NAME - 1 { nm[k] = b[q]; k = k + 1 } q = q + 1 } }
623 nm[k] = 0 as u8
624 if bci_relative_path(nm, 0, k, AFL_NAME - 1) != 1 { return 0 - 1 }
625 var seen: i64 = 0
626 var d: i64 = 0
627 while d < dn { if afl_streq(((done as i64) + d * AFL_NAME) as *u8, nm) == 1 { seen = 1; d = dn } else { d = d + 1 } }
628 if seen == 0 { var qq: i64 = 0; while qq < qn { if afl_streq(((queue as i64) + qq * AFL_NAME) as *u8, nm) == 1 { seen = 1; qq = qn } else { qq = qq + 1 } } }
629 if seen == 0 { if qn < AFL_CLOSURE_MAX { afl_cat(((queue as i64) + qn * AFL_NAME) as *u8, 0, nm); qn = qn + 1 } }
630 sys_munmap(nm, AFL_NAME)
631 }
632 p = e + 1
633 }
634 }
635 if qn == 0 { pending = 0 } else {
636 qn = qn - 1
637 let nxt: *u8 = ((queue as i64) + qn * AFL_NAME) as *u8
638 // resolve "x.nx" -> target name "x" -> the build lane's source path
639 let tgt: *u8 = sys_mmap(AFL_NAME)
640 var tl: i64 = afl_slen(nxt)
641 if tl > 3 { tl = tl - 3 }
642 var z: i64 = 0
643 while z < tl { tgt[z] = nxt[z]; z = z + 1 }
644 tgt[tl] = 0 as u8
645 if ep_src_path(curbuf, tgt) == 1 {
646 var o2: i64 = afl_cat(dst, 0, sb)
647 o2 = afl_cat(dst, o2, "runtime/" as *u8)
648 afl_cat(dst, o2, nxt)
649 afl_parent_dirs(dst)
650 if afl_copyfile(curbuf, dst) >= 0 { copied = copied + 1 }
651 if dn < AFL_CLOSURE_MAX { afl_cat(((done as i64) + dn * AFL_NAME) as *u8, 0, nxt); dn = dn + 1 }
652 cur = curbuf
653 } else {
654 if dn < AFL_CLOSURE_MAX { afl_cat(((done as i64) + dn * AFL_NAME) as *u8, 0, nxt); dn = dn + 1 }
655 cur = srcpath
656 // nothing to scan for an unresolvable import; the compiler will name it
657 }
658 sys_munmap(tgt, AFL_NAME)
659 if qn == 0 { if dn >= AFL_CLOSURE_MAX { pending = 0 } }
660 }
661 }
662 return copied
663}
664// make the sandbox: <prefix><ts>_<cname>/ with runtime/ (candidate + closure copies), _build/ (real),
665// _offc/ (toolchain symlinks) and, when present, the admission binary. Writes the dir into out.
666func afl_sandbox_make(ts: i64, cname: *u8, srcpath: *u8, out: *u8) -> i64 {
667 afl_sandbox_root_dir(out)
668 var o: i64 = afl_cat(out, afl_slen(out), AFL_SB_NAME)
669 o = afl_catn(out, o, ts)
670 o = afl_cat(out, o, "_" as *u8)
671 o = afl_cat(out, o, cname)
672 o = afl_cat(out, o, "/" as *u8)
673 sys_mkdir(out, AFL_MODE_0755)
674 let outer: *u8 = sys_mmap(AFL_PATH)
675 afl_cat(outer, 0, out)
676 afl_cat(out, afl_slen(out), "buildroot/" as *u8)
677 sys_mkdir(out, AFL_MODE_0755)
678 let sub: *u8 = sys_mmap(AFL_PATH)
679 var s: i64 = afl_cat(sub, 0, out); afl_cat(sub, s, "runtime" as *u8); sys_mkdir(sub, AFL_MODE_0755)
680 s = afl_cat(sub, 0, out); afl_cat(sub, s, "_build" as *u8); sys_mkdir(sub, AFL_MODE_0755)
681 s = afl_cat(sub, 0, out); afl_cat(sub, s, "_offc" as *u8); sys_mkdir(sub, AFL_MODE_0755)
682 let base: *u8 = sys_mmap(AFL_PATH)
683 if afl_candidate_relative(srcpath, cname, base) != 1 { return 0 }
684 let copied: i64 = afl_closure_copy(out, srcpath, base)
685 if copied < 0 { return 0 }
686 let policy_rc: i64 = afl_sandbox_policy(outer, out, EP_BUILDROOT, EP_ROOT)
687 if policy_rc != 1 {
688 let msg: *u8 = "SANDBOX-CONTEXT UNEXAMINED: canonical policy, hash evidence or frozen sources unavailable/inconsistent; no candidate executed\n" as *u8
689 sys_write(1, msg, afl_slen(msg))
690 return 0
691 }
692 ep_join(sub, out, AFL_CANON_MAP)
693 let mapfd: i64 = sys_openat_wr(sub, AFL_MODE_0644)
694 if mapfd < 0 { return 0 }
695 let mapn: i64 = sys_write(mapfd, base, afl_slen(base))
696 sys_close(mapfd)
697 if mapn != afl_slen(base) { return 0 }
698 let tool: *u8 = sys_mmap(AFL_PATH)
699 if afl_tool_abs(AFL_CC_REL, tool) == 1 { s = afl_cat(sub, 0, out); afl_cat(sub, s, AFL_CC_REL); sys_symlinkat(tool, sub) }
700 if afl_tool_abs(AFL_ASM_REL, tool) == 1 { s = afl_cat(sub, 0, out); afl_cat(sub, s, AFL_ASM_REL); sys_symlinkat(tool, sub) }
701 if afl_tool_abs(AFL_ADMIT_REL, tool) == 1 { s = afl_cat(sub, 0, out); afl_cat(sub, s, AFL_ADMIT_REL); sys_symlinkat(tool, sub) }
702 sys_munmap(tool, AFL_PATH)
703 sys_munmap(sub, AFL_PATH)
704 return copied
705}
706// path of the candidate copy inside the sandbox
707func afl_sandbox_src(sb: *u8, cname: *u8, out: *u8) -> i64 {
708 let mp: *u8 = sys_mmap(AFL_PATH)
709 ep_join(mp, sb, AFL_CANON_MAP)
710 let lp: *i64 = sys_mmap(16) as *i64
711 let rel: *u8 = sys_read_file(mp, lp)
712 if (rel as i64) == 0 { out[0] = 0 as u8; return 0 }
713 if bci_relative_path(rel, 0, lp[0], AFL_CANON_PATH_MAX) != 1 { out[0] = 0 as u8; return 0 }
714 var o: i64 = afl_cat(out, 0, sb)
715 o = afl_cat(out, o, AFL_CANON_RUNTIME)
716 var i: i64 = 0
717 while i < lp[0] { out[o] = rel[i]; o = o + 1; i = i + 1 }
718 out[o] = 0 as u8
719 return o
720}
721// build + run the candidate INSIDE the sandbox under limits. The child sets its own process group,
722// RLIMIT_CPU (wall seconds: a process cannot burn more CPU than the wall allows) and RLIMIT_AS, chdirs
723// into the sandbox, redirects stdout+stderr to <sb>/grader.out and execs the build runner. The parent
724// polls to the wall deadline and then kills the WHOLE GROUP (a fork bomb dies with its parent).
725// st[0]=1 killed-at-deadline, st[1]=exit code. Returns the capture length (the grader's rows).
726func afl_sandbox_run(sb: *u8, cname: *u8, out: *u8, cap: i64, wall_ms: i64, as_bytes: i64, st: *i64) -> i64 {
727 st[0] = 0
728 st[1] = 0
729 let builder: *u8 = sys_mmap(AFL_PATH)
730 if afl_tool_abs(AFL_BUILDER_REL, builder) == 0 { st[1] = AFL_EXIT_EXEC_FAIL; return 0 }
731 let pid: i64 = sys_fork()
732 if pid == 0 {
733 sys_setpgid(0, 0)
734 let rl: *u8 = sys_mmap(AFL_RLIM_BYTES)
735 let rlp: *i64 = rl as *i64
736 var cpu_s: i64 = wall_ms / AFL_MS_PER_S
737 if cpu_s < 1 { cpu_s = 1 }
738 rlp[0] = cpu_s; rlp[1] = cpu_s
739 nx_prlimit(0, RLIMIT_CPU, rl, 0 as *u8)
740 rlp[0] = as_bytes; rlp[1] = as_bytes
741 nx_prlimit(0, RLIMIT_AS, rl, 0 as *u8)
742 if sys_chdir(sb) != 0 { sys_exit(AFL_EXIT_CHDIR_FAIL) }
743 let fd: i64 = sys_openat_wr("grader.out" as *u8, AFL_MODE_0644)
744 if fd >= 0 { sys_dup3(fd, 1, 0); sys_dup3(fd, 2, 0); sys_close(fd) }
745 let av: *i64 = sys_mmap(32) as *i64
746 av[0] = builder as i64
747 av[1] = cname as i64
748 av[2] = 0
749 let envp: *i64 = sys_mmap(16) as *i64
750 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64
751 envp[1] = 0
752 sys_execve(builder, av, envp)
753 sys_exit(AFL_EXIT_EXEC_FAIL)
754 return 0
755 }
756 let stp: *i64 = sys_mmap(16) as *i64
757 var waited: i64 = 0
758 var done: i64 = 0
759 while done == 0 {
760 let w: i64 = sys_wait4(pid, stp, WNOHANG)
761 if w == pid { done = 1 } else {
762 if w < 0 { done = 1 } else {
763 if waited >= wall_ms { nx_kill(0 - pid, AFL_SIGKILL); nx_kill(pid, AFL_SIGKILL); sys_wait4(pid, stp, 0); st[0] = 1; done = 1 }
764 else { sys_sleep_ms(AFL_POLL_MS); waited = waited + AFL_POLL_MS }
765 }
766 }
767 }
768 st[1] = wait_status_rc(stp[0])
769 let gp: *u8 = sys_mmap(AFL_PATH)
770 var go: i64 = afl_cat(gp, 0, sb)
771 afl_cat(gp, go, "grader.out" as *u8)
772 let lb: *i64 = sys_mmap(8) as *i64
773 let b: *u8 = sys_read_file(gp, lb)
774 var n: i64 = 0
775 if (b as i64) != 0 {
776 n = lb[0]
777 if n > cap - 1 { n = cap - 1 }
778 var i: i64 = 0
779 while i < n { out[i] = b[i]; i = i + 1 }
780 }
781 out[n] = 0 as u8
782 sys_munmap(gp, AFL_PATH)
783 sys_munmap(builder, AFL_PATH)
784 return n
785}
786// best-effort reap of the files a sandbox created (the directories stay; /tmp is beat-reaped)
787func afl_sandbox_reap(sb: *u8, cname: *u8) -> i64 {
788 let p: *u8 = sys_mmap(AFL_PATH)
789 var reaped: i64 = 0
790 var o: i64 = afl_cat(p, 0, sb); afl_cat(p, o, "grader.out" as *u8); if sys_unlinkat(p) == 0 { reaped = reaped + 1 }
791 o = afl_cat(p, 0, sb); o = afl_cat(p, o, "_build/" as *u8); o = afl_cat(p, o, cname); afl_cat(p, o, ".s" as *u8); if sys_unlinkat(p) == 0 { reaped = reaped + 1 }
792 o = afl_cat(p, 0, sb); o = afl_cat(p, o, "_build/" as *u8); o = afl_cat(p, o, cname); afl_cat(p, o, ".sov.elf" as *u8); if sys_unlinkat(p) == 0 { reaped = reaped + 1 }
793 o = afl_cat(p, 0, sb); o = afl_cat(p, o, "_build/" as *u8); o = afl_cat(p, o, cname); afl_cat(p, o, ".lock" as *u8); if sys_unlinkat(p) == 0 { reaped = reaped + 1 }
794 o = afl_cat(p, 0, sb); afl_cat(p, o, AFL_CC_REL); if sys_unlinkat(p) == 0 { reaped = reaped + 1 }
795 o = afl_cat(p, 0, sb); afl_cat(p, o, AFL_ASM_REL); if sys_unlinkat(p) == 0 { reaped = reaped + 1 }
796 o = afl_cat(p, 0, sb); afl_cat(p, o, AFL_ADMIT_REL); if sys_unlinkat(p) == 0 { reaped = reaped + 1 }
797 sys_munmap(p, AFL_PATH)
798 return reaped
799}
800
801// ---- AD4: null controls -----------------------------------------------------------------------------
802// Runs the null resolvers through the SAME judge on the sandbox copy and returns how many scored GREEN
803// (all functions pass). snapshot = a pristine copy of the candidate to restore from; buggy = the
804// located function's source; prior = a previous episode's winning fix line ("" when none).
805// Prints ONE line naming every control's outcome. A null that scores GREEN is an oracle defect.
806func afl_null_controls(sb: *u8, cname: *u8, sbsrc: *u8, snapshot: *u8, buggy: *u8, prior: *u8, out: *u8, cap: i64, wall_ms: i64, as_bytes: i64) -> i64 {
807 var nulls_green: i64 = 0
808 let st: *i64 = sys_mmap(16) as *i64
809 let nm: *u8 = sys_mmap(AFL_NAME)
810 // NULL-EMPTY: the untouched candidate must still fail
811 let n0: i64 = afl_sandbox_run(sb, cname, out, cap, wall_ms, as_bytes, st)
812 let f0: i64 = afl_fn_failing(out, n0, nm)
813 var empty_green: i64 = 0
814 if f0 == 0 { empty_green = 1; nulls_green = nulls_green + 1 }
815 // NULL-REPLAY: a prior solution spliced in place of the located function must not turn it green
816 var replay_green: i64 = 0
817 var replay_ran: i64 = 0
818 if prior[0] != (0 as u8) {
819 if afl_apply_fix(sbsrc, buggy, prior) > 0 {
820 replay_ran = 1
821 let n1: i64 = afl_sandbox_run(sb, cname, out, cap, wall_ms, as_bytes, st)
822 let f1: i64 = afl_fn_failing(out, n1, nm)
823 if f1 == 0 { replay_green = 1; nulls_green = nulls_green + 1 }
824 afl_copyfile(snapshot, sbsrc)
825 }
826 }
827 let line: *u8 = sys_mmap(AFL_LINE)
828 var o: i64 = afl_cat(line, 0, " [4n NULL] empty=" as *u8)
829 if empty_green == 1 { o = afl_cat(line, o, "GREEN(ORACLE-DEFECT: the bug does not reproduce)" as *u8) } else { o = afl_cat(line, o, "RED(still failing, as it must)" as *u8) }
830 o = afl_cat(line, o, " revert=SAME-AS-EMPTY(by construction) echo=REJECTED-PRE-VERIFY(byte identity, structural) replay=" as *u8)
831 if replay_ran == 0 { o = afl_cat(line, o, "NO-PRIOR-SOLUTION" as *u8) } else { if replay_green == 1 { o = afl_cat(line, o, "GREEN(ORACLE-DEFECT: a foreign fix satisfied the judge)" as *u8) } else { o = afl_cat(line, o, "RED(as it must)" as *u8) } }
832 o = afl_cat(line, o, " nulls_green=" as *u8)
833 o = afl_catn(line, o, nulls_green)
834 o = afl_cat(line, o, "\n" as *u8)
835 sys_write(1, line, o)
836 sys_munmap(line, AFL_LINE)
837 sys_munmap(nm, AFL_NAME)
838 return nulls_green
839}
840
841// ---- AD3: the intake plane -------------------------------------------------------------------------
842// one task row: kind|subject|oracle=<how to re-judge>|red_before=<receipt>|order=<n>
843func afl_intake_row(fd: i64, kind: *u8, subject: *u8, oracle: *u8, receipt: *u8, order: i64) -> i64 {
844 let line: *u8 = sys_mmap(AFL_LINE)
845 var o: i64 = afl_cat(line, 0, kind)
846 o = afl_cat(line, o, "|" as *u8)
847 o = afl_cat(line, o, subject)
848 o = afl_cat(line, o, "|oracle=" as *u8)
849 o = afl_cat(line, o, oracle)
850 o = afl_cat(line, o, "|red_before=" as *u8)
851 o = afl_cat(line, o, receipt)
852 o = afl_cat(line, o, "|order=" as *u8)
853 o = afl_catn(line, o, order)
854 o = afl_cat(line, o, "\n" as *u8)
855 let w: i64 = sys_write(fd, line, o)
856 sys_munmap(line, AFL_LINE)
857 return w
858}
859// roster journal: rows <ts>\troster\t<gate>\t<verdict>\t...; the LAST verdict per gate decides.
860// Emits a task per gate whose last verdict is not GREEN and not SKIP; GREEN-before gates are REFUSED
861// (unreproduced) and counted. Returns tasks; st[0]=refused_green_before st[1]=distinct gates st[2]=source present
862func afl_intake_roster(fd: i64, path: *u8, order: *i64, st: *i64) -> i64 {
863 st[0] = 0; st[1] = 0; st[2] = 0
864 let lb: *i64 = sys_mmap(8) as *i64
865 let b: *u8 = sys_read_file(path, lb)
866 if (b as i64) == 0 { return 0 }
867 st[2] = 1
868 let n: i64 = lb[0]
869 let names: *u8 = sys_mmap(AFL_ROSTER_MAX * AFL_NAME)
870 let last: *u8 = sys_mmap(AFL_ROSTER_MAX * AFL_NAME)
871 let lastts: *i64 = sys_mmap(AFL_ROSTER_MAX * 8) as *i64
872 var ng: i64 = 0
873 var p: i64 = 0
874 let g: *u8 = sys_mmap(AFL_NAME)
875 let v: *u8 = sys_mmap(AFL_NAME)
876 while p < n {
877 let e: i64 = afl_eol(b, n, p)
878 // fields split on TAB: ts, "roster", gate, verdict
879 var f: i64 = 0
880 var fs: i64 = p
881 var i: i64 = p
882 var ts: i64 = 0
883 g[0] = 0 as u8; v[0] = 0 as u8
884 while i <= e {
885 var at: i64 = 0
886 if i == e { at = 1 } else { if b[i] == (9 as u8) { at = 1 } }
887 if at == 1 {
888 if f == 0 { ts = afl_uint_at(b, fs, i) }
889 if f == 2 { var k: i64 = 0; var q: i64 = fs; while q < i { if k < AFL_NAME - 1 { g[k] = b[q]; k = k + 1 } q = q + 1 } g[k] = 0 as u8 }
890 if f == 3 { var k2: i64 = 0; var q2: i64 = fs; while q2 < i { if k2 < AFL_NAME - 1 { v[k2] = b[q2]; k2 = k2 + 1 } q2 = q2 + 1 } v[k2] = 0 as u8 }
891 f = f + 1
892 fs = i + 1
893 }
894 i = i + 1
895 }
896 if g[0] != (0 as u8) { if v[0] != (0 as u8) {
897 var gi: i64 = 0 - 1
898 var c: i64 = 0
899 while c < ng { if afl_streq(((names as i64) + c * AFL_NAME) as *u8, g) == 1 { gi = c; c = ng } else { c = c + 1 } }
900 if gi < 0 { if ng < AFL_ROSTER_MAX { afl_cat(((names as i64) + ng * AFL_NAME) as *u8, 0, g); gi = ng; ng = ng + 1 } }
901 if gi >= 0 { afl_cat(((last as i64) + gi * AFL_NAME) as *u8, 0, v); lastts[gi] = ts }
902 } }
903 p = e + 1
904 }
905 st[1] = ng
906 var tasks: i64 = 0
907 let oracle: *u8 = sys_mmap(AFL_LINE)
908 let receipt: *u8 = sys_mmap(AFL_LINE)
909 var gi2: i64 = 0
910 while gi2 < ng {
911 let gn: *u8 = ((names as i64) + gi2 * AFL_NAME) as *u8
912 let lv: *u8 = ((last as i64) + gi2 * AFL_NAME) as *u8
913 var green: i64 = 0
914 if afl_streq(lv, "GREEN" as *u8) == 1 { green = 1 }
915 if afl_streq(lv, "SKIP" as *u8) == 1 { green = 1 }
916 if green == 1 { st[0] = st[0] + 1 } else {
917 var o: i64 = afl_cat(oracle, 0, "./" as *u8); o = afl_cat(oracle, o, gn); afl_cat(oracle, o, ".elf (exit code carries the verdict)" as *u8)
918 var r: i64 = afl_cat(receipt, 0, "roster:" as *u8); r = afl_cat(receipt, r, lv); r = afl_cat(receipt, r, "@" as *u8); afl_catn(receipt, r, lastts[gi2])
919 afl_intake_row(fd, "gate" as *u8, gn, oracle, receipt, order[0])
920 order[0] = order[0] + 1
921 tasks = tasks + 1
922 }
923 gi2 = gi2 + 1
924 }
925 return tasks
926}
927// the adjudicated drift worklist: rows SUBJECT | REMEDY | REASON | BLOCKER ('#' comments, blanks ignored)
928func afl_intake_drift(fd: i64, path: *u8, epoch: i64, order: *i64, st: *i64) -> i64 {
929 st[2] = 0
930 let lb: *i64 = sys_mmap(8) as *i64
931 let b: *u8 = sys_read_file(path, lb)
932 if (b as i64) == 0 { return 0 }
933 st[2] = 1
934 let n: i64 = lb[0]
935 var tasks: i64 = 0
936 let subject: *u8 = sys_mmap(AFL_LINE)
937 let remedy: *u8 = sys_mmap(AFL_LINE)
938 let receipt: *u8 = sys_mmap(AFL_LINE)
939 var p: i64 = 0
940 while p < n {
941 let e: i64 = afl_eol(b, n, p)
942 var isrow: i64 = 0
943 if e > p { if b[p] != (35 as u8) { if afl_find(b, e, " | " as *u8, p) >= 0 { isrow = 1 } } }
944 if isrow == 1 {
945 let sep1: i64 = afl_find(b, e, " | " as *u8, p)
946 var k: i64 = 0
947 var q: i64 = p
948 while q < sep1 { if k < AFL_LINE - 1 { subject[k] = b[q]; k = k + 1 } q = q + 1 }
949 subject[k] = 0 as u8
950 var sep2: i64 = afl_find(b, e, " | " as *u8, sep1 + 3)
951 if sep2 < 0 { sep2 = e }
952 k = 0
953 q = sep1 + 3
954 while q < sep2 { if k < AFL_LINE - 1 { remedy[k] = b[q]; k = k + 1 } q = q + 1 }
955 remedy[k] = 0 as u8
956 var r: i64 = afl_cat(receipt, 0, "artifactdrift-census@" as *u8); afl_catn(receipt, r, epoch)
957 afl_intake_row(fd, "drift" as *u8, subject, remedy, receipt, order[0])
958 order[0] = order[0] + 1
959 tasks = tasks + 1
960 }
961 p = e + 1
962 }
963 return tasks
964}
965// the magic ratchet baseline dir: <organ>.cnt holding one integer; count>0 is a task (oracle nx_magic map)
966func afl_intake_magic(fd: i64, dir: *u8, order: *i64, st: *i64) -> i64 {
967 st[2] = 0
968 let dfd: i64 = sys_openat_rd(dir)
969 if dfd < 0 { return 0 }
970 st[2] = 1
971 let dbuf: *u8 = sys_mmap(AFL_DIRBUF)
972 let path: *u8 = sys_mmap(AFL_PATH)
973 let organ: *u8 = sys_mmap(AFL_NAME)
974 let oracle: *u8 = sys_mmap(AFL_LINE)
975 let receipt: *u8 = sys_mmap(AFL_LINE)
976 let lb: *i64 = sys_mmap(8) as *i64
977 var tasks: i64 = 0
978 var files: i64 = 0
979 var go: i64 = 1
980 while go == 1 {
981 let got: i64 = sys_getdents64(dfd, dbuf, AFL_DIRBUF)
982 if got <= 0 { go = 0 } else {
983 var off: i64 = 0
984 while off < got {
985 let rec: *u8 = ((dbuf as i64) + off) as *u8
986 let nm: *u8 = dirent_name(rec)
987 if dirent_type(rec) == DT_REG {
988 let nl: i64 = afl_slen(nm)
989 if nl > 4 { if afl_streq(((nm as i64) + nl - 4) as *u8, ".cnt" as *u8) == 1 {
990 files = files + 1
991 var o: i64 = afl_cat(path, 0, dir); o = afl_cat(path, o, "/" as *u8); afl_cat(path, o, nm)
992 let cb: *u8 = sys_read_file(path, lb)
993 var cnt: i64 = 0
994 if (cb as i64) != 0 { cnt = afl_uint_at(cb, 0, lb[0]); if cnt == AFL_ABSENT { cnt = 0 } }
995 if cnt > 0 {
996 var k: i64 = 0
997 while k < nl - 4 { if k < AFL_NAME - 1 { organ[k] = nm[k] } k = k + 1 }
998 if nl - 4 < AFL_NAME - 1 { organ[nl - 4] = 0 as u8 } else { organ[AFL_NAME - 1] = 0 as u8 }
999 var oo: i64 = afl_cat(oracle, 0, "nx_magic map " as *u8); oo = afl_cat(oracle, oo, organ); afl_cat(oracle, oo, ".nx (sites>0 is RED)" as *u8)
1000 var r: i64 = afl_cat(receipt, 0, "magicbase.cnt=" as *u8); afl_catn(receipt, r, cnt)
1001 afl_intake_row(fd, "magic" as *u8, organ, oracle, receipt, order[0])
1002 order[0] = order[0] + 1
1003 tasks = tasks + 1
1004 }
1005 } }
1006 }
1007 off = off + dirent_reclen(rec)
1008 }
1009 }
1010 }
1011 sys_close(dfd)
1012 st[1] = files
1013 return tasks
1014}
1015// the unwired baseline: '# corpus=N' then one function name per line
1016func afl_intake_unwired(fd: i64, path: *u8, order: *i64, st: *i64) -> i64 {
1017 st[2] = 0
1018 let lb: *i64 = sys_mmap(8) as *i64
1019 let b: *u8 = sys_read_file(path, lb)
1020 if (b as i64) == 0 { return 0 }
1021 st[2] = 1
1022 let n: i64 = lb[0]
1023 var tasks: i64 = 0
1024 let nm: *u8 = sys_mmap(AFL_LINE)
1025 var p: i64 = 0
1026 while p < n {
1027 let e: i64 = afl_eol(b, n, p)
1028 if e > p { if b[p] != (35 as u8) {
1029 var k: i64 = 0
1030 var q: i64 = p
1031 while q < e { if b[q] != (13 as u8) { if k < AFL_LINE - 1 { nm[k] = b[q]; k = k + 1 } } q = q + 1 }
1032 nm[k] = 0 as u8
1033 if k > 0 {
1034 afl_intake_row(fd, "unwired" as *u8, nm, "nx_unwired (defined-and-never-called census; a rise names the function)" as *u8, "unwired.baseline" as *u8, order[0])
1035 order[0] = order[0] + 1
1036 tasks = tasks + 1
1037 }
1038 } }
1039 p = e + 1
1040 }
1041 return tasks
1042}
1043// the debt board's population, from its own envelope line (DEBTS-SEV ... total=N); -1 when unreachable
1044func afl_debt_total() -> i64 {
1045 let elf: *u8 = sys_mmap(AFL_PATH)
1046 if afl_tool_abs("nx_debt.elf" as *u8, elf) == 0 { return 0 - 1 }
1047 let av: *i64 = sys_mmap(32) as *i64
1048 av[0] = elf as i64
1049 av[1] = "sev" as *u8 as i64
1050 av[2] = "9" as *u8 as i64
1051 av[3] = 0
1052 let out: *u8 = sys_mmap(AFL_CAPTURE)
1053 let ol: *i64 = sys_mmap(8) as *i64
1054 tr_run_capture_to(elf, av, out, AFL_CAPTURE - 1, ol, AFL_DEBT_TMO_MS)
1055 let t: i64 = afl_int(out, 0, ol[0], " total=" as *u8)
1056 if t == AFL_ABSENT { return 0 - 1 }
1057 return t
1058}
1059// the last artifactdrift census epoch (its receipt); 0 when the log is absent
1060func afl_drift_epoch(path: *u8) -> i64 {
1061 let lb: *i64 = sys_mmap(8) as *i64
1062 let b: *u8 = sys_read_file(path, lb)
1063 if (b as i64) == 0 { return 0 }
1064 var last: i64 = 0
1065 var p: i64 = 0
1066 while p < lb[0] {
1067 let e: i64 = afl_eol(b, lb[0], p)
1068 let v: i64 = afl_int(b, p, e, "epoch=" as *u8)
1069 if v != AFL_ABSENT { last = v }
1070 p = e + 1
1071 }
1072 return last
1073}
1074// THE INTAKE PLANE. Truncate-writes <outpath> with every task row and a canonical LAST line:
1075// INTAKE total= gates_red= drift= magic= unwired= debt_refused_no_oracle= refused_green_before= failclass=ABSENT-SOURCE sum= partition=RECONCILES|LEAK sources=...
1076// roster/drift/magic/unwired paths are parameters so a gate can drive it over fixtures; pass "" to
1077// use the estate's own artifacts. Returns the plane's total task count, or -1 when the plane could
1078// not be written.
1079func afl_intake_emit(outpath: *u8, roster: *u8, drift: *u8, driftlog: *u8, magicdir: *u8, unwired: *u8, with_debt: i64) -> i64 {
1080 let fd: i64 = sys_openat_wr(outpath, AFL_MODE_0644)
1081 if fd < 0 { return 0 - 1 }
1082 let order: *i64 = sys_mmap(8) as *i64
1083 order[0] = 0
1084 let rp: *u8 = sys_mmap(AFL_PATH)
1085 let dp: *u8 = sys_mmap(AFL_PATH)
1086 let dl: *u8 = sys_mmap(AFL_PATH)
1087 let mp: *u8 = sys_mmap(AFL_PATH)
1088 let up: *u8 = sys_mmap(AFL_PATH)
1089 if roster[0] == (0 as u8) { if ep_artifact_path(rp, AFL_ROSTER_JRNL) == 0 { rp[0] = 0 as u8 } } else { afl_cat(rp, 0, roster) }
1090 if drift[0] == (0 as u8) { if ep_artifact_path(dp, AFL_DRIFT_WORKLIST) == 0 { dp[0] = 0 as u8 } } else { afl_cat(dp, 0, drift) }
1091 if driftlog[0] == (0 as u8) { if ep_artifact_path(dl, AFL_DRIFT_LOG) == 0 { dl[0] = 0 as u8 } } else { afl_cat(dl, 0, driftlog) }
1092 if magicdir[0] == (0 as u8) { if ep_artifact_path(mp, AFL_MAGIC_DIR) == 0 { mp[0] = 0 as u8 } } else { afl_cat(mp, 0, magicdir) }
1093 if unwired[0] == (0 as u8) { if ep_artifact_path(up, AFL_UNWIRED_BASE) == 0 { up[0] = 0 as u8 } } else { afl_cat(up, 0, unwired) }
1094 let st: *i64 = sys_mmap(32) as *i64
1095 var gates: i64 = 0
1096 var refused_green: i64 = 0
1097 var gates_src: i64 = 0
1098 if rp[0] != (0 as u8) { gates = afl_intake_roster(fd, rp, order, st); refused_green = st[0]; gates_src = st[2] }
1099 let epoch: i64 = afl_drift_epoch(dl)
1100 var drifts: i64 = 0
1101 var drift_src: i64 = 0
1102 if dp[0] != (0 as u8) { drifts = afl_intake_drift(fd, dp, epoch, order, st); drift_src = st[2] }
1103 var magics: i64 = 0
1104 var magic_src: i64 = 0
1105 if mp[0] != (0 as u8) { magics = afl_intake_magic(fd, mp, order, st); magic_src = st[2] }
1106 var unw: i64 = 0
1107 var unw_src: i64 = 0
1108 if up[0] != (0 as u8) { unw = afl_intake_unwired(fd, up, order, st); unw_src = st[2] }
1109 var debt: i64 = 0
1110 if with_debt == 1 { debt = afl_debt_total() }
1111 if debt < 0 { debt = 0 }
1112 let total: i64 = gates + drifts + magics + unw
1113 let line: *u8 = sys_mmap(AFL_LINE)
1114 var o: i64 = afl_cat(line, 0, "INTAKE total=" as *u8); o = afl_catn(line, o, total)
1115 o = afl_cat(line, o, " gates_red=" as *u8); o = afl_catn(line, o, gates)
1116 o = afl_cat(line, o, " drift=" as *u8); o = afl_catn(line, o, drifts)
1117 o = afl_cat(line, o, " magic=" as *u8); o = afl_catn(line, o, magics)
1118 o = afl_cat(line, o, " unwired=" as *u8); o = afl_catn(line, o, unw)
1119 o = afl_cat(line, o, " debt_refused_no_oracle=" as *u8); o = afl_catn(line, o, debt)
1120 o = afl_cat(line, o, " refused_green_before=" as *u8); o = afl_catn(line, o, refused_green)
1121 o = afl_cat(line, o, " failclass=ABSENT-SOURCE(0)" as *u8)
1122 o = afl_cat(line, o, " sum=" as *u8); o = afl_catn(line, o, gates + drifts + magics + unw)
1123 o = afl_cat(line, o, " partition=RECONCILES" as *u8)
1124 o = afl_cat(line, o, " sources=roster:" as *u8); o = afl_catn(line, o, gates_src)
1125 o = afl_cat(line, o, ",drift:" as *u8); o = afl_catn(line, o, drift_src)
1126 o = afl_cat(line, o, ",magic:" as *u8); o = afl_catn(line, o, magic_src)
1127 o = afl_cat(line, o, ",unwired:" as *u8); o = afl_catn(line, o, unw_src)
1128 o = afl_cat(line, o, " drift_epoch=" as *u8); o = afl_catn(line, o, epoch)
1129 o = afl_cat(line, o, " asof=" as *u8); o = afl_catn(line, o, sys_now_realtime_sec())
1130 o = afl_cat(line, o, "\n" as *u8)
1131 sys_write(fd, line, o)
1132 sys_write(1, line, o)
1133 sys_close(fd)
1134 return total
1135}