code wiki / _hdl_build / nx_doc_ubscan.nx
nx_doc_ubscan.nx source
↩ module page · 45 lines · 2616 B
1// nx_doc_ubscan.nx -- a static detector for the UNDEFINED-BEHAVIOR pattern LM-031: an inline cast-then-index
2// `(EXPR as *T)[i]` SILENTLY MISCOMPILES in NishiLang, returning a garbage pointer-sized value with NO error (it
3// cost a real bug: nx_vizsla_notes nv_occ_window returned a bogus deadline year; nx_doc_demo_const ran exit 156 for
4// expected 99). The fix-by-construction (LM-031): bind the cast to a local FIRST, then index. This detector flags
5// the dangerous inline form so it's caught BEFORE it corrupts data -- the build-resolve loop can't (it's a silent
6// miscompile, no build failure). LINEAGE: this is the NISHI RECYCLER's CONVERT of the undefined-behavior critique
7// (knowledge/fetched/recyc_crit_ub.raw) that nx_recycler_extract flagged as APPLIES-to-us -> turned into real
8// hardening. READ-ONLY (no rewrite). Doctor-family sibling of nx_doc_kwscan / nx_doc_mainscan. license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_doc_heal_let.nx"
11
12func ub_skipws(src: *u8, n: i64, p0: i64) -> i64 {
13 var p: i64=p0; var go: i64=1
14 while go==1 { if p<n { if src[p]==(32 as u8){p=p+1} else { if src[p]==(9 as u8){p=p+1} else { if src[p]==(10 as u8){p=p+1} else {go=0} } } } else {go=0} }
15 return p
16}
17// 1 iff a cast-then-index `as *<ident> ) [` pattern starts at the `as` token at position i.
18func ub_castindex_at(src: *u8, n: i64, i: i64) -> i64 {
19 if i+2 > n { return 0 }
20 if src[i]!=(97 as u8) { return 0 } // 'a'
21 if src[i+1]!=(115 as u8) { return 0 } // 's'
22 if i>0 { if dhl_isident(src[i-1] as i64)==1 { return 0 } } // 'as' is a whole word
23 if i+2<n { if dhl_isident(src[i+2] as i64)==1 { return 0 } }
24 var p: i64 = ub_skipws(src, n, i+2)
25 if p>=n { return 0 }
26 if src[p]!=(42 as u8) { return 0 } // '*'
27 p = ub_skipws(src, n, p+1)
28 if p>=n { return 0 }
29 if dhl_isident(src[p] as i64)==0 { return 0 } // the pointed-to type name
30 var go: i64=1
31 while go==1 { if p<n { if dhl_isident(src[p] as i64)==1 { p=p+1 } else { go=0 } } else { go=0 } }
32 p = ub_skipws(src, n, p)
33 if p>=n { return 0 }
34 if src[p]!=(41 as u8) { return 0 } // ')'
35 p = ub_skipws(src, n, p+1)
36 if p>=n { return 0 }
37 if src[p]!=(91 as u8) { return 0 } // '[' -> the dangerous inline index
38 return 1
39}
40// byte offset of the first inline cast-then-index in src[0..n), or -1.
41func ub_scan(src: *u8, n: i64) -> i64 {
42 var i: i64=0
43 while i<n { if ub_castindex_at(src, n, i)==1 { return i } i=i+1 }
44 return 0-1
45}