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}