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 newB[len(newB)-1] != '\n' {
237 t.Error("didn't write final newline to corpus file")
238 }
239
240 want := test.want
241 if want == "" {
242 want = test.in
243 }
244 want += "\n"
245 got := string(newB)
246 if got != want {
247 t.Errorf("unexpected marshaled value\ngot:\n%s\nwant:\n%s", got, want)
248 }
249 })
250 }
251 }
252
253
254
255
256 func BenchmarkMarshalCorpusFile(b *testing.B) {
257 buf := make([]byte, 1024*1024)
258 for i := 0; i < len(buf); i++ {
259 buf[i] = byte(i)
260 }
261
262 for sz := 1; sz <= len(buf); sz <<= 1 {
263 sz := sz
264 b.Run(strconv.Itoa(sz), func(b *testing.B) {
265 for i := 0; i < b.N; i++ {
266 b.SetBytes(int64(sz))
267 marshalCorpusFile(buf[:sz])
268 }
269 })
270 }
271 }
272
273
274
275
276 func BenchmarkUnmarshalCorpusFile(b *testing.B) {
277 buf := make([]byte, 1024*1024)
278 for i := 0; i < len(buf); i++ {
279 buf[i] = byte(i)
280 }
281
282 for sz := 1; sz <= len(buf); sz <<= 1 {
283 sz := sz
284 data := marshalCorpusFile(buf[:sz])
285 b.Run(strconv.Itoa(sz), func(b *testing.B) {
286 for i := 0; i < b.N; i++ {
287 b.SetBytes(int64(sz))
288 unmarshalCorpusFile(data)
289 }
290 })
291 }
292 }
293
294 func TestByteRoundTrip(t *testing.T) {
295 for x := 0; x < 256; x++ {
296 b1 := byte(x)
297 buf := marshalCorpusFile(b1)
298 vs, err := unmarshalCorpusFile(buf)
299 if err != nil {
300 t.Fatal(err)
301 }
302 b2 := vs[0].(byte)
303 if b2 != b1 {
304 t.Fatalf("unmarshaled %v, want %v:\n%s", b2, b1, buf)
305 }
306 }
307 }
308
309 func TestInt8RoundTrip(t *testing.T) {
310 for x := -128; x < 128; x++ {
311 i1 := int8(x)
312 buf := marshalCorpusFile(i1)
313 vs, err := unmarshalCorpusFile(buf)
314 if err != nil {
315 t.Fatal(err)
316 }
317 i2 := vs[0].(int8)
318 if i2 != i1 {
319 t.Fatalf("unmarshaled %v, want %v:\n%s", i2, i1, buf)
320 }
321 }
322 }
323
324 func FuzzFloat64RoundTrip(f *testing.F) {
325 f.Add(math.Float64bits(0))
326 f.Add(math.Float64bits(math.Copysign(0, -1)))
327 f.Add(math.Float64bits(math.MaxFloat64))
328 f.Add(math.Float64bits(math.SmallestNonzeroFloat64))
329 f.Add(math.Float64bits(math.NaN()))
330 f.Add(uint64(0x7FF0000000000001))
331 f.Add(math.Float64bits(math.Inf(1)))
332 f.Add(math.Float64bits(math.Inf(-1)))
333
334 f.Fuzz(func(t *testing.T, u1 uint64) {
335 x1 := math.Float64frombits(u1)
336
337 b := marshalCorpusFile(x1)
338 t.Logf("marshaled math.Float64frombits(0x%x):\n%s", u1, b)
339
340 xs, err := unmarshalCorpusFile(b)
341 if err != nil {
342 t.Fatal(err)
343 }
344 if len(xs) != 1 {
345 t.Fatalf("unmarshaled %d values", len(xs))
346 }
347 x2 := xs[0].(float64)
348 u2 := math.Float64bits(x2)
349 if u2 != u1 {
350 t.Errorf("unmarshaled %v (bits 0x%x)", x2, u2)
351 }
352 })
353 }
354
355 func FuzzRuneRoundTrip(f *testing.F) {
356 f.Add(rune(-1))
357 f.Add(rune(0xd800))
358 f.Add(rune(0xdfff))
359 f.Add(rune(unicode.ReplacementChar))
360 f.Add(rune(unicode.MaxASCII))
361 f.Add(rune(unicode.MaxLatin1))
362 f.Add(rune(unicode.MaxRune))
363 f.Add(rune(unicode.MaxRune + 1))
364 f.Add(rune(-0x80000000))
365 f.Add(rune(0x7fffffff))
366
367 f.Fuzz(func(t *testing.T, r1 rune) {
368 b := marshalCorpusFile(r1)
369 t.Logf("marshaled rune(0x%x):\n%s", r1, b)
370
371 rs, err := unmarshalCorpusFile(b)
372 if err != nil {
373 t.Fatal(err)
374 }
375 if len(rs) != 1 {
376 t.Fatalf("unmarshaled %d values", len(rs))
377 }
378 r2 := rs[0].(rune)
379 if r2 != r1 {
380 t.Errorf("unmarshaled rune(0x%x)", r2)
381 }
382 })
383 }
384
385 func FuzzStringRoundTrip(f *testing.F) {
386 f.Add("")
387 f.Add("\x00")
388 f.Add(string([]rune{unicode.ReplacementChar}))
389
390 f.Fuzz(func(t *testing.T, s1 string) {
391 b := marshalCorpusFile(s1)
392 t.Logf("marshaled %q:\n%s", s1, b)
393
394 rs, err := unmarshalCorpusFile(b)
395 if err != nil {
396 t.Fatal(err)
397 }
398 if len(rs) != 1 {
399 t.Fatalf("unmarshaled %d values", len(rs))
400 }
401 s2 := rs[0].(string)
402 if s2 != s1 {
403 t.Errorf("unmarshaled %q", s2)
404 }
405 })
406 }
407
View as plain text