nx_canary_test.nx source
↩ module page · 171 lines · 8570 B
1// nx_canary_test.nx -- end-to-end smoke for nx_canary_value.
2//
3// Exercises the verify-constant path, verify-bytes path, verify-file
4// path (composing sys_read_file), and the aggregate scan with a
5// mixed-verdict canary array. All five exit codes are mapped to
6// specific assertions so a failed smoke pinpoints the broken
7// invariant.
8
9import "syscalls.nx"
10import "nx_canary_value.nx"
11
12const NX_TEST_NOW_UNIX: i64 = 1747436400 // 2026-05-16 reference timestamp
13
14func main() -> i64 {
15 // ===== 1. Constant canary install + verify ====================
16
17 let name_const: *u8 = sys_mmap(32)
18 name_const[0] = 0x66; name_const[1] = 0x6e; name_const[2] = 0x76 // "fnv"
19 name_const[3] = 0x5f; name_const[4] = 0x70; name_const[5] = 0x72 // "_pr"
20 name_const[6] = 0x69; name_const[7] = 0x6d; name_const[8] = 0x65 // "ime"
21 name_const[9] = 0
22
23 let loc_const: *u8 = sys_mmap(32)
24 loc_const[0] = 0x4e; loc_const[1] = 0x58 // "NX"
25 loc_const[2] = 0x5f; loc_const[3] = 0x43; loc_const[4] = 0x41 // "_CA"
26 loc_const[5] = 0x4e; loc_const[6] = 0x41; loc_const[7] = 0x52 // "NAR"
27 loc_const[8] = 0x59 // "Y"
28 loc_const[9] = 0
29
30 let c_const: *CanaryRecord = nx_canary_install(
31 name_const, 9, NX_CANARY_CLASS_PRIMITIVE_CONSTANT,
32 NX_CANARY_FNV1A_PRIME, 0,
33 loc_const, 9, NX_TEST_NOW_UNIX)
34 if c_const == 0 as *CanaryRecord { return __syscall(93, 1, 0, 0, 0, 0, 0) }
35
36 // Verify with correct value -> INTACT
37 let v1: i64 = nx_canary_verify_constant(c_const, NX_CANARY_FNV1A_PRIME, NX_TEST_NOW_UNIX)
38 if v1 != NX_CANARY_INTACT { return __syscall(93, 2, 0, 0, 0, 0, 0) }
39 if c_const.last_verdict != NX_CANARY_INTACT { return __syscall(93, 3, 0, 0, 0, 0, 0) }
40
41 // Verify with wrong value -> VIOLATED
42 let v2: i64 = nx_canary_verify_constant(c_const, NX_CANARY_FNV1A_PRIME + 1, NX_TEST_NOW_UNIX)
43 if v2 != NX_CANARY_VIOLATED { return __syscall(93, 4, 0, 0, 0, 0, 0) }
44
45 // Re-verify with correct value to leave it INTACT for aggregate test
46 nx_canary_verify_constant(c_const, NX_CANARY_FNV1A_PRIME, NX_TEST_NOW_UNIX)
47
48 // ===== 2. Bytes canary self-consistency =======================
49
50 let msg_buf: *u8 = sys_mmap(32)
51 msg_buf[0] = 0x68; msg_buf[1] = 0x65; msg_buf[2] = 0x6c; msg_buf[3] = 0x6c // "hell"
52 msg_buf[4] = 0x6f; msg_buf[5] = 0x20; msg_buf[6] = 0x63; msg_buf[7] = 0x61 // "o ca"
53 msg_buf[8] = 0x6e; msg_buf[9] = 0x61; msg_buf[10] = 0x72; msg_buf[11] = 0x79 // "nary"
54 msg_buf[12] = 0x20; msg_buf[13] = 0x77; msg_buf[14] = 0x6f; msg_buf[15] = 0x72 // " wor"
55 msg_buf[16] = 0x6c; msg_buf[17] = 0x64 // "ld"
56 let msg_len: i64 = 18
57
58 let msg_hash: i64 = nx_canary_hash_bytes(msg_buf, msg_len)
59 if msg_hash == 0 { return __syscall(93, 5, 0, 0, 0, 0, 0) }
60
61 let name_bytes: *u8 = sys_mmap(32)
62 name_bytes[0] = 0x6d; name_bytes[1] = 0x73; name_bytes[2] = 0x67; name_bytes[3] = 0
63
64 let c_bytes: *CanaryRecord = nx_canary_install(
65 name_bytes, 3, NX_CANARY_CLASS_CATALOG_ROW,
66 0, msg_hash,
67 loc_const, 9, NX_TEST_NOW_UNIX)
68 if c_bytes == 0 as *CanaryRecord { return __syscall(93, 6, 0, 0, 0, 0, 0) }
69
70 // Verify with same bytes -> INTACT
71 let v3: i64 = nx_canary_verify_bytes(c_bytes, msg_buf, msg_len, NX_TEST_NOW_UNIX)
72 if v3 != NX_CANARY_INTACT { return __syscall(93, 7, 0, 0, 0, 0, 0) }
73
74 // Mutate one byte, re-verify -> VIOLATED
75 msg_buf[5] = 0x21 // " " -> "!"
76 let v4: i64 = nx_canary_verify_bytes(c_bytes, msg_buf, msg_len, NX_TEST_NOW_UNIX)
77 if v4 != NX_CANARY_VIOLATED { return __syscall(93, 8, 0, 0, 0, 0, 0) }
78 msg_buf[5] = 0x20 // restore for any later use
79
80 // Re-verify INTACT for aggregate
81 nx_canary_verify_bytes(c_bytes, msg_buf, msg_len, NX_TEST_NOW_UNIX)
82
83 // ===== 3. File canary self-consistency ========================
84 //
85 // Read runtime/nx_canary_value.nx itself, hash it, install a
86 // canary pointing at that path with that hash, verify INTACT.
87 // Then mutate the expected hash, verify VIOLATED.
88
89 let path: *u8 = sys_mmap(64)
90 // "runtime/nx_canary_value.nx\0"
91 path[0] = 0x72; path[1] = 0x75; path[2] = 0x6e; path[3] = 0x74 // "runt"
92 path[4] = 0x69; path[5] = 0x6d; path[6] = 0x65; path[7] = 0x2f // "ime/"
93 path[8] = 0x6e; path[9] = 0x78; path[10] = 0x5f; path[11] = 0x63 // "nx_c"
94 path[12] = 0x61; path[13] = 0x6e; path[14] = 0x61; path[15] = 0x72 // "anar"
95 path[16] = 0x79; path[17] = 0x5f; path[18] = 0x76; path[19] = 0x61 // "y_va"
96 path[20] = 0x6c; path[21] = 0x75; path[22] = 0x65; path[23] = 0x2e // "lue."
97 path[24] = 0x6e; path[25] = 0x78; path[26] = 0 // "nx\0"
98
99 let flen_box: *u8 = sys_mmap(8)
100 let flen_p: *i64 = flen_box as *i64
101 let file_bytes: *u8 = sys_read_file(path, flen_p)
102 if file_bytes == 0 as *u8 { return __syscall(93, 9, 0, 0, 0, 0, 0) }
103 if *flen_p <= 0 { return __syscall(93, 10, 0, 0, 0, 0, 0) }
104
105 let file_hash: i64 = nx_canary_hash_bytes(file_bytes, *flen_p)
106
107 let name_file: *u8 = sys_mmap(16)
108 name_file[0] = 0x66; name_file[1] = 0x69; name_file[2] = 0x6c; name_file[3] = 0x65 // "file"
109 name_file[4] = 0
110
111 let c_file: *CanaryRecord = nx_canary_install(
112 name_file, 4, NX_CANARY_CLASS_DOCUMENT_HASH,
113 0, file_hash,
114 path, 26, NX_TEST_NOW_UNIX)
115 if c_file == 0 as *CanaryRecord { return __syscall(93, 11, 0, 0, 0, 0, 0) }
116
117 let v5: i64 = nx_canary_verify_file(c_file, NX_TEST_NOW_UNIX)
118 if v5 != NX_CANARY_INTACT { return __syscall(93, 12, 0, 0, 0, 0, 0) }
119
120 // Mutate canary's expected hash -> next verify must report VIOLATED
121 c_file.expected_value_hash = file_hash + 1
122 let v6: i64 = nx_canary_verify_file(c_file, NX_TEST_NOW_UNIX)
123 if v6 != NX_CANARY_VIOLATED { return __syscall(93, 13, 0, 0, 0, 0, 0) }
124
125 // Restore expected hash to INTACT for aggregate
126 c_file.expected_value_hash = file_hash
127 nx_canary_verify_file(c_file, NX_TEST_NOW_UNIX)
128
129 // ===== 4. Aggregate scan over mixed canary array ==============
130 //
131 // Build [INTACT, INTACT, INTACT] -> aggregate verdict INTACT.
132 // Then mutate the middle canary's expected to wrong value, run
133 // scan_files (which re-verifies via verify_file) and assert
134 // aggregate VIOLATED with violated=1, intact=2.
135
136 let arr_raw: *u8 = sys_mmap(24) // 3 *CanaryRecord pointers
137 let arr: **CanaryRecord = arr_raw as **CanaryRecord
138 arr[0] = c_const
139 arr[1] = c_bytes
140 arr[2] = c_file
141
142 let scope: *u8 = sys_mmap(16)
143 scope[0] = 0x73; scope[1] = 0x6d; scope[2] = 0x6b // "smk"
144 scope[3] = 0
145
146 // First aggregate without re-reading files: verify_bytes / verify_constant
147 // were last called with INTACT-producing inputs, so all three should be INTACT.
148 let r1: *CanaryScanReport = nx_canary_scan_aggregate(arr, 3, scope, NX_TEST_NOW_UNIX)
149 if r1.verdict != NX_CANARY_INTACT { return __syscall(93, 14, 0, 0, 0, 0, 0) }
150 if r1.canaries_intact != 3 { return __syscall(93, 15, 0, 0, 0, 0, 0) }
151 if r1.canaries_violated != 0 { return __syscall(93, 16, 0, 0, 0, 0, 0) }
152
153 // Now mutate the file canary's expected hash + re-scan via
154 // scan_files (which re-verifies each via verify_file). File canary
155 // must report VIOLATED; constant + bytes still report INTACT
156 // because scan_files only re-verifies FILE canaries -- wait, actually
157 // scan_files re-verifies every canary via verify_file which only
158 // makes sense for canaries with valid location paths. The constant
159 // + bytes canaries have location=loc_const ("NX_CANARY") which is
160 // NOT a file, so sys_read_file will return NULL -> last_verdict
161 // becomes NX_CANARY_MISSING for those two. Aggregate verdict will
162 // be VIOLATED because file canary trips, and MISSING for the others
163 // -- but aggregate-verdict priority is VIOLATED > MISSING > INTACT,
164 // so verdict is VIOLATED. Test that.
165 c_file.expected_value_hash = file_hash + 1
166 let r2: *CanaryScanReport = nx_canary_scan_files(arr, 3, scope, NX_TEST_NOW_UNIX)
167 if r2.verdict != NX_CANARY_VIOLATED { return __syscall(93, 17, 0, 0, 0, 0, 0) }
168 if r2.canaries_violated < 1 { return __syscall(93, 18, 0, 0, 0, 0, 0) }
169
170 return 0
171}