diff --git a/s2/encode_test.go b/s2/encode_test.go index c0b05057eb..29acebce70 100644 --- a/s2/encode_test.go +++ b/s2/encode_test.go @@ -87,39 +87,58 @@ func TestEncoderRegression(t *testing.T) { if testing.Short() && len(data) > 10000 { t.SkipNow() } + var blocksTested bool for name, opts := range testOptions(t) { t.Run(name, func(t *testing.T) { var buf bytes.Buffer dec := NewReader(nil) enc := NewWriter(&buf, opts...) - comp := Encode(make([]byte, MaxEncodedLen(len(data))), data) - decoded, err := Decode(nil, comp) - if err != nil { - t.Error(err) - return - } - if !bytes.Equal(data, decoded) { - t.Error("block decoder mismatch") - return - } - if mel := MaxEncodedLen(len(data)); len(comp) > mel { - t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) - return - } - comp = EncodeBetter(make([]byte, MaxEncodedLen(len(data))), data) - decoded, err = Decode(nil, comp) - if err != nil { - t.Error(err) - return - } - if !bytes.Equal(data, decoded) { - t.Error("block decoder mismatch") - return - } - if mel := MaxEncodedLen(len(data)); len(comp) > mel { - t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) - return + if !blocksTested { + comp := Encode(make([]byte, MaxEncodedLen(len(data))), data) + decoded, err := Decode(nil, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + comp = EncodeBetter(make([]byte, MaxEncodedLen(len(data))), data) + decoded, err = Decode(nil, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + + comp = EncodeBest(make([]byte, MaxEncodedLen(len(data))), data) + decoded, err = Decode(nil, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + blocksTested = true } // Test writer. @@ -143,7 +162,7 @@ func TestEncoderRegression(t *testing.T) { t.Error(err) return } - comp = buf.Bytes() + comp := buf.Bytes() if enc.pad > 0 && len(comp)%enc.pad != 0 { t.Error(fmt.Errorf("wanted size to be mutiple of %d, got size %d with remainder %d", enc.pad, len(comp), len(comp)%enc.pad)) return diff --git a/s2/fuzz_test.go b/s2/fuzz_test.go new file mode 100644 index 0000000000..830d3656d6 --- /dev/null +++ b/s2/fuzz_test.go @@ -0,0 +1,130 @@ +//go:build go1.18 +// +build go1.18 + +package s2 + +import ( + "bytes" + "fmt" + "testing" + + "github.com/klauspost/compress/internal/fuzz" + "github.com/klauspost/compress/internal/snapref" +) + +func FuzzEncodingBlocks(f *testing.F) { + fuzz.AddFromZip(f, "testdata/enc_regressions.zip", true, false) + fuzz.AddFromZip(f, "testdata/fuzz/block-corpus-raw.zip", true, testing.Short()) + fuzz.AddFromZip(f, "testdata/fuzz/block-corpus-enc.zip", false, testing.Short()) + + // Fuzzing tweaks: + const ( + // Max input size: + maxSize = 8 << 20 + ) + + f.Fuzz(func(t *testing.T, data []byte) { + if len(data) > maxSize { + return + } + + writeDst := make([]byte, MaxEncodedLen(len(data)), MaxEncodedLen(len(data))+4) + writeDst = append(writeDst, 1, 2, 3, 4) + defer func() { + got := writeDst[MaxEncodedLen(len(data)):] + want := []byte{1, 2, 3, 4} + if !bytes.Equal(got, want) { + t.Fatalf("want %v, got %v - dest modified outside cap", want, got) + } + }() + compDst := writeDst[:MaxEncodedLen(len(data)):MaxEncodedLen(len(data))] // Hard cap + decDst := make([]byte, len(data)) + comp := Encode(compDst, data) + decoded, err := Decode(decDst, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + comp = EncodeBetter(compDst, data) + decoded, err = Decode(decDst, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + + comp = EncodeBest(compDst, data) + decoded, err = Decode(decDst, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + + comp = EncodeSnappy(compDst, data) + decoded, err = snapref.Decode(decDst, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + comp = EncodeSnappyBetter(compDst, data) + decoded, err = snapref.Decode(decDst, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + + comp = EncodeSnappyBest(compDst, data) + decoded, err = snapref.Decode(decDst, comp) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(data, decoded) { + t.Error("block decoder mismatch") + return + } + if mel := MaxEncodedLen(len(data)); len(comp) > mel { + t.Error(fmt.Errorf("MaxEncodedLen Exceed: input: %d, mel: %d, got %d", len(data), mel, len(comp))) + return + } + }) +} diff --git a/s2/testdata/fuzz/block-corpus-enc.zip b/s2/testdata/fuzz/block-corpus-enc.zip new file mode 100644 index 0000000000..1e44ebcefb Binary files /dev/null and b/s2/testdata/fuzz/block-corpus-enc.zip differ diff --git a/s2/testdata/fuzz/block-corpus-raw.zip b/s2/testdata/fuzz/block-corpus-raw.zip new file mode 100644 index 0000000000..c18dd54e72 Binary files /dev/null and b/s2/testdata/fuzz/block-corpus-raw.zip differ