Browse Source

Merge pull request #4 from stevenjohnstone/dos-fixes

Make library robust to malformed input
Evan Huus 7 years ago
parent
commit
040cc1a32f
36 changed files with 105 additions and 8 deletions
  1. BIN
      corpus/020dfb19a68cbcf99dc93dc1030068d4c9968ad0-2
  2. 1 0
      corpus/05979b224be0294bf350310d4ba5257c9bb815db-3
  3. BIN
      corpus/0e64ca2823923c5efa03ff2bd6e0aa1018eeca3b-9
  4. BIN
      corpus/1
  5. BIN
      corpus/361a1c6d2a8f80780826c3d83ad391d0475c922f-4
  6. BIN
      corpus/4117af68228fa64339d362cf980c68ffadff96c8-12
  7. BIN
      corpus/4142249be82c8a617cf838eef05394ece39becd3-9
  8. BIN
      corpus/41ea8c7d904f1cd913b52e9ead4a96c639d76802-10
  9. BIN
      corpus/44083e1447694980c0ee682576e32358c9ee883f-2
  10. BIN
      corpus/4d6b359bd538feaa7d36c89235d07d0a443797ac-1
  11. 1 0
      corpus/521e7e67b6063a75e0eeb24b0d1dd20731d34ad8-4
  12. BIN
      corpus/526e6f85d1b8777f0d9f70634c9f8b77fbdccdff-7
  13. 1 0
      corpus/581b8fe7088f921567811fdf30e1f527c9f48e5e
  14. BIN
      corpus/60cd10738158020f5843b43960158c3d116b3a71-11
  15. BIN
      corpus/652b031b4b9d601235f86ef62523e63d733b8623-3
  16. BIN
      corpus/684a011f6fdfc7ae9863e12381165e82d2a2e356-9
  17. BIN
      corpus/72e42fc8e5eaed6a8a077f420fc3bd1f9a7c0919-1
  18. 1 0
      corpus/80881d1b911b95e0203b3b0e7dc6360c35f7620f-7
  19. BIN
      corpus/8484b3082d522e0a1f315db1fa1b2a5118be7cc3-8
  20. BIN
      corpus/9635bb09260f100bc4a2ee4e3b980fecc5b874ce-1
  21. BIN
      corpus/99d36b0b5b1be7151a508dd440ec725a2576c41c-1
  22. BIN
      corpus/9d339eddb4e2714ea319c3fb571311cb95fdb067-6
  23. 1 0
      corpus/b2419fcb7a9aef359de67cb6bd2b8a8c1f5c100f-4
  24. BIN
      corpus/c1951b29109ec1017f63535ce3699630f46f54e1-5
  25. 1 0
      corpus/cb806bc4f67316af02d6ae677332a3b6005a18da-5
  26. BIN
      corpus/cd7dd228703739e9252c7ea76f1c5f82ab44686a-10
  27. BIN
      corpus/ce3671e91907349cea04fc3f2a4b91c65b99461d-3
  28. 1 0
      corpus/ce3c6f4c31f74d72fbf74c17d14a8d29aa62059e-6
  29. 0 0
      corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709-1
  30. BIN
      corpus/e2230aa0ecaebb9b890440effa13f501a89247b2-1
  31. BIN
      corpus/efa11d676fb2a77afb8eac3d7ed30e330a7c2efe-11
  32. BIN
      corpus/f0445ac39e03978bbc8011316ac8468015ddb72c-1
  33. BIN
      corpus/f241da53c6bc1fe3368c55bf28db86ce15a2c784-2
  34. 16 0
      fuzz.go
  35. 36 8
      snappy.go
  36. 46 0
      snappy_test.go

BIN
corpus/020dfb19a68cbcf99dc93dc1030068d4c9968ad0-2


+ 1 - 0
corpus/05979b224be0294bf350310d4ba5257c9bb815db-3

@@ -0,0 +1 @@
+�ï¿ï¿½ï¿½Y

BIN
corpus/0e64ca2823923c5efa03ff2bd6e0aa1018eeca3b-9


BIN
corpus/1


