github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/internal/fuzz/encoding_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 // if different from in 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 // The two IEEE 754 bit patterns used for the math.Float{64,32}frombits 127 // encodings are non-math.NAN quiet-NaN values. Since they are not equal 128 // to math.NaN(), they should be re-encoded to their bit patterns. They 129 // are, respectively: 130 // * math.Float64bits(math.NaN())+1 131 // * math.Float32bits(float32(math.NaN()))+1 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 // Although we arbitrarily choose default integer bases (0 or 16), we may 147 // want to change those arbitrary choices in the future and should not 148 // break the parser. Verify that integers in the opposite bases still 149 // parse correctly. 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 // BenchmarkMarshalCorpusFile measures the time it takes to serialize byte 254 // slices of various sizes to a corpus file. The slice contains a repeating 255 // sequence of bytes 0-255 to mix escaped and non-escaped characters. 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 // BenchmarkUnmarshalCorpusfile measures the time it takes to deserialize 274 // files encoding byte slices of various sizes. The slice contains a repeating 275 // sequence of bytes 0-255 to mix escaped and non-escaped characters. 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)) // signaling NaN 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 }