Source file
src/internal/fuzz/encoding_test.go
1
2
3
4
5 package fuzz
6
7 import (
8 "math"
9 "strconv"
10 "testing"
11 "unicode"
12 )
13
14 func TestUnmarshalMarshal(t *testing.T) {
15 var tests = []struct {
16 desc string
17 in string
18 reject bool
19 want string
20 }{
21 {
22 desc: "missing version",
23 in: "int(1234)",
24 reject: true,
25 },
26 {
27 desc: "malformed string",
28 in: `go test fuzz v1
29 string("a"bcad")`,
30 reject: true,
31 },
32 {
33 desc: "empty value",
34 in: `go test fuzz v1
35 int()`,
36 reject: true,
37 },
38 {
39 desc: "negative uint",
40 in: `go test fuzz v1
41 uint(-32)`,
42 reject: true,
43 },
44 {
45 desc: "int8 too large",
46 in: `go test fuzz v1
47 int8(1234456)`,
48 reject: true,
49 },
50 {
51 desc: "multiplication in int value",
52 in: `go test fuzz v1
53 int(20*5)`,
54 reject: true,
55 },
56 {
57 desc: "double negation",
58 in: `go test fuzz v1
59 int(--5)`,
60 reject: true,
61 },
62 {
63 desc: "malformed bool",
64 in: `go test fuzz v1
65 bool(0)`,
66 reject: true,
67 },
68 {
69 desc: "malformed byte",
70 in: `go test fuzz v1
71 byte('aa)`,
72 reject: true,
73 },
74 {
75 desc: "byte out of range",
76 in: `go test fuzz v1
77 byte('☃')`,
78 reject: true,
79 },
80 {
81 desc: "extra newline",
82 in: `go test fuzz v1
83 string("has extra newline")
84 `,
85 want: `go test fuzz v1
86 string("has extra newline")`,
87 },
88 {
89 desc: "trailing spaces",
90 in: `go test fuzz v1
91 string("extra")
92 []byte("spacing")
93 `,
94 want: `go test fuzz v1
95 string("extra")
96 []byte("spacing")`,
97 },
98 {
99 desc: "float types",
100 in: `go test fuzz v1
101 float64(0)
102 float32(0)`,
103 },
104 {
105 desc: "various types",
106 in: `go test fuzz v1
107 int(-23)
108 int8(-2)
109 int64(2342425)
110 uint(1)
111 uint16(234)
112 uint32(352342)
113 uint64(123)
114 rune('œ')
115 byte('K')
116 byte('ÿ')
117 []byte("hello¿")
118 []byte("a")
119 bool(true)
120 string("hello\\xbd\\xb2=\\xbc ⌘")
121 float64(-12.5)
122 float32(2.5)`,
123 },
124 {
125 desc: "float edge cases",
126
127
128
129
130
131
132 in: `go test fuzz v1
133 float32(-0)
134 float64(-0)
135 float32(+Inf)
136 float32(-Inf)
137 float32(NaN)
138 float64(+Inf)
139 float64(-Inf)
140 float64(NaN)
141 math.Float64frombits(0x7ff8000000000002)
142 math.Float32frombits(0x7fc00001)`,
143 },
144 {
145 desc: "int variations",
146
147
148
149
150 in: `go test fuzz v1
151 int(0x0)
152 int32(0x41)
153 int64(0xfffffffff)
154 uint32(0xcafef00d)
155 uint64(0xffffffffffffffff)
156 uint8(0b0000000)
157 byte(0x0)
158 byte('\000')
159 byte('\u0000')
160 byte('\'')
161 math.Float64frombits(9221120237041090562)
162 math.Float32frombits(2143289345)`,
163 want: `go test fuzz v1
164 int(0)
165 rune('A')
166 int64(68719476735)
167 uint32(3405705229)
168 uint64(18446744073709551615)
169 byte('\x00')
170 byte('\x00')
171 byte('\x00')
172 byte('\x00')
173 byte('\'')
174 math.Float64frombits(0x7ff8000000000002)
175 math.Float32frombits(0x7fc00001)`,
176 },
177 {
178 desc: "rune validation",
179 in: `go test fuzz v1
180 rune(0)
181 rune(0x41)
182 rune(-1)
183 rune(0xfffd)
184 rune(0xd800)
185 rune(0x10ffff)
186 rune(0x110000)
187 `,
188 want: `go test fuzz v1
189 rune('\x00')
190 rune('A')
191 int32(-1)
192 rune('�')
193 int32(55296)
194 rune('\U0010ffff')
195 int32(1114112)`,
196 },
197 {
198 desc: "int overflow",
199 in: `go test fuzz v1
200 int(0x7fffffffffffffff)
201 uint(0xffffffffffffffff)`,
202 want: func() string {
203 switch strconv.IntSize {
204 case 32:
205 return `go test fuzz v1
206 int(-1)
207 uint(4294967295)`
208 case 64:
209 return `go test fuzz v1
210 int(9223372036854775807)
211 uint(18446744073709551615)`
212 default:
213 panic("unreachable")
214 }
215 }(),
216 },
217 {
218 desc: "windows new line",
219 in: "go test fuzz v1\r\nint(0)\r\n",
220 want: "go test fuzz v1\nint(0)",
221 },
222 }
223 for _, test := range tests {
224 t.Run(test.desc, func(t *testing.T) {
225 vals, err := unmarshalCorpusFile([]byte(test.in))
226 if test.reject {
227 if err == nil {
228 t.Fatalf("unmarshal unexpected success")
229 }
230 return
231 }
232 if err != nil {
233 t.Fatalf("unmarshal unexpected error: %v", err)
234 }
235 newB := marshalCorpusFile(vals...)
236 if err != nil {
237 t.Fatalf("marshal unexpected error: %v", err)
238 }
239 if newB[len(newB)-1] != '\n' {
240 t.Error("didn't write final newline to corpus file")
241 }
242
243 want := test.want
244 if want == "" {
245 want = test.in
246 }
247 want += "\n"
248 got := string(newB)
249 if got != want {
250 t.Errorf("unexpected marshaled value\ngot:\n%s\nwant:\n%s", got, want)
251 }
252 })
253 }
254 }
255
256
257
258
259 func BenchmarkMarshalCorpusFile(b *testing.B) {
260 buf := make([]byte, 1024*1024)
261 for i := 0; i < len(buf); i++ {
262 buf[i] = byte(i)
263 }
264
265 for sz := 1; sz <= len(buf); sz <<= 1 {
266 sz := sz
267 b.Run(strconv.Itoa(sz), func(b *testing.B) {
268 for i := 0; i < b.N; i++ {
269 b.SetBytes(int64(sz))
270 marshalCorpusFile(buf[:sz])
271 }
272 })
273 }
274 }
275
276
277
278
279 func BenchmarkUnmarshalCorpusFile(b *testing.B) {
280 buf := make([]byte, 1024*1024)
281 for i := 0; i < len(buf); i++ {
282 buf[i] = byte(i)
283 }
284
285 for sz := 1; sz <= len(buf); sz <<= 1 {
286 sz := sz
287 data := marshalCorpusFile(buf[:sz])
288 b.Run(strconv.Itoa(sz), func(b *testing.B) {
289 for i := 0; i < b.N; i++ {
290 b.SetBytes(int64(sz))
291 unmarshalCorpusFile(data)
292 }
293 })
294 }
295 }
296
297 func TestByteRoundTrip(t *testing.T) {
298 for x := 0; x < 256; x++ {
299 b1 := byte(x)
300 buf := marshalCorpusFile(b1)
301 vs, err := unmarshalCorpusFile(buf)
302 if err != nil {
303 t.Fatal(err)
304 }
305 b2 := vs[0].(byte)
306 if b2 != b1 {
307 t.Fatalf("unmarshaled %v, want %v:\n%s", b2, b1, buf)
308 }
309 }
310 }
311
312 func TestInt8RoundTrip(t *testing.T) {
313 for x := -128; x < 128; x++ {
314 i1 := int8(x)
315 buf := marshalCorpusFile(i1)
316 vs, err := unmarshalCorpusFile(buf)
317 if err != nil {
318 t.Fatal(err)
319 }
320 i2 := vs[0].(int8)
321 if i2 != i1 {
322 t.Fatalf("unmarshaled %v, want %v:\n%s", i2, i1, buf)
323 }
324 }
325 }
326
327 func FuzzFloat64RoundTrip(f *testing.F) {
328 f.Add(math.Float64bits(0))
329 f.Add(math.Float64bits(math.Copysign(0, -1)))
330 f.Add(math.Float64bits(math.MaxFloat64))
331 f.Add(math.Float64bits(math.SmallestNonzeroFloat64))
332 f.Add(math.Float64bits(math.NaN()))
333 f.Add(uint64(0x7FF0000000000001))
334 f.Add(math.Float64bits(math.Inf(1)))
335 f.Add(math.Float64bits(math.Inf(-1)))
336
337 f.Fuzz(func(t *testing.T, u1 uint64) {
338 x1 := math.Float64frombits(u1)
339
340 b := marshalCorpusFile(x1)
341 t.Logf("marshaled math.Float64frombits(0x%x):\n%s", u1, b)
342
343 xs, err := unmarshalCorpusFile(b)
344 if err != nil {
345 t.Fatal(err)
346 }
347 if len(xs) != 1 {
348 t.Fatalf("unmarshaled %d values", len(xs))
349 }
350 x2 := xs[0].(float64)
351 u2 := math.Float64bits(x2)
352 if u2 != u1 {
353 t.Errorf("unmarshaled %v (bits 0x%x)", x2, u2)
354 }
355 })
356 }
357
358 func FuzzRuneRoundTrip(f *testing.F) {
359 f.Add(rune(-1))
360 f.Add(rune(0xd800))
361 f.Add(rune(0xdfff))
362 f.Add(rune(unicode.ReplacementChar))
363 f.Add(rune(unicode.MaxASCII))
364 f.Add(rune(unicode.MaxLatin1))
365 f.Add(rune(unicode.MaxRune))
366 f.Add(rune(unicode.MaxRune + 1))
367 f.Add(rune(-0x80000000))
368 f.Add(rune(0x7fffffff))
369
370 f.Fuzz(func(t *testing.T, r1 rune) {
371 b := marshalCorpusFile(r1)
372 t.Logf("marshaled rune(0x%x):\n%s", r1, b)
373
374 rs, err := unmarshalCorpusFile(b)
375 if err != nil {
376 t.Fatal(err)
377 }
378 if len(rs) != 1 {
379 t.Fatalf("unmarshaled %d values", len(rs))
380 }
381 r2 := rs[0].(rune)
382 if r2 != r1 {
383 t.Errorf("unmarshaled rune(0x%x)", r2)
384 }
385 })
386 }
387
388 func FuzzStringRoundTrip(f *testing.F) {
389 f.Add("")
390 f.Add("\x00")
391 f.Add(string([]rune{unicode.ReplacementChar}))
392
393 f.Fuzz(func(t *testing.T, s1 string) {
394 b := marshalCorpusFile(s1)
395 t.Logf("marshaled %q:\n%s", s1, b)
396
397 rs, err := unmarshalCorpusFile(b)
398 if err != nil {
399 t.Fatal(err)
400 }
401 if len(rs) != 1 {
402 t.Fatalf("unmarshaled %d values", len(rs))
403 }
404 s2 := rs[0].(string)
405 if s2 != s1 {
406 t.Errorf("unmarshaled %q", s2)
407 }
408 })
409 }
410
View as plain text