github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/internal/zstd/fuzz_test.go (about) 1 // Copyright 2023 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 zstd 6 7 import ( 8 "bytes" 9 "io" 10 "os" 11 "os/exec" 12 "testing" 13 ) 14 15 // badStrings is some inputs that FuzzReader failed on earlier. 16 var badStrings = []string{ 17 "(\xb5/\xfdd00,\x05\x00\xc4\x0400000000000000000000000000000000000000000000000000000000000000000000000000000 \xa07100000000000000000000000000000000000000000000000000000000000000000000000000aM\x8a2y0B\b", 18 "(\xb5/\xfd00$\x05\x0020 00X70000a70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 19 "(\xb5/\xfd00$\x05\x0020 00B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 20 "(\xb5/\xfd00}\x00\x0020\x00\x9000000000000", 21 "(\xb5/\xfd00}\x00\x00&0\x02\x830!000000000", 22 "(\xb5/\xfd\x1002000$\x05\x0010\xcc0\xa8100000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 23 "(\xb5/\xfd\x1002000$\x05\x0000\xcc0\xa8100d\x0000001000000000000000000000000000000000000000000000000000000000000000000000000\x000000000000000000000000000000000000000000000000000000000000000000000000000000", 24 "(\xb5/\xfd001\x00\x0000000000000000000", 25 } 26 27 // This is a simple fuzzer to see if the decompressor panics. 28 func FuzzReader(f *testing.F) { 29 for _, test := range tests { 30 f.Add([]byte(test.compressed)) 31 } 32 for _, s := range badStrings { 33 f.Add([]byte(s)) 34 } 35 f.Fuzz(func(t *testing.T, b []byte) { 36 r := NewReader(bytes.NewReader(b)) 37 io.Copy(io.Discard, r) 38 }) 39 } 40 41 // Fuzz test to verify that what we decompress is what we compress. 42 // This isn't a great fuzz test because the fuzzer can't efficiently 43 // explore the space of decompressor behavior, since it can't see 44 // what the compressor is doing. But it's better than nothing. 45 func FuzzDecompressor(f *testing.F) { 46 if _, err := os.Stat("/usr/bin/zstd"); err != nil { 47 f.Skip("skipping because /usr/bin/zstd does not exist") 48 } 49 50 for _, test := range tests { 51 f.Add([]byte(test.uncompressed)) 52 } 53 54 // Add some larger data, as that has more interesting compression. 55 f.Add(bytes.Repeat([]byte("abcdefghijklmnop"), 256)) 56 var buf bytes.Buffer 57 for i := 0; i < 256; i++ { 58 buf.WriteByte(byte(i)) 59 } 60 f.Add(bytes.Repeat(buf.Bytes(), 64)) 61 f.Add(bigData(f)) 62 63 f.Fuzz(func(t *testing.T, b []byte) { 64 cmd := exec.Command("/usr/bin/zstd", "-z") 65 cmd.Stdin = bytes.NewReader(b) 66 var compressed bytes.Buffer 67 cmd.Stdout = &compressed 68 cmd.Stderr = os.Stderr 69 if err := cmd.Run(); err != nil { 70 t.Errorf("running zstd failed: %v", err) 71 } 72 73 r := NewReader(bytes.NewReader(compressed.Bytes())) 74 got, err := io.ReadAll(r) 75 if err != nil { 76 t.Fatal(err) 77 } 78 if !bytes.Equal(got, b) { 79 showDiffs(t, got, b) 80 } 81 }) 82 } 83 84 // Fuzz test to check that if we can decompress some data, 85 // so can zstd, and that we get the same result. 86 func FuzzReverse(f *testing.F) { 87 if _, err := os.Stat("/usr/bin/zstd"); err != nil { 88 f.Skip("skipping because /usr/bin/zstd does not exist") 89 } 90 91 for _, test := range tests { 92 f.Add([]byte(test.compressed)) 93 } 94 95 // Set a hook to reject some cases where we don't match zstd. 96 fuzzing = true 97 defer func() { fuzzing = false }() 98 99 f.Fuzz(func(t *testing.T, b []byte) { 100 r := NewReader(bytes.NewReader(b)) 101 goExp, goErr := io.ReadAll(r) 102 103 cmd := exec.Command("/usr/bin/zstd", "-d") 104 cmd.Stdin = bytes.NewReader(b) 105 var uncompressed bytes.Buffer 106 cmd.Stdout = &uncompressed 107 cmd.Stderr = os.Stderr 108 zstdErr := cmd.Run() 109 zstdExp := uncompressed.Bytes() 110 111 if goErr == nil && zstdErr == nil { 112 if !bytes.Equal(zstdExp, goExp) { 113 showDiffs(t, zstdExp, goExp) 114 } 115 } else { 116 // Ideally we should check that this package and 117 // the zstd program both fail or both succeed, 118 // and that if they both fail one byte sequence 119 // is an exact prefix of the other. 120 // Actually trying this proved to be frustrating, 121 // as the zstd program appears to accept invalid 122 // byte sequences using rules that are difficult 123 // to determine. 124 // So we just check the prefix. 125 126 c := len(goExp) 127 if c > len(zstdExp) { 128 c = len(zstdExp) 129 } 130 goExp = goExp[:c] 131 zstdExp = zstdExp[:c] 132 if !bytes.Equal(goExp, zstdExp) { 133 t.Error("byte mismatch after error") 134 t.Logf("Go error: %v\n", goErr) 135 t.Logf("zstd error: %v\n", zstdErr) 136 showDiffs(t, zstdExp, goExp) 137 } 138 } 139 }) 140 }