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