github.com/JimmyHuang454/JLS-go@v0.0.0-20230831150107-90d536585ba0/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 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 // BenchmarkMarshalCorpusFile measures the time it takes to serialize byte 257 // slices of various sizes to a corpus file. The slice contains a repeating 258 // sequence of bytes 0-255 to mix escaped and non-escaped characters. 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 // BenchmarkUnmarshalCorpusfile measures the time it takes to deserialize 277 // files encoding byte slices of various sizes. The slice contains a repeating 278 // sequence of bytes 0-255 to mix escaped and non-escaped characters. 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)) // signaling NaN 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 }