diff --git a/tsdb/engine/tsm1/bool.go b/tsdb/engine/tsm1/bool.go index 0bb4708ec6..1ee39f379a 100644 --- a/tsdb/engine/tsm1/bool.go +++ b/tsdb/engine/tsm1/bool.go @@ -5,7 +5,10 @@ package tsm1 // how many booleans are packed in the slice. The remaining bytes contains 1 byte for every // 8 boolean values encoded. -import "encoding/binary" +import ( + "encoding/binary" + "fmt" +) const ( // booleanUncompressed is an uncompressed boolean format. @@ -97,27 +100,44 @@ type BooleanDecoder struct { // SetBytes initializes the decoder with a new set of bytes to read from. // This must be called before calling any other methods. func (e *BooleanDecoder) SetBytes(b []byte) { + if len(b) == 0 { + return + } + // First byte stores the encoding type, only have 1 bit-packet format // currently ignore for now. b = b[1:] count, n := binary.Uvarint(b) + if n <= 0 { + e.err = fmt.Errorf("BooleanDecoder: invalid count") + return + } e.b = b[n:] e.i = -1 e.n = int(count) + + if min := len(e.b) * 8; min < e.n { + // Shouldn't happen - TSM file was truncated/corrupted + e.n = min + } } func (e *BooleanDecoder) Next() bool { + if e.err != nil { + return false + } + e.i++ return e.i < e.n } func (e *BooleanDecoder) Read() bool { // Index into the byte slice - idx := e.i / 8 + idx := e.i >> 3 // integer division by 8 // Bit position - pos := (8 - e.i%8) - 1 + pos := 7 - (e.i & 0x7) // The mask to select the bit mask := byte(1 << uint(pos)) diff --git a/tsdb/engine/tsm1/bool_test.go b/tsdb/engine/tsm1/bool_test.go index 751abdf73d..0721830925 100644 --- a/tsdb/engine/tsm1/bool_test.go +++ b/tsdb/engine/tsm1/bool_test.go @@ -113,3 +113,49 @@ func Test_BooleanEncoder_Quick(t *testing.T) { t.Fatal(err) } } + +func Test_BooleanDecoder_Corrupt(t *testing.T) { + cases := []string{ + "", // Empty + "\x10\x90", // Packed: invalid count + "\x10\x7f", // Packed: count greater than remaining bits, multiple bytes expected + "\x10\x01", // Packed: count greater than remaining bits, one byte expected + } + + for _, c := range cases { + var dec tsm1.BooleanDecoder + dec.SetBytes([]byte(c)) + if dec.Next() { + t.Fatalf("exp next == false, got true for case %q", c) + } + } +} + +func BenchmarkBooleanDecoder_2048(b *testing.B) { benchmarkBooleanDecoder(b, 2048) } + +func benchmarkBooleanDecoder(b *testing.B, size int) { + e := tsm1.NewBooleanEncoder() + for i := 0; i < size; i++ { + e.Write(i&1 == 1) + } + bytes, err := e.Bytes() + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + var d tsm1.BooleanDecoder + d.SetBytes(bytes) + + var n int + for d.Next() { + _ = d.Read() + n++ + } + if n != size { + b.Fatalf("expected to read %d booleans, but read %d", size, n) + } + } +} diff --git a/tsdb/engine/tsm1/encoding.go b/tsdb/engine/tsm1/encoding.go index 9062fff899..f26a225d62 100644 --- a/tsdb/engine/tsm1/encoding.go +++ b/tsdb/engine/tsm1/encoding.go @@ -122,7 +122,10 @@ func BlockCount(block []byte) int { panic(fmt.Sprintf("count of short block: got %v, exp %v", len(block), encodedBlockHeaderSize)) } // first byte is the block type - tb, _ := unpackBlock(block[1:]) + tb, _, err := unpackBlock(block[1:]) + if err != nil { + panic(fmt.Sprintf("BlockCount: error unpacking block: %s", err.Error())) + } return CountTimestamps(tb) } @@ -255,7 +258,10 @@ func DecodeFloatBlock(block []byte, tdec *TimeDecoder, vdec *FloatDecoder, a *[] } block = block[1:] - tb, vb := unpackBlock(block) + tb, vb, err := unpackBlock(block) + if err != nil { + return nil, err + } // Setup our timestamp and value decoders tdec.Init(tb) @@ -356,7 +362,10 @@ func DecodeBooleanBlock(block []byte, tdec *TimeDecoder, vdec *BooleanDecoder, a } block = block[1:] - tb, vb := unpackBlock(block) + tb, vb, err := unpackBlock(block) + if err != nil { + return nil, err + } // Setup our timestamp and value decoders tdec.Init(tb) @@ -443,7 +452,10 @@ func DecodeIntegerBlock(block []byte, tdec *TimeDecoder, vdec *IntegerDecoder, a block = block[1:] // The first 8 bytes is the minimum timestamp of the block - tb, vb := unpackBlock(block) + tb, vb, err := unpackBlock(block) + if err != nil { + return nil, err + } // Setup our timestamp and value decoders tdec.Init(tb) @@ -530,7 +542,10 @@ func DecodeStringBlock(block []byte, tdec *TimeDecoder, vdec *StringDecoder, a * block = block[1:] // The first 8 bytes is the minimum timestamp of the block - tb, vb := unpackBlock(block) + tb, vb, err := unpackBlock(block) + if err != nil { + return nil, err + } // Setup our timestamp and value decoders tdec.Init(tb) @@ -583,15 +598,24 @@ func packBlock(ts []byte, values []byte) []byte { return append(block, values...) } -func unpackBlock(buf []byte) (ts, values []byte) { +func unpackBlock(buf []byte) (ts, values []byte, err error) { // Unpack the timestamp block length tsLen, i := binary.Uvarint(buf) + if i <= 0 { + err = fmt.Errorf("unpackBlock: unable to read timestamp block length") + return + } // Unpack the timestamp bytes - ts = buf[int(i) : int(i)+int(tsLen)] + tsIdx := int(i) + int(tsLen) + if tsIdx > len(buf) { + err = fmt.Errorf("unpackBlock: not enough data for timestamp") + return + } + ts = buf[int(i):tsIdx] // Unpack the value bytes - values = buf[int(i)+int(tsLen):] + values = buf[tsIdx:] return } diff --git a/tsdb/engine/tsm1/float.go b/tsdb/engine/tsm1/float.go index c6a5b74814..bedd006516 100644 --- a/tsdb/engine/tsm1/float.go +++ b/tsdb/engine/tsm1/float.go @@ -142,13 +142,19 @@ type FloatDecoder struct { // SetBytes initializes the decoder with b. Must call before calling Next(). func (it *FloatDecoder) SetBytes(b []byte) error { - // first byte is the compression type. - // we currently just have gorilla compression. - it.br.Reset(b[1:]) + var v uint64 + if len(b) == 0 { + v = uvnan + } else { + // first byte is the compression type. + // we currently just have gorilla compression. + it.br.Reset(b[1:]) - v, err := it.br.ReadBits(64) - if err != nil { - return err + var err error + v, err = it.br.ReadBits(64) + if err != nil { + return err + } } // Reset all fields. diff --git a/tsdb/engine/tsm1/float_test.go b/tsdb/engine/tsm1/float_test.go index c30ae34524..6327cec42b 100644 --- a/tsdb/engine/tsm1/float_test.go +++ b/tsdb/engine/tsm1/float_test.go @@ -239,6 +239,17 @@ func Test_FloatEncoder_Quick(t *testing.T) { }, nil) } +func TestFloatDecoder_Empty(t *testing.T) { + var dec tsm1.FloatDecoder + if err := dec.SetBytes([]byte{}); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if dec.Next() { + t.Fatalf("exp next == false, got true") + } +} + func BenchmarkFloatEncoder(b *testing.B) { for i := 0; i < b.N; i++ { s := tsm1.NewFloatEncoder() diff --git a/tsdb/engine/tsm1/int.go b/tsdb/engine/tsm1/int.go index 5b7fdc4de0..8da99d61a3 100644 --- a/tsdb/engine/tsm1/int.go +++ b/tsdb/engine/tsm1/int.go @@ -193,7 +193,7 @@ func (d *IntegerDecoder) Next() bool { d.err = fmt.Errorf("unknown encoding %v", d.encoding) } } - return d.i < d.n + return d.err == nil && d.i < d.n } func (d *IntegerDecoder) Error() error { @@ -219,6 +219,11 @@ func (d *IntegerDecoder) decodeRLE() { return } + if len(d.bytes) < 8 { + d.err = fmt.Errorf("IntegerDecoder: not enough data to decode RLE starting value") + return + } + var i, n int // Next 8 bytes is the starting value @@ -227,11 +232,18 @@ func (d *IntegerDecoder) decodeRLE() { // Next 1-10 bytes is the delta value value, n := binary.Uvarint(d.bytes[i:]) - + if n <= 0 { + d.err = fmt.Errorf("IntegerDecoder: invalid RLE delta value") + return + } i += n // Last 1-10 bytes is how many times the value repeats count, n := binary.Uvarint(d.bytes[i:]) + if n <= 0 { + d.err = fmt.Errorf("IntegerDecoder: invalid RLE repeat value") + return + } // Store the first value and delta value so we do not need to allocate // a large values slice. We can compute the value at position d.i on @@ -250,6 +262,11 @@ func (d *IntegerDecoder) decodePacked() { return } + if len(d.bytes) < 8 { + d.err = fmt.Errorf("IntegerDecoder: not enough data to decode packed value") + return + } + v := binary.BigEndian.Uint64(d.bytes[0:8]) // The first value is always unencoded if d.first { @@ -275,6 +292,11 @@ func (d *IntegerDecoder) decodeUncompressed() { return } + if len(d.bytes) < 8 { + d.err = fmt.Errorf("IntegerDecoder: not enough data to decode uncompressed value") + return + } + d.values[0] = binary.BigEndian.Uint64(d.bytes[0:8]) d.i = 0 d.n = 1 diff --git a/tsdb/engine/tsm1/int_test.go b/tsdb/engine/tsm1/int_test.go index 0b895dd8f5..8d8525d11d 100644 --- a/tsdb/engine/tsm1/int_test.go +++ b/tsdb/engine/tsm1/int_test.go @@ -464,6 +464,25 @@ func Test_IntegerEncoder_Quick(t *testing.T) { }, nil) } +func Test_IntegerDecoder_Corrupt(t *testing.T) { + cases := []string{ + "", // Empty + "\x00abc", // Uncompressed: less than 8 bytes + "\x10abc", // Packed: less than 8 bytes + "\x20abc", // RLE: less than 8 bytes + "\x2012345678\x90", // RLE: valid starting value but invalid delta value + "\x2012345678\x01\x90", // RLE: valid starting, valid delta value, invalid repeat value + } + + for _, c := range cases { + var dec IntegerDecoder + dec.SetBytes([]byte(c)) + if dec.Next() { + t.Fatalf("exp next == false, got true") + } + } +} + func BenchmarkIntegerEncoderRLE(b *testing.B) { enc := NewIntegerEncoder() x := make([]int64, 1024) diff --git a/tsdb/engine/tsm1/reader.go b/tsdb/engine/tsm1/reader.go index 01e3bd6cbf..237ff257db 100644 --- a/tsdb/engine/tsm1/reader.go +++ b/tsdb/engine/tsm1/reader.go @@ -596,10 +596,15 @@ func (d *indirectIndex) Key(idx int) (string, []IndexEntry) { if idx < 0 || idx >= len(d.offsets) { return "", nil } - n, key, _ := readKey(d.b[d.offsets[idx]:]) + n, key, err := readKey(d.b[d.offsets[idx]:]) + if err != nil { + return "", nil + } var entries indexEntries - readEntries(d.b[int(d.offsets[idx])+n:], &entries) + if _, err := readEntries(d.b[int(d.offsets[idx])+n:], &entries); err != nil { + return "", nil + } return string(key), entries.entries } @@ -773,6 +778,9 @@ func (d *indirectIndex) UnmarshalBinary(b []byte) error { // Keep a reference to the actual index bytes d.b = b + if len(b) == 0 { + return nil + } //var minKey, maxKey []byte var minTime, maxTime int64 = math.MaxInt64, 0 @@ -783,18 +791,28 @@ func (d *indirectIndex) UnmarshalBinary(b []byte) error { // basically skips across the slice keeping track of the counter when we are at a key // field. var i int32 - for i < int32(len(b)) { + iMax := int32(len(b)) + for i < iMax { d.offsets = append(d.offsets, i) // Skip to the start of the values // key length value (2) + type (1) + length of key + if i+2 >= iMax { + return fmt.Errorf("indirectIndex: not enough data for key length value") + } i += 3 + int32(binary.BigEndian.Uint16(b[i:i+2])) // count of index entries + if i+indexCountSize >= iMax { + return fmt.Errorf("indirectIndex: not enough data for index entries count") + } count := int32(binary.BigEndian.Uint16(b[i : i+indexCountSize])) i += indexCountSize // Find the min time for the block + if i+8 >= iMax { + return fmt.Errorf("indirectIndex: not enough data for min time") + } minT := int64(binary.BigEndian.Uint64(b[i : i+8])) if minT < minTime { minTime = minT @@ -803,6 +821,9 @@ func (d *indirectIndex) UnmarshalBinary(b []byte) error { i += (count - 1) * indexEntrySize // Find the max time for the block + if i+16 >= iMax { + return fmt.Errorf("indirectIndex: not enough data for max time") + } maxT := int64(binary.BigEndian.Uint64(b[i+8 : i+16])) if maxT > maxTime { maxTime = maxT @@ -871,9 +892,15 @@ func (m *mmapAccessor) init() (*indirectIndex, error) { if err != nil { return nil, err } + if len(m.b) < 8 { + return nil, fmt.Errorf("mmapAccessor: byte slice too small for indirectIndex") + } indexOfsPos := len(m.b) - 8 indexStart := binary.BigEndian.Uint64(m.b[indexOfsPos : indexOfsPos+8]) + if indexStart >= uint64(indexOfsPos) { + return nil, fmt.Errorf("mmapAccessor: invalid indexStart") + } m.index = NewIndirectIndex() if err := m.index.UnmarshalBinary(m.b[indexStart:indexOfsPos]); err != nil { @@ -1106,6 +1133,10 @@ func readKey(b []byte) (n int, key []byte, err error) { } func readEntries(b []byte, entries *indexEntries) (n int, err error) { + if len(b) < 1+indexCountSize { + return 0, fmt.Errorf("readEntries: data too short for headers") + } + // 1 byte block type entries.Type = b[n] n++ @@ -1117,7 +1148,12 @@ func readEntries(b []byte, entries *indexEntries) (n int, err error) { entries.entries = make([]IndexEntry, count) for i := 0; i < count; i++ { var ie IndexEntry - if err := ie.UnmarshalBinary(b[i*indexEntrySize+indexCountSize+indexTypeSize : i*indexEntrySize+indexCountSize+indexEntrySize+indexTypeSize]); err != nil { + start := i*indexEntrySize + indexCountSize + indexTypeSize + end := start + indexEntrySize + if end > len(b) { + return 0, fmt.Errorf("readEntries: data too short for indexEntry %d", i) + } + if err := ie.UnmarshalBinary(b[start:end]); err != nil { return 0, fmt.Errorf("readEntries: unmarshal error: %v", err) } entries.entries[i] = ie diff --git a/tsdb/engine/tsm1/reader_test.go b/tsdb/engine/tsm1/reader_test.go index 28326237a8..f3e9539182 100644 --- a/tsdb/engine/tsm1/reader_test.go +++ b/tsdb/engine/tsm1/reader_test.go @@ -2,8 +2,10 @@ package tsm1_test import ( "fmt" + "io/ioutil" "math" "os" + "path/filepath" "testing" "github.com/influxdata/influxdb/tsdb/engine/tsm1" @@ -1171,6 +1173,75 @@ func TestTSMReader_File_ReadAll(t *testing.T) { } } +func TestTSMReader_FuzzCrashes(t *testing.T) { + cases := []string{ + "", + "\x16\xd1\x16\xd1\x01\x10\x14X\xfb\x03\xac~\x80\xf0\x00\x00\x00I^K" + + "_\xf0\x00\x00\x00D424259389w\xf0\x00\x00\x00" + + "o\x93\bO\x10?\xf0\x00\x00\x00\x00\b\x00\xc2_\xff\xd8\x0fX^" + + "/\xbf\xe8\x00\x00\x00\x00\x00\x01\x00\bctr#!~#n\x00" + + "\x00\x01\x14X\xfb\xb0\x03\xac~\x80\x14X\xfb\xb1\x00\xd4ܥ\x00\x00" + + "\x00\x00\x00\x00\x00\x05\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00E", + "\x16\xd1\x16\xd1\x01\x80'Z\\\x00\v)\x00\x00\x00\x00;\x9a\xca\x00" + + "\x01\x05\x10?\xf0\x00\x00\x00\x00\x00\x00\xc2_\xff\xd6\x1d\xd4&\xed\v" + + "\xc5\xf7\xfb\xc0\x00\x00\x00\x00\x00 \x00\x06a#!~#v\x00\x00" + + "\x01\x00\x00\x00\x00;\x9a\xca\x00\x00\x00\x00\x01*\x05\xf2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x002", + "\x16\xd1\x16\xd1\x01\x80\xf0\x00\x00\x00I^K_\xf0\x00\x00\x00D7" + + "\nw\xf0\x00\x00\x00o\x93\bO\x10?\xf0\x00\x00\x00\x00\x00\x00\xc2" + + "_\xff\x14X\xfb\xb0\x03\xac~\x80\x14X\xfb\xb1\x00\xd4ܥ\x00\x00" + + "\x00\x00\x00\x00\x00\x05\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00E", + "\x16\xd1\x16\xd1\x01000000000000000" + + "00000000000000000000" + + "0000000000\x00\x000\x00\x0100000" + + "000\x00\x00\x00\x00\x00\x00\x002", + "\x16\xd1\x16\xd1\x01", + "\x16\xd1\x16\xd1\x01\x00\x00o\x93\bO\x10?\xf0\x00\x00\x00\x00X^" + + "/\xbf\xe8\x00\x00\x00\x00\x00\x01\x00\bctr#!~#n\x00" + + "\x00\x01\x14X\xfb\xb0\x03\xac~\x80\x14X\xfb\xb1\x00\xd4ܥ\x00\x00" + + "\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00E", + } + + for _, c := range cases { + func() { + dir := MustTempDir() + defer os.RemoveAll(dir) + + filename := filepath.Join(dir, "x.tsm") + if err := ioutil.WriteFile(filename, []byte(c), 0600); err != nil { + t.Fatalf("exp no error, got %s", err) + } + defer os.RemoveAll(dir) + + f, err := os.Open(filename) + if err != nil { + t.Fatalf("exp no error, got %s", err) + } + defer f.Close() + + r, err := tsm1.NewTSMReader(f) + if err != nil { + return + } + defer r.Close() + + iter := r.BlockIterator() + for iter.Next() { + key, _, _, _, _, err := iter.Read() + if err != nil { + return + } + + _, _ = r.Type(key) + + if _, err = r.ReadAll(key); err != nil { + return + } + } + }() + } +} + func TestTSMReader_File_Read(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) diff --git a/tsdb/engine/tsm1/string.go b/tsdb/engine/tsm1/string.go index 7c1f81fa71..bf5e4fcceb 100644 --- a/tsdb/engine/tsm1/string.go +++ b/tsdb/engine/tsm1/string.go @@ -59,9 +59,13 @@ type StringDecoder struct { func (e *StringDecoder) SetBytes(b []byte) error { // First byte stores the encoding type, only have snappy format // currently so ignore for now. - data, err := snappy.Decode(nil, b[1:]) - if err != nil { - return fmt.Errorf("failed to decode string block: %v", err.Error()) + var data []byte + if len(b) > 0 { + var err error + data, err = snappy.Decode(nil, b[1:]) + if err != nil { + return fmt.Errorf("failed to decode string block: %v", err.Error()) + } } e.b = data @@ -73,6 +77,10 @@ func (e *StringDecoder) SetBytes(b []byte) error { } func (e *StringDecoder) Next() bool { + if e.err != nil { + return false + } + e.i += e.l return e.i < len(e.b) } @@ -80,11 +88,26 @@ func (e *StringDecoder) Next() bool { func (e *StringDecoder) Read() string { // Read the length of the string length, n := binary.Uvarint(e.b[e.i:]) + if n <= 0 { + e.err = fmt.Errorf("StringDecoder: invalid encoded string length") + return "" + } // The length of this string plus the length of the variable byte encoded length e.l = int(length) + n - return string(e.b[e.i+n : e.i+n+int(length)]) + lower := e.i + n + upper := lower + int(length) + if upper < lower { + e.err = fmt.Errorf("StringDecoder: length overflow") + return "" + } + if upper > len(e.b) { + e.err = fmt.Errorf("StringDecoder: not enough data to represent encoded string") + return "" + } + + return string(e.b[lower:upper]) } func (e *StringDecoder) Error() error { diff --git a/tsdb/engine/tsm1/string_test.go b/tsdb/engine/tsm1/string_test.go index 74fcd158e8..34c5c71218 100644 --- a/tsdb/engine/tsm1/string_test.go +++ b/tsdb/engine/tsm1/string_test.go @@ -125,3 +125,57 @@ func Test_StringEncoder_Quick(t *testing.T) { return true }, nil) } + +func Test_StringDecoder_Empty(t *testing.T) { + var dec StringDecoder + if err := dec.SetBytes([]byte{}); err != nil { + t.Fatal(err) + } + + if dec.Next() { + t.Fatalf("exp Next() == false, got true") + } +} + +func Test_StringDecoder_CorruptInitial(t *testing.T) { + cases := []string{ + "\x10\x03\b\x03Hi", // Higher length than actual data + "\x10\x1dp\x9c\x90\x90\x90\x90\x90\x90\x90\x90\x90length overflow----", + } + + for _, c := range cases { + var dec StringDecoder + if err := dec.SetBytes([]byte(c)); err != nil { + t.Fatal(err) + } + + if !dec.Next() { + t.Fatalf("exp Next() to return true, got false") + } + + _ = dec.Read() + if dec.Error() == nil { + t.Fatalf("exp an err, got nil: %q", c) + } + } +} + +func Test_StringDecoder_CorruptReadAll(t *testing.T) { + cases := []string{ + "0t\x00\x01\x000\x00\x01\x000\x00\x01\x000\x00\x01\x000\x00\x01" + + "\x000\x00\x01\x000\x00\x01\x000\x00\x00\x00\xff:\x01\x00\x01\x00\x01" + + "\x00\x01\x00\x01\x00\x01\x00\x010\x010\x000\x010\x010\x010\x01" + + "0\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010", // Upper slice bounds overflows negative + } + + for _, c := range cases { + var dec StringDecoder + if err := dec.SetBytes([]byte(c)); err != nil { + t.Fatal(err) + } + + for dec.Next() { + _ = dec.Read() + } + } +} diff --git a/tsdb/engine/tsm1/timestamp.go b/tsdb/engine/tsm1/timestamp.go index d5436e157b..27dff49ecf 100644 --- a/tsdb/engine/tsm1/timestamp.go +++ b/tsdb/engine/tsm1/timestamp.go @@ -209,6 +209,10 @@ func (d *TimeDecoder) Init(b []byte) { } func (d *TimeDecoder) Next() bool { + if d.err != nil { + return false + } + if d.encoding == timeCompressedRLE { if d.i >= d.n { return false @@ -252,6 +256,10 @@ func (d *TimeDecoder) decode(b []byte) { } func (d *TimeDecoder) decodePacked(b []byte) { + if len(b) < 9 { + d.err = fmt.Errorf("TimeDecoder: not enough data to decode packed timestamps") + return + } div := uint64(math.Pow10(int(b[0] & 0xF))) first := uint64(binary.BigEndian.Uint64(b[1:9])) @@ -275,6 +283,11 @@ func (d *TimeDecoder) decodePacked(b []byte) { } func (d *TimeDecoder) decodeRLE(b []byte) { + if len(b) < 9 { + d.err = fmt.Errorf("TimeDecoder: not enough data for initial RLE timestamp") + return + } + var i, n int // Lower 4 bits hold the 10 based exponent so we can scale the values back up @@ -287,13 +300,21 @@ func (d *TimeDecoder) decodeRLE(b []byte) { // Next 1-10 bytes is our (scaled down by factor of 10) run length values value, n := binary.Uvarint(b[i:]) + if n <= 0 { + d.err = fmt.Errorf("TimeDecoder: invalid run length in decodeRLE") + return + } // Scale the value back up value *= uint64(mod) i += n // Last 1-10 bytes is how many times the value repeats - count, _ := binary.Uvarint(b[i:]) + count, n := binary.Uvarint(b[i:]) + if n <= 0 { + d.err = fmt.Errorf("TimeDecoder: invalid repeat value in decodeRLE") + return + } d.v = int64(first - value) d.rleDelta = int64(value) diff --git a/tsdb/engine/tsm1/timestamp_test.go b/tsdb/engine/tsm1/timestamp_test.go index 91dfe2f211..158773ae26 100644 --- a/tsdb/engine/tsm1/timestamp_test.go +++ b/tsdb/engine/tsm1/timestamp_test.go @@ -525,6 +525,25 @@ func TestTimeEncoder_Count_Simple8(t *testing.T) { } } +func TestTimeDecoder_Corrupt(t *testing.T) { + cases := []string{ + "", // Empty + "\x10\x14", // Packed: not enough data + "\x20\x00", // RLE: not enough data for starting timestamp + "\x2012345678\x90", // RLE: initial timestamp but invalid uvarint encoding + "\x2012345678\x7f", // RLE: timestamp, RLE but invalid repeat + "\x00123", // Raw: data length not multiple of 8 + } + + for _, c := range cases { + var dec TimeDecoder + dec.Init([]byte(c)) + if dec.Next() { + t.Fatalf("exp next == false, got true") + } + } +} + func BenchmarkTimeEncoder(b *testing.B) { enc := NewTimeEncoder() x := make([]int64, 1024)