BIN
corpus/361a1c6d2a8f80780826c3d83ad391d0475c922f-4


BIN
corpus/4117af68228fa64339d362cf980c68ffadff96c8-12


BIN
corpus/4142249be82c8a617cf838eef05394ece39becd3-9


BIN
corpus/41ea8c7d904f1cd913b52e9ead4a96c639d76802-10


BIN
corpus/44083e1447694980c0ee682576e32358c9ee883f-2


BIN
corpus/4d6b359bd538feaa7d36c89235d07d0a443797ac-1


+ 1 - 0
corpus/521e7e67b6063a75e0eeb24b0d1dd20731d34ad8-4

@@ -0,0 +1 @@
+�ï¿ï¿½¿ï¿½ï�Y

BIN
corpus/526e6f85d1b8777f0d9f70634c9f8b77fbdccdff-7


+ 1 - 0
corpus/581b8fe7088f921567811fdf30e1f527c9f48e5e

@@ -0,0 +1 @@
+package 

BIN
corpus/60cd10738158020f5843b43960158c3d116b3a71-11


BIN
corpus/652b031b4b9d601235f86ef62523e63d733b8623-3


BIN
corpus/684a011f6fdfc7ae9863e12381165e82d2a2e356-9


BIN
corpus/72e42fc8e5eaed6a8a077f420fc3bd1f9a7c0919-1


+ 1 - 0
corpus/80881d1b911b95e0203b3b0e7dc6360c35f7620f-7

@@ -0,0 +1 @@
+箭쏙여쏙울�슴占쏙옙占全�슴占㈌占㈌占�슴占쏙옙占全�슴占쏘울苑楮珽옙

BIN
corpus/8484b3082d522e0a1f315db1fa1b2a5118be7cc3-8


BIN
corpus/9635bb09260f100bc4a2ee4e3b980fecc5b874ce-1


BIN
corpus/99d36b0b5b1be7151a508dd440ec725a2576c41c-1


BIN
corpus/9d339eddb4e2714ea319c3fb571311cb95fdb067-6


+ 1 - 0
corpus/b2419fcb7a9aef359de67cb6bd2b8a8c1f5c100f-4

@@ -0,0 +1 @@
+½ï¿ï¿½ï¿½Y

BIN
corpus/c1951b29109ec1017f63535ce3699630f46f54e1-5


+ 1 - 0
corpus/cb806bc4f67316af02d6ae677332a3b6005a18da-5

@@ -0,0 +1 @@
+ο©½οΏο�½ΏοΏ½ξΏοζΏ½

BIN
corpus/cd7dd228703739e9252c7ea76f1c5f82ab44686a-10


BIN
corpus/ce3671e91907349cea04fc3f2a4b91c65b99461d-3


+ 1 - 0
corpus/ce3c6f4c31f74d72fbf74c17d14a8d29aa62059e-6

@@ -0,0 +1 @@
+箭쏙여쏙울�슴占쏙옙占全�슴占쏘울轅�

+ 0 - 0
corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709-1


BIN
corpus/e2230aa0ecaebb9b890440effa13f501a89247b2-1


BIN
corpus/efa11d676fb2a77afb8eac3d7ed30e330a7c2efe-11


BIN
corpus/f0445ac39e03978bbc8011316ac8468015ddb72c-1


BIN
corpus/f241da53c6bc1fe3368c55bf28db86ce15a2c784-2


+ 16 - 0
fuzz.go

@@ -0,0 +1,16 @@
+// +build gofuzz
+
+package snappy
+
+func Fuzz(data []byte) int {
+	decode, err := Decode(data)
+	if decode == nil && err == nil {
+		panic("nil error with nil result")
+	}
+
+	if err != nil {
+		return 0
+	}
+
+	return 1
+}

+ 36 - 8
snappy.go

@@ -3,11 +3,22 @@ package snappy
 import (
 	"bytes"
 	"encoding/binary"
+	"errors"
 
 	master "github.com/golang/snappy"
 )
 
