nx_cloexec_gate.nx
buildroot/runtime/nx_cloexec_gate.nx
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
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
structs
| none |
consts
| 22 | const CX_LOG: *u8 = "knowledge/status/cloexec_gate.log" |
| 23 | const CX_MODE: i64 = 420 |
| 24 | const CX_FCNTL: i64 = 72 |
| 25 | const CX_F_GETFD: i64 = 1 |
| 26 | const CX_F_SETFD: i64 = 2 |
| 27 | const CX_FD_CLOEXEC: i64 = 1 |
| 28 | const CX_AF_INET: i64 = 2 |
| 29 | const CX_SOCK_STREAM: i64 = 1 |
functions
| 31 | func 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 |
| 32 | func cx_w(fd: i64, s: *u8) -> i64 { let n: i64 = cx_len(s); sys_write(fd, s, n); return 0 } |
| 33 | func cx_p(s: *u8) -> i64 { return cx_w(1, s) } |
| 34 | func cx_pn(v: i64) -> i64 |
| 47 | func cx_ck(name: *u8, c: i64) -> i64 |
| 54 | func cx_is_cloexec(fd: i64) -> i64 called by 1: main |
| 62 | func cx_addr(port: i64) -> *u8 |
| 77 | func main() -> i64 |