code wiki / _hdl_build / nx_dos_timeout_scan.nx
nx_dos_timeout_scan.nx source
↩ module page · 145 lines · 7357 B
1// nx_dos_timeout_scan.nx -- FLEET DoS-STARVATION GUARD (seq321). A single-threaded accept-loop daemon that
2// loop-reads to Content-Length can be starved FOREVER by one peer that declares a body it never finishes sending
3// = a one-request DoS (hostile OR merely buggy client). The 2-line cure is sys_set_socket_timeout(cfd,N) right
4// after accept (RCVTIMEO+SNDTIMEO). This guard SUPERVISES the class: it scans the organ tree and flags every
5// DAEMON source that has an accept() call but NO socket-timeout tooth, so the fix (fold-in-on-touch) is a visible
6// standing signal, not a one-time audit. Read-only (never patches). Pattern: nx_dup_source_check.
7// nx_dos_timeout_scan -- self-test the detector, then scan buildroot/runtime/_hdl_build; RED if vulns
8// license_tier: ORIGINAL Sovereign: nx_syscalls. expect_exit: 0 when clean.
9import "nx_syscalls.nx"
10const K_MAGIC_131072: i64 = 131072
11const K_MAGIC_524288: i64 = 524288
12const K_MAGIC_4096: i64 = 4096
13const K_MAGIC_524287: i64 = 524287
14
15func dw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
16func dwn(v: i64) -> i64 {
17 var t: i64=v; if t<0 { sys_write(1,"-" as *u8,1); t=0-t }
18 let tm: *u8=sys_mmap(24); var k: i64=0; if t==0 { tm[0]=48 as u8; k=1 }
19 while t>0 { tm[k]=(48+(t%10)) as u8; t=t/10; k=k+1 }
20 let b: *u8=sys_mmap(24); var j: i64=0; while j<k { b[j]=tm[k-1-j]; j=j+1 } sys_write(1,b,k); return 0
21}
22func dslen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
23func ends_nx(nm: *u8) -> i64 {
24 let n: i64 = dslen(nm)
25 if n < 3 { return 0 }
26 if nm[n-3]==(46 as u8) { if nm[n-2]==(110 as u8) { if nm[n-1]==(120 as u8) { return 1 } } }
27 return 0
28}
29// substring present in buf[0..n)?
30func has_sub(buf: *u8, n: i64, needle: *u8) -> i64 {
31 let m: i64 = dslen(needle)
32 if m == 0 { return 0 }
33 var i: i64 = 0
34 while i + m <= n {
35 var k: i64=0; var hit: i64=1
36 while k < m { if buf[i+k] != needle[k] { hit=0; k=m } else { k=k+1 } }
37 if hit==1 { return 1 }
38 i=i+1
39 }
40 return 0
41}
42// is the basename a LIVE-daemon candidate? must contain "daemon" or "serve"; exclude test/gate/ref/tamper/mock/example.
43func name_is_daemon(nm: *u8) -> i64 {
44 let n: i64 = dslen(nm)
45 if has_sub(nm, n, "daemon" as *u8)==0 { if has_sub(nm, n, "serve" as *u8)==0 { return 0 } }
46 if has_sub(nm, n, "_test" as *u8)==1 { return 0 }
47 if has_sub(nm, n, "_gate" as *u8)==1 { return 0 }
48 if has_sub(nm, n, "_ref" as *u8)==1 { return 0 }
49 if has_sub(nm, n, "_tamper" as *u8)==1 { return 0 }
50 if has_sub(nm, n, "_mock" as *u8)==1 { return 0 }
51 if nm[0]==(95 as u8) { return 0 } // leading '_' = example/scratch
52 return 1
53}
54func join_path(buf: *u8, dir: *u8, name: *u8) -> i64 {
55 var o: i64=0; var i: i64=0
56 while dir[i]!=(0 as u8) { buf[o]=dir[i]; o=o+1; i=i+1 }
57 buf[o]=47 as u8; o=o+1
58 i=0; while name[i]!=(0 as u8) { buf[o]=name[i]; o=o+1; i=i+1 }
59 buf[o]=0 as u8
60 return 0
61}
62func read_file(path: *u8, buf: *u8, cap: i64) -> i64 {
63 let fd: i64 = sys_openat_rd(path)
64 if fd < 0 { return 0-1 }
65 var n: i64=0
66 var go: i64=1
67 while go==1 { let r: i64=sys_read(fd, ((buf as i64)+n) as *u8, cap-n); if r<=0 { go=0 } else { n=n+r } if n>=cap { go=0 } }
68 sys_close(fd)
69 return n
70}
71// a source is DoS-VULNERABLE iff it does an accept() but has NO socket-timeout tooth.
72func is_vuln(buf: *u8, n: i64) -> i64 {
73 var accepts: i64=0
74 if has_sub(buf, n, "sys_accept" as *u8)==1 { accepts=1 }
75 if has_sub(buf, n, "accept4" as *u8)==1 { accepts=1 }
76 if accepts==0 { return 0 }
77 if has_sub(buf, n, "set_socket_timeout" as *u8)==1 { return 0 }
78 if has_sub(buf, n, "SO_RCVTIMEO" as *u8)==1 { return 0 }
79 return 1
80}
81// scan a dir; flag daemon .nx that are vulnerable. verbose=1 prints each. returns the vuln count.
82func scan(dir: *u8, verbose: i64) -> i64 {
83 let fd: i64 = sys_openat_rd(dir)
84 if fd < 0 { return 0 }
85 let dbuf: *u8 = sys_mmap(K_MAGIC_131072)
86 let fbuf: *u8 = sys_mmap(K_MAGIC_524288)
87 var vulns: i64=0
88 var go: i64=1
89 while go==1 {
90 let nr: i64 = sys_getdents64(fd, dbuf, K_MAGIC_131072)
91 if nr<=0 { go=0 } else {
92 var off: i64=0
93 while off<nr {
94 let rec: *u8 = (dbuf as i64 + off) as *u8
95 let ty: i64 = dirent_type(rec)
96 let nm: *u8 = dirent_name(rec)
97 if ty != 4 {
98 if ends_nx(nm)==1 { if name_is_daemon(nm)==1 {
99 let p: *u8 = sys_mmap(K_MAGIC_4096)
100 join_path(p, dir, nm)
101 let fn: i64 = read_file(p, fbuf, K_MAGIC_524287)
102 if fn>0 { if is_vuln(fbuf, fn)==1 {
103 vulns=vulns+1
104 if verbose==1 { dw(" DoS-VULN (accept-loop, no socket timeout): " as *u8); dw(nm); dw("\n" as *u8) }
105 } }
106 } }
107 }
108 off = off + dirent_reclen(rec)
109 }
110 }
111 }
112 sys_close(fd)
113 return vulns
114}
115func wfile(path: *u8, s: *u8) -> i64 { let fd: i64=sys_openat_wr(path, 420); if fd>=0 { sys_write(fd, s, dslen(s)); sys_close(fd) } return 0 }
116// prove the detector: a daemon-named file with accept + NO timeout -> vuln; one WITH the timeout -> safe;
117// a non-daemon (no accept) -> ignored. scan must report exactly 1.
118func selftest() -> i64 {
119 sys_mkdir("/tmp/nxdos" as *u8, 0x1ed)
120 wfile("/tmp/nxdos/nx_bad_daemon.nx" as *u8, "func serve() { let cfd: i64 = sys_accept(lfd); handle(cfd) }\x00" as *u8)
121 wfile("/tmp/nxdos/nx_good_daemon.nx" as *u8, "func serve() { let cfd: i64 = sys_accept(lfd); sys_set_socket_timeout(cfd, 25); handle(cfd) }\x00" as *u8)
122 wfile("/tmp/nxdos/nx_util_serve.nx" as *u8, "func compute() { return 42 }\x00" as *u8) // no accept -> ignored
123 let v: i64 = scan("/tmp/nxdos" as *u8, 0)
124 if v==1 { return 1 }
125 dw(" SELFTEST FAIL: expected exactly 1 vuln, got "); dwn(v); dw("\n" as *u8)
126 return 0
127}
128func main() -> i64 {
129 dw("nx_dos_timeout_scan (fleet DoS-starvation guard -- flags accept-loop daemons missing the socket-timeout tooth, seq321)\n" as *u8)
130 if selftest() != 1 { dw("verdict=RED (detector self-test failed)\n" as *u8); sys_exit(1); return 1 }
131 dw(" self-test OK (accept-without-timeout caught, with-timeout + non-daemon ignored)\n" as *u8)
132 dw(" scanning buildroot/runtime/_hdl_build ...\n" as *u8)
133 let v: i64 = scan("buildroot/runtime/_hdl_build" as *u8, 1)
134 dw(" DoS-vulnerable daemon sources: "); dwn(v); dw("\n" as *u8)
135 let logf: i64 = sys_openat_wr("knowledge/status/dos_timeout_scan.log" as *u8, 0x1a4)
136 if logf>=0 {
137 if v==0 { sys_write(logf, "VERDICT=GREEN dos-timeout 0 accept-loop daemons missing the socket-timeout tooth (seq321 class clean)\n" as *u8, 96) }
138 else { sys_write(logf, "VERDICT=VULN dos-timeout accept-loop daemons missing socket-timeout tooth -- fold sys_set_socket_timeout in on next touch (seq321)\n" as *u8, 127) }
139 sys_close(logf)
140 }
141 if v==0 { dw("verdict=GREEN (no DoS-vulnerable daemons)\n" as *u8); sys_exit(0); return 0 }
142 dw("verdict=VULN (fold sys_set_socket_timeout(cfd,N) after accept into each -- 2-line cure, seq321; supervised now)\n" as *u8)
143 sys_exit(0)
144 return 0
145}