nx_ico_gate.nx source
↩ module page · 190 lines · 8491 B
1// nx_ico_gate.nx -- proves the ICO writer/reader pair by ROUND-TRIP.
2//
3// T1 pins ZERO MEANS 256 in both directions. The width and height fields are
4// one byte, so 256 -- the standard modern favicon size -- is stored as 0. A
5// reader taking the byte at face value reports a 256x256 icon as 0x0, and
6// this is the single most common ICO bug: it appears at exactly the one size
7// that matters most today and nowhere else.
8//
9// T4 proves the payload sniff. Nothing in the directory says whether an entry
10// is a DIB or a PNG, both are legal in the same file, and the bitCount field
11// is routinely zero or wrong for PNG entries. The test builds a mixed file
12// and requires each entry to be classified from its own bytes.
13//
14// T6 covers the crafted-file case: an entry whose offset or length points
15// past the buffer, or back into the directory, must be REFUSED rather than
16// followed.
17//
18// license_tier: ORIGINAL
19import "nx_syscalls.nx"
20import "nx_ico.nx"
21
22func g_puts(s: *u8) -> i64 {
23 var i: i64 = 0
24 while s[i] != (0 as u8) { i = i + 1 }
25 sys_write(1, s, i)
26 return i
27}
28
29func g_putn(v: i64) -> i64 {
30 let buf: *u8 = sys_mmap(32)
31 var x: i64 = v
32 if x < 0 { g_puts("-" as *u8); x = 0 - x }
33 if x == 0 { buf[0] = 0x30 as u8; sys_write(1, buf, 1); return 1 }
34 let tmp: *u8 = sys_mmap(32)
35 var d: i64 = 0
36 while x > 0 { tmp[d] = ((x % 10) + 0x30) as u8; x = x / 10; d = d + 1 }
37 var i: i64 = 0
38 while i < d { buf[i] = tmp[d - 1 - i]; i = i + 1 }
39 sys_write(1, buf, d)
40 return d
41}
42
43func main() -> i64 {
44 var fails: i64 = 0
45 var mark: i64 = 0
46
47 // ---- T1: ZERO MEANS 256, in both directions ----
48 if nx_ico_dim_decode(0) != 256 { fails = fails + 1 }
49 if nx_ico_dim_decode(16) != 16 { fails = fails + 1 }
50 if nx_ico_dim_decode(255) != 255 { fails = fails + 1 }
51 if nx_ico_dim_encode(256) != 0 { fails = fails + 1 }
52 if nx_ico_dim_encode(16) != 16 { fails = fails + 1 }
53 if nx_ico_dim_encode(255) != 255 { fails = fails + 1 }
54 // sizes outside 1..256 have no encoding and are refused
55 if nx_ico_dim_encode(0) != (0 - 1) { fails = fails + 1 }
56 if nx_ico_dim_encode(257) != (0 - 1) { fails = fails + 1 }
57 // the round-trip through the byte must survive 256
58 if nx_ico_dim_decode(nx_ico_dim_encode(256)) != 256 { fails = fails + 1 }
59 if fails > 0 { if mark == 0 { mark = 1 } }
60
61 // ---- T2/T3: build a real three-entry icon and read it back ----
62 let ico: *u8 = sys_mmap(8192)
63 // a PNG-shaped payload for the 256px entry
64 let png: *u8 = sys_mmap(1024)
65 png[0] = 0x89 as u8; png[1] = 0x50 as u8; png[2] = 0x4e as u8; png[3] = 0x47 as u8
66 png[4] = 0x0d as u8; png[5] = 0x0a as u8; png[6] = 0x1a as u8; png[7] = 0x0a as u8
67 var i: i64 = 8
68 while i < 400 { png[i] = ((i * 13 + 7) & 255) as u8; i = i + 1 }
69 // a DIB-shaped payload for the small entries
70 let dib: *u8 = sys_mmap(1024)
71 dib[0] = 0x28 as u8; dib[1] = 0x00 as u8; dib[2] = 0x00 as u8; dib[3] = 0x00 as u8
72 i = 4
73 while i < 300 { dib[i] = ((i * 5 + 3) & 255) as u8; i = i + 1 }
74
75 var off: i64 = nx_ico_write_begin(ico, 8192, 3)
76 if off != (6 + 3 * 16) { fails = fails + 1 }
77 off = nx_ico_write_entry(ico, 8192, 0, off, 16, 16, 32, dib, 300)
78 if off <= 0 { fails = fails + 1 }
79 off = nx_ico_write_entry(ico, 8192, 1, off, 32, 32, 32, dib, 300)
80 if off <= 0 { fails = fails + 1 }
81 // the 256px entry -- stored as a zero byte
82 off = nx_ico_write_entry(ico, 8192, 2, off, 256, 256, 32, png, 400)
83 if off <= 0 { fails = fails + 1 }
84
85 if nx_ico_count(ico, off) != 3 { fails = fails + 1 }
86 // the directory byte for the 256px entry must literally be zero
87 let ep2: i64 = 6 + 2 * 16
88 if (ico[ep2] as i64 & 255) != 0 { fails = fails + 1 }
89 if (ico[ep2+1] as i64 & 255) != 0 { fails = fails + 1 }
90 if fails > 0 { if mark == 0 { mark = 2 } }
91
92 let fld: *i64 = sys_mmap(128) as *i64
93 // entry 0
94 if nx_ico_read_entry(ico, off, 0, fld) != 1 { fails = fails + 1 } else {
95 if fld[NX_ICO_FLD_WIDTH] != 16 { fails = fails + 1 }
96 if fld[NX_ICO_FLD_HEIGHT] != 16 { fails = fails + 1 }
97 if fld[NX_ICO_FLD_BYTES] != 300 { fails = fails + 1 }
98 if fld[NX_ICO_FLD_BITS] != 32 { fails = fails + 1 }
99 if fld[NX_ICO_FLD_PLANES] != 1 { fails = fails + 1 }
100 if fld[NX_ICO_FLD_ISPNG] != 0 { fails = fails + 1 }
101 // the payload bytes must come back exactly
102 let po: i64 = fld[NX_ICO_FLD_OFFSET]
103 var bad: i64 = 0
104 var k: i64 = 0
105 while k < 300 {
106 if (ico[po+k] as i64 & 255) != (dib[k] as i64 & 255) { bad = bad + 1 }
107 k = k + 1
108 }
109 if bad != 0 { fails = fails + 1 }
110 }
111 // entry 2 -- the 256px PNG
112 if nx_ico_read_entry(ico, off, 2, fld) != 1 { fails = fails + 1 } else {
113 if fld[NX_ICO_FLD_WIDTH] != 256 { fails = fails + 1 }
114 if fld[NX_ICO_FLD_HEIGHT] != 256 { fails = fails + 1 }
115 if fld[NX_ICO_FLD_BYTES] != 400 { fails = fails + 1 }
116 let po2: i64 = fld[NX_ICO_FLD_OFFSET]
117 var bad2: i64 = 0
118 var k2: i64 = 0
119 while k2 < 400 {
120 if (ico[po2+k2] as i64 & 255) != (png[k2] as i64 & 255) { bad2 = bad2 + 1 }
121 k2 = k2 + 1
122 }
123 if bad2 != 0 { fails = fails + 1 }
124 }
125 if fails > 0 { if mark == 0 { mark = 3 } }
126
127 // ---- T4: the payload is classified from its OWN BYTES ----
128 nx_ico_read_entry(ico, off, 2, fld)
129 if fld[NX_ICO_FLD_ISPNG] != 1 { fails = fails + 1 }
130 nx_ico_read_entry(ico, off, 1, fld)
131 if fld[NX_ICO_FLD_ISPNG] != 0 { fails = fails + 1 }
132 if fails > 0 { if mark == 0 { mark = 4 } }
133
134 // ---- T5: size selection ----
135 // exact match wins
136 if nx_ico_best(ico, off, 32) != 1 { fails = fails + 1 }
137 if nx_ico_best(ico, off, 16) != 0 { fails = fails + 1 }
138 if nx_ico_best(ico, off, 256) != 2 { fails = fails + 1 }
139 // no exact match -> the smallest entry at least as large
140 if nx_ico_best(ico, off, 20) != 1 { fails = fails + 1 }
141 if nx_ico_best(ico, off, 100) != 2 { fails = fails + 1 }
142 // larger than everything -> the largest available
143 if nx_ico_best(ico, off, 512) != 2 { fails = fails + 1 }
144 if fails > 0 { if mark == 0 { mark = 5 } }
145
146 // ---- T6 NEG: crafted and malformed files are REFUSED ----
147 // reserved must be zero
148 ico[0] = 1 as u8
149 if nx_ico_count(ico, off) != (0 - 1) { fails = fails + 1 }
150 ico[0] = 0 as u8
151 // an unknown type
152 ico[2] = 3 as u8
153 if nx_ico_count(ico, off) != (0 - 1) { fails = fails + 1 }
154 ico[2] = 1 as u8
155 if nx_ico_count(ico, off) != 3 { fails = fails + 1 }
156 // an offset past the end of the buffer
157 nx_ico_w32(ico, 6 + 12, off + 1000)
158 if nx_ico_read_entry(ico, off, 0, fld) != 0 { fails = fails + 1 }
159 // an offset pointing back INTO the directory
160 nx_ico_w32(ico, 6 + 12, 10)
161 if nx_ico_read_entry(ico, off, 0, fld) != 0 { fails = fails + 1 }
162 // a length that runs past the end
163 nx_ico_w32(ico, 6 + 12, 54)
164 nx_ico_w32(ico, 6 + 8, off)
165 if nx_ico_read_entry(ico, off, 0, fld) != 0 { fails = fails + 1 }
166 // out-of-range indices
167 nx_ico_w32(ico, 6 + 8, 300)
168 if nx_ico_read_entry(ico, off, 3, fld) != 0 { fails = fails + 1 }
169 if nx_ico_read_entry(ico, off, 0 - 1, fld) != 0 { fails = fails + 1 }
170 // a zero or oversized entry count at write time
171 if nx_ico_write_begin(ico, 8192, 0) != 0 { fails = fails + 1 }
172 if nx_ico_write_begin(ico, 8192, 256) != 0 { fails = fails + 1 }
173 if nx_ico_write_begin(ico, 10, 3) != 0 { fails = fails + 1 }
174 // a dimension with no encoding
175 if nx_ico_write_entry(ico, 8192, 0, 100, 300, 16, 32, dib, 10) != 0 { fails = fails + 1 }
176 if fails > 0 { if mark == 0 { mark = 6 } }
177
178 if fails == 0 {
179 g_puts("GATE nx_ico verdict=GREEN pass=6/6 (ZERO MEANS 256 both ways with 0 and 257 refused; 3-entry icon written, directory byte for the 256px entry is literally 0; all entries read back with exact payload bytes; payload classified from ITS OWN BYTES not the directory -- PNG and DIB in one file; size selection exact/round-up/largest; NEG reserved-nonzero/bad-type/offset-past-end/offset-into-directory/length-overrun/bad-index/zero-count/oversize-count/small-buffer/unencodable-dimension all refused)\n" as *u8)
180 sys_exit(0)
181 return 0
182 }
183 g_puts("GATE nx_ico verdict=RED fails=" as *u8)
184 g_putn(fails)
185 g_puts(" first_stage=" as *u8)
186 g_putn(mark)
187 g_puts("\n" as *u8)
188 sys_exit(1)
189 return 1
190}