code wiki / (root) / nx_cloexec_gate.nx

nx_cloexec_gate.nx

buildroot/runtime/nx_cloexec_gate.nx

6843 B140 linesdepth 4pulls 4 transitivereach 0 importersview sourcekind gate/proof
docsdependenciesstructsconstsfunctions

about

nx_cloexec_gate.nx -- proves every listening socket this tree creates is FD_CLOEXEC, so a forked+exec'd child can never inherit it and hold the port hostage. THE OUTAGE THIS GATE EXISTS FOR (measured live 2026-07-30): `netstat -tlnp` showed BOTH 127.0.0.1:18098 LISTEN 707/nx_hostctl <- the SUPERVISOR 127.0.0.1:18098 LISTEN 24311/./nx_opaque_l <- a login daemon whose argv names ONLY port 9091 while nx_mgmt_api -- whose port 18098 is -- was NOT LISTENING AT ALL and its restart counter climbed 6 -> 7. THE CHAIN: /api/deploy forks+execs nx_hostctl from inside nx_mgmt_api, which was holding its listening socket with no FD_CLOEXEC. hostctl inherited the fd; hostctl is the long-lived supervisor, so it held mgmt's port FOREVER and re-leaked it into every child it subsequently spawned. Restarting the victim can never help -- it is an outage that SURVIVES EVERY RESTART and reads as a causeless crash-loop. SO_REUSEPORT DOES NOT RESCUE IT: the kernel permits co-binding only when EVERY socket on the port set SO_REUSEPORT, so one inherited legacy socket locks out even a REUSEPORT binder. T1 is the CONTROL: a raw socket with no fix MUST read FD_CLOEXEC CLEAR. Without it, T3/T4 could pass on a kernel that sets the bit by default and would prove nothing. T5 is the neg-control on the READER itself. license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0

dependencies 2 imports · 0 importers

nx_syscalls.nx nx_http_server.nx nx_cloexec_gate.nx

imports: nx_syscalls.nxnx_http_server.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main cx_p cx_w cx_len sys_write sys_socket cx_is_cloexec cx_ck cx_p ↻ sys_close sys_mmap nx_http_server_listen sys_ignore_sigpipe sys_mmap ↻ sys_socket ↻ sys_mmap ↻ sys_setsockopt sys_bind sys_close ↻ sys_listen cx_addr sys_mmap ↻ nx_http_server_listen_hot sys_ignore_sigpipe ↻ sys_socket ↻ sys_mmap ↻ sys_setsockopt ↻ sys_bind ↻ sys_close ↻ sys_listen ↻ cx_pn sys_mmap ↻ sys_write ↻ sys_openat_append cx_w ↻ sys_exit

structs

none

consts

22const CX_LOG: *u8 = "knowledge/status/cloexec_gate.log"
23const CX_MODE: i64 = 420
24const CX_FCNTL: i64 = 72
25const CX_F_GETFD: i64 = 1
26const CX_F_SETFD: i64 = 2
27const CX_FD_CLOEXEC: i64 = 1
28const CX_AF_INET: i64 = 2
29const CX_SOCK_STREAM: i64 = 1

functions

31func cx_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
called by 1: cx_w
32func cx_w(fd: i64, s: *u8) -> i64 { let n: i64 = cx_len(s); sys_write(fd, s, n); return 0 }
called by 2: cx_pmain calls 2: cx_lensys_write
33func cx_p(s: *u8) -> i64 { return cx_w(1, s) }
called by 2: cx_ckmain calls 1: cx_w
34func cx_pn(v: i64) -> i64
called by 1: main calls 2: sys_mmapsys_write
47func cx_ck(name: *u8, c: i64) -> i64
called by 1: main calls 1: cx_p
54func cx_is_cloexec(fd: i64) -> i64
called by 1: main
62func cx_addr(port: i64) -> *u8
called by 1: main calls 1: sys_mmap
77func main() -> i64