code wiki / (root) / fuzz_lex_test.nx

fuzz_lex_test.nx source

↩ module page · 97 lines · 3384 B

1// fuzz_lex_test.nx -- mutational fuzz against lex_source. 2// 3// Mutates valid NishiLang sources and feeds them to the lexer. 4// The lexer should ALWAYS return without crashing -- malformed 5// input may produce unexpected tokens, but it must never 6// dereference NULL or run off the end of the buffer. 7// 8// This turns the theoretical "lex_source is robust" claim into 9// an empirical "we've tried 10,000 mutated inputs and none 10// crashed" result. If any run DOES crash, nx_assert fires and 11// we get a clean post-mortem + the exact seed for replay. 12// 13// Seed corpus: 14// "func f() { return 42 }" -- minimal function 15// "let x: i64 = 100" -- let statement 16// "0xdeadbeef + 0b1010" -- mixed-base integers 17// "\"hello world\"" -- string literal 18// "if a < b { x = 1 }" -- control flow 19// "x[0].field" -- postfix chain 20// "// comment\nfunc g() {}" -- comment handling 21// "0.5 + 3.14f" -- floating-point literals 22 23import "syscalls.nx" 24import "types.nx" 25import "lex_kinds.nx" 26import "lex.nx" 27import "nx_fuzz.nx" 28 29const FUZZ_ITERS: i64 = 10000 30const FUZZ_MAX_INPUT: i64 = 1024 31 32func main() -> i64 { 33 let f: *NxFuzz = nx_fuzz_new(0xDEADC0DE) 34 35 // Populate corpus. 36 nx_fuzz_add_seed(f, "func f() { return 42 }" as *u8, 22) 37 nx_fuzz_add_seed(f, "let x: i64 = 100" as *u8, 16) 38 nx_fuzz_add_seed(f, "0xdeadbeef + 0b1010" as *u8, 19) 39 nx_fuzz_add_seed(f, "\"hello world\"" as *u8, 13) 40 nx_fuzz_add_seed(f, "if a < b { x = 1 }" as *u8, 18) 41 nx_fuzz_add_seed(f, "x[0].field" as *u8, 10) 42 nx_fuzz_add_seed(f, "// comment\nfunc g() {}" as *u8, 22) 43 nx_fuzz_add_seed(f, "0.5 + 3.14f" as *u8, 11) 44 45 let buf: *u8 = sys_mmap(FUZZ_MAX_INPUT + 16) 46 47 var iter: i64 = 0 48 while iter < FUZZ_ITERS { 49 let len: i64 = nx_fuzz_mutate(f, buf, FUZZ_MAX_INPUT) 50 if len < 0 { 51 return __syscall(93, 10, 0, 0, 0, 0, 0) 52 } 53 // NUL-terminate so the lexer sees EOF at the right spot. 54 buf[len] = 0 55 56 // Fire the lexer. We don't CARE what it produces -- 57 // we only care that it returns without crashing. 58 // (The SECURITY property: no infinite loops, no segv, 59 // no OOM. If any of those happen, process dies and the 60 // harness catches non-zero exit.) 61 let toks: *Tok = lex_source(buf, 1024) 62 if toks == (0 as *Tok) { 63 // Null return is a valid lex failure mode (e.g. out of 64 // token capacity). Count it as a failed run + move on. 65 nx_fuzz_crash(f) 66 } 67 68 iter = iter + 1 69 } 70 71 // Report stats. If we survived 10000 iterations without 72 // segfault / infinite loop, the lexer is robust enough for 73 // this mutation style. 74 sys_write(2, "fuzz_lex: runs=" as *u8, 15) 75 let sbuf: *u8 = sys_mmap(32) 76 var v: i64 = f.runs 77 var si: i64 = 0 78 if v == 0 { sbuf[0] = 0x30; si = 1 } 79 while v > 0 { 80 sbuf[si] = 0x30 + (v - (v / 10) * 10) 81 v = v / 10 82 si = si + 1 83 } 84 var j: i64 = 0 85 var k: i64 = si - 1 86 while j < k { 87 let tmp: i64 = sbuf[j] 88 sbuf[j] = sbuf[k] 89 sbuf[k] = tmp 90 j = j + 1 91 k = k - 1 92 } 93 sbuf[si] = 0x0A 94 sys_write(2, sbuf, si + 1) 95 96 return 0 97}