-var xerialHeader = []byte{130, 83, 78, 65, 80, 80, 89, 0}
+const (
+	sizeOffset = 16
+	sizeBytes  = 4
+)
+
+var (
+	xerialHeader = []byte{130, 83, 78, 65, 80, 80, 89, 0}
+	// ErrMalformed is returned by the decoder when the xerial framing
+	// is malformed
+	ErrMalformed = errors.New("malformed xerial framing")
+)
 
 // Encode encodes data as snappy with no framing header.
 func Encode(src []byte) []byte {
@@ -17,26 +28,43 @@ func Encode(src []byte) []byte {
 // Decode decodes snappy data whether it is traditional unframed
 // or includes the xerial framing format.
 func Decode(src []byte) ([]byte, error) {
+	var max = len(src)
+	if max < len(xerialHeader) {
+		return nil, ErrMalformed
+	}
+
 	if !bytes.Equal(src[:8], xerialHeader) {
 		return master.Decode(nil, src)
 	}
 
+	if max < sizeOffset+sizeBytes {
+		return nil, ErrMalformed
+	}
+
 	var (
-		pos   = uint32(16)
-		max   = uint32(len(src))
+		pos   = sizeOffset
 		dst   = make([]byte, 0, len(src))
 		chunk []byte
 		err   error
 	)
-	for pos < max {
-		size := binary.BigEndian.Uint32(src[pos : pos+4])
-		pos += 4
 
-		chunk, err = master.Decode(chunk, src[pos:pos+size])
+	for pos+sizeBytes <= max {
+		size := int(binary.BigEndian.Uint32(src[pos : pos+sizeBytes]))
+		pos += sizeBytes
+
+		nextPos := pos + size
+		// On architectures where int is 32-bytes wide size + pos could
+		// overflow so we need to check the low bound as well as the
+		// high
+		if nextPos < pos || nextPos > max {
+			return nil, ErrMalformed
+		}
+
+		chunk, err = master.Decode(chunk, src[pos:nextPos])
 		if err != nil {
 			return nil, err
 		}
-		pos += size
+		pos = nextPos
 		dst = append(dst, chunk...)
 	}
 	return dst, nil

+ 46 - 0
snappy_test.go

@@ -47,3 +47,49 @@ func TestSnappyDecodeStreams(t *testing.T) {
 		}
 	}
 }
+
+func TestSnappyDecodeMalformedTruncatedHeader(t *testing.T) {
+	// Truncated headers should not cause a panic.
+	for i := 0; i < len(xerialHeader); i++ {
+		buf := make([]byte, i)
+		copy(buf, xerialHeader[:i])
+		if _, err := Decode(buf); err != ErrMalformed {
+			t.Errorf("expected ErrMalformed got %v", err)
+		}
+	}
+}
+
+func TestSnappyDecodeMalformedTruncatedSize(t *testing.T) {
+	// Inputs with valid Xerial header but truncated "size" field
+	sizes := []int{sizeOffset + 1, sizeOffset + 2, sizeOffset + 3}
+	for _, size := range sizes {
+		buf := make([]byte, size)
+		copy(buf, xerialHeader)
+		if _, err := Decode(buf); err != ErrMalformed {
+			t.Errorf("expected ErrMalformed got %v", err)
+		}
+	}
+}
+
+func TestSnappyDecodeMalformedBNoData(t *testing.T) {
+	// No data after the size field
+	buf := make([]byte, 20)
+	copy(buf, xerialHeader)
+	// indicate that there's one byte of data to be read
+	buf[len(buf)-1] = 1
+	if _, err := Decode(buf); err != ErrMalformed {
+		t.Errorf("expected ErrMalformed got %v", err)
+	}
+}
+
+func TestSnappyMasterDecodeFailed(t *testing.T) {
+	buf := make([]byte, 21)
+	copy(buf, xerialHeader)
+	// indicate that there's one byte of data to be read
+	buf[len(buf)-2] = 1
+	// A payload which will not decode
+	buf[len(buf)-1] = 1
+	if _, err := Decode(buf); err == ErrMalformed || err == nil {
+		t.Errorf("unexpected err: %v", err)
+	}
+}