diff --git a/internal/bsr/chunk.go b/internal/bsr/chunk.go index 8ad8cf19f..46be61d6f 100644 --- a/internal/bsr/chunk.go +++ b/internal/bsr/chunk.go @@ -18,6 +18,11 @@ const ( crcDataSize = protocolSize + chunkTypeSize + directionSize + timestampSize chunkBaseSize = lengthSize + crcDataSize crcSize = 4 + + // MaxChunkDataLength sets an upper bound on BSR chunk lengths + // Default to 64MB, as this is the limit for protobufs, which back our chunks + // TODO: Should this be configurable as an option? + MaxChunkDataLength = 64 * 100 * 1000 ) // Chunk Types diff --git a/internal/bsr/decode.go b/internal/bsr/decode.go index c0383d708..5702b7046 100644 --- a/internal/bsr/decode.go +++ b/internal/bsr/decode.go @@ -126,6 +126,9 @@ func (d *ChunkDecoder) Decode(ctx context.Context) (Chunk, error) { crc := crc32.NewIEEE() length, buf = binary.BigEndian.Uint32(buf[:lengthSize]), buf[lengthSize:] + if length > MaxChunkDataLength { + return nil, fmt.Errorf("%s: chunk length %d exceeds max chunk length of %d: %w", op, length, MaxChunkDataLength, ErrChunkDecode) + } databuf := make([]byte, length) _, err = io.ReadAtLeast(d.r, databuf, int(length)) if err != nil { @@ -184,7 +187,9 @@ func (d *ChunkDecoder) Decode(ctx context.Context) (Chunk, error) { } } - decompressed, err := io.ReadAll(decompressor) + // Decompressed chunk data should not go beyond MaxChunkDataLength/ prevent allocations beyond this limit + limitedDecompressionReader := io.LimitReader(decompressor, MaxChunkDataLength) + decompressed, err := io.ReadAll(limitedDecompressionReader) if err != nil { return nil, fmt.Errorf("%s: %w: %w", op, err, ErrChunkDecode) } diff --git a/internal/bsr/decode_test.go b/internal/bsr/decode_test.go index 8f8245c49..04e4b662b 100644 --- a/internal/bsr/decode_test.go +++ b/internal/bsr/decode_test.go @@ -338,6 +338,22 @@ func TestChunkDecoderDecodeErrors(t *testing.T) { )), errors.New("bsr.(ChunkDecoder).Decode: unexpected EOF: error decoding chunk"), }, + { + "chuck-length-exceeds-max", + bytes.NewBuffer([]byte( + "" + // so everything else aligns better + "\xff\xff\xff\xff" + // length + "TEST" + // protocol + "HEAD" + // type + "\x01" + // direction + "\x00\x00\x00\x00\x64\x12\xf3\xa7" + // time seconds + "\x00\x00\x00\x0e" + // time nanoseconds + "\x00" + // compression method + "\x00" + // encryption method + "", + )), + errors.New("bsr.(ChunkDecoder).Decode: chunk length 4294967295 exceeds max chunk length of 6400000: error decoding chunk"), + }, { "chuck-missing-data", bytes.NewBuffer([]byte(