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}