code wiki / (root) / nx_sweep_core.nx

nx_sweep_core.nx source

↩ module page · 136 lines · 6727 B

1// nx_sweep_core.nx -- STANDING-SWEEP core (library). The SOTA property a naive sweep lacks: it NEVER 2// HANGS. Each check runs in a forked child whose stdout/stderr are captured, under a SINGLE wall-clock 3// DEADLINE; if the child overruns it is SIGKILLed and the check verdict is TIMEOUT -- one slow/wedged 4// check can never stall the whole sweep. Pure + gate-testable: sw_run_deadline is the primitive, sw_verdict 5// maps (rc, expect) -> verdict. Data-driven: checks come from a registry the daemon parses, never hardcoded. 6// license_tier: ORIGINAL 7import "nx_syscalls.nx" 8 9const SW_TIMEOUT: i64 = 0 - 100 // child killed on the deadline 10const SW_SPAWN_ERR: i64 = 0 - 101 // pipe/fork failed 11const SW_POLLIN: i64 = 1 // poll events: data-to-read 12const SW_SIGKILL: i64 = 9 13const SW_MS_PER_S: i64 = 1000 14const SW_NS_PER_MS: i64 = 1000000 15const SW_POLL_TICK: i64 = 200 // max ms per poll wait (re-checks the deadline this often) 16const SW_REAP_TICK: i64 = 5 // ms between WNOHANG reap polls after EOF 17const SW_FD_MASK: i64 = 0xFFFFFFFF 18const SW_B0: i64 = 0xFF // byte masks for the 32-bit pollfd fd field 19const SW_SH8: i64 = 8 20const SW_SH16: i64 = 16 21const SW_SH24: i64 = 24 22const SW_POLLFD_BYTES: i64 = 8 // struct pollfd = {int fd; short events; short revents} 23const SW_PF_EV: i64 = 4 // events offset 24// verdict codes (data-driven; no magic in callers) 25const SW_V_PASS: i64 = 0 26const SW_V_FAIL: i64 = 1 27const SW_V_TIMEOUT: i64 = 2 28const SW_V_ERROR: i64 = 3 29const SW_PTRPAIR: i64 = 16 // scratch for a two-i64 struct (timespec / fds / wait-status) 30const SW_EXEC_FAIL: i64 = 127 // child exit code when execve fails (bad path / not ELF) 31const SW_FD_STDOUT: i64 = 1 32const SW_FD_STDERR: i64 = 2 33const SW_NFDS_1: i64 = 1 // poll: exactly one fd 34const SW_SCR_PF: i64 = 16 // scratch offset: pollfd (after fds[2]) 35const SW_SCR_STP: i64 = 24 // scratch offset: wait-status (after pollfd) 36const SW_SCR_MIN: i64 = 40 // minimum caller scratch bytes 37 38// monotonic milliseconds (elapsed measurement -- immune to wall-clock steps). LEAK-FREE: munmaps its 39// scratch (this is called MANY times per check in the deadline poll loop; a bare sys_mmap here leaked a 40// 4KB page per call -- invisible until fast-cycled, the exact never-surfaced-until-measured leak class). 41func sw_mono_ms() -> i64 { 42 let ts: *i64 = sys_mmap(SW_PTRPAIR) as *i64 43 sys_clock_gettime_mono(ts) 44 let r: i64 = ts[0] * SW_MS_PER_S + ts[1] / SW_NS_PER_MS 45 sys_munmap(ts as *u8, SW_PTRPAIR) 46 return r 47} 48// map a run result + expected exit code -> verdict 49func sw_verdict(rc: i64, expect: i64) -> i64 { 50 if rc == SW_TIMEOUT { return SW_V_TIMEOUT } 51 if rc == SW_SPAWN_ERR { return SW_V_ERROR } 52 if rc == expect { return SW_V_PASS } 53 return SW_V_FAIL 54} 55// write a struct pollfd {int fd; short events=POLLIN; short revents=0} into pf[0..8). SW_PF_EV (=4) doubles 56// as the fd byte-width; the two shorts after it are zeroed. Byte-loop -> no literal offsets (rule 11). 57func sw_pollfd(pf: *u8, fd: i64) -> i64 { 58 var b: i64 = 0 59 while b < SW_PF_EV { pf[b] = ((fd >> (b * SW_SH8)) & SW_B0) as u8; b = b + 1 } // 32-bit fd, little-endian 60 pf[SW_PF_EV] = SW_POLLIN as u8 61 var e: i64 = SW_POLLIN // =1: start after the low events byte 62 while e < SW_PF_EV { pf[SW_PF_EV + e] = 0 as u8; e = e + 1 } // clear events-hi + revents 63 return 0 64} 65// Run `path` with argv (NULL-terminated *i64 vector, argv[0]=path) in directory `cwd` (0 or ""-> inherit), 66// capturing up to cap bytes of stdout+stderr into out, KILLING the child if it overruns deadline_ms. 67// Returns the child's exit code, or SW_TIMEOUT (killed), or SW_SPAWN_ERR. outlen (if non-null) gets bytes. 68// cwd is applied IN THE CHILD (isolated -- the daemon's own cwd is never mutated). 69// `scr` is a caller-provided scratch block (>= SW_SCR_MIN bytes) reused across calls, so a hot loop calling 70// this every cycle does ZERO sys_mmap here (the forever-daemon never-leak requirement). Layout: fds@0(16), 71// pf@SW_SCR_PF(8), stp@SW_SCR_STP(16). 72func sw_run_deadline(path: *u8, argv: *i64, cwd: *u8, out: *u8, cap: i64, outlen: *i64, deadline_ms: i64, scr: *u8) -> i64 { 73 if (outlen as i64) != 0 { outlen[0] = 0 } 74 let fds: *i64 = scr as *i64 75 if sys_pipe2(fds, 0) != 0 { return SW_SPAWN_ERR } 76 let packed: i64 = fds[0] 77 let rfd: i64 = packed & SW_FD_MASK 78 let wfd: i64 = (packed >> SW_SH24 >> SW_SH8) & SW_FD_MASK // >>32, staged to avoid a 32-shift literal 79 let pid: i64 = sys_fork() 80 if pid < 0 { sys_close(rfd); sys_close(wfd); return SW_SPAWN_ERR } 81 if pid == 0 { 82 sys_dup3(wfd, SW_FD_STDOUT, 0) 83 sys_dup3(wfd, SW_FD_STDERR, 0) 84 sys_close(rfd) 85 sys_close(wfd) 86 if (cwd as i64) != 0 { if cwd[0] != (0 as u8) { sys_chdir(cwd) } } 87 let envp: *i64 = sys_mmap(SW_PTRPAIR) as *i64 88 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 89 envp[1] = 0 90 sys_execve(path, argv, envp) 91 sys_exit(SW_EXEC_FAIL) 92 return 0 93 } 94 sys_close(wfd) 95 let pf: *u8 = ((scr as i64) + SW_SCR_PF) as *u8 96 let stp: *i64 = ((scr as i64) + SW_SCR_STP) as *i64 97 let start: i64 = sw_mono_ms() 98 var total: i64 = 0 99 var eof: i64 = 0 100 // ---- drain phase: read until EOF or deadline ---- 101 while eof == 0 { 102 let remaining: i64 = deadline_ms - (sw_mono_ms() - start) 103 if remaining <= 0 { 104 nx_kill(pid, SW_SIGKILL) 105 sys_wait4(pid, stp, 0) 106 sys_close(rfd) 107 if (outlen as i64) != 0 { outlen[0] = total } 108 return SW_TIMEOUT 109 } 110 var wait: i64 = remaining 111 if wait > SW_POLL_TICK { wait = SW_POLL_TICK } 112 sw_pollfd(pf, rfd) 113 let pr: i64 = sys_poll(pf, SW_NFDS_1, wait) 114 if pr > 0 { 115 if total < cap { 116 let r: i64 = sys_read(rfd, ((out as i64) + total) as *u8, cap - total) 117 if r <= 0 { eof = 1 } else { total = total + r } 118 } else { eof = 1 } // buffer full -> stop reading, still reap under the deadline 119 } 120 } 121 sys_close(rfd) 122 if (outlen as i64) != 0 { outlen[0] = total } 123 // ---- reap phase: bounded by the SAME deadline ---- 124 var run: i64 = 1 125 while run == 1 { 126 let w: i64 = sys_wait4(pid, stp, WNOHANG) 127 if w == pid { return wait_exit_code(stp[0]) } 128 if deadline_ms - (sw_mono_ms() - start) <= 0 { 129 nx_kill(pid, SW_SIGKILL) 130 sys_wait4(pid, stp, 0) 131 return SW_TIMEOUT 132 } 133 sys_sleep_ms(SW_REAP_TICK) 134 } 135 return SW_SPAWN_ERR 136}