diff --git a/clickhouse/base/compressed.cpp b/clickhouse/base/compressed.cpp index 06d72376..7820662d 100644 --- a/clickhouse/base/compressed.cpp +++ b/clickhouse/base/compressed.cpp @@ -79,7 +79,11 @@ bool CompressedInput::Decompress() { } if (compressed > DBMS_MAX_COMPRESSED_SIZE) { - throw CompressionError("compressed data too big"); + throw CompressionError("compressed block size " + std::to_string(compressed) + " exceeds maximum allowed, data is likely corrupted"); + } + + if (compressed < HEADER_SIZE) { + throw CompressionError("compressed block size " + std::to_string(compressed) + " is smaller than header, data is likely corrupted"); } Buffer tmp(compressed); diff --git a/ut/stream_ut.cpp b/ut/stream_ut.cpp index e139d8bc..0bcf669c 100644 --- a/ut/stream_ut.cpp +++ b/ut/stream_ut.cpp @@ -1,11 +1,33 @@ #include #include #include +#include #include using namespace clickhouse; +namespace { + +Buffer MakeCompressedBlockHeader(uint32_t compressed_size) { + Buffer buffer; + BufferOutput output(&buffer); + + uint8_t hash[16] = {}; + const uint8_t method = 0x82; + const uint32_t original_size = 0; + + WireFormat::WriteFixed(output, hash); + WireFormat::WriteFixed(output, method); + WireFormat::WriteFixed(output, compressed_size); + WireFormat::WriteFixed(output, original_size); + output.Flush(); + + return buffer; +} + +} + TEST(CodedStreamCase, Varint64) { Buffer buf; @@ -22,3 +44,15 @@ TEST(CodedStreamCase, Varint64) { ASSERT_EQ(value, 18446744071965638648ULL); } } + +TEST(CompressedInputCase, RejectsBlocksShorterThanHeader) { + for (const uint32_t compressed_size : {0U, 1U, 8U}) { + Buffer buffer = MakeCompressedBlockHeader(compressed_size); + ArrayInput input(buffer.data(), buffer.size()); + CompressedInput compressed_input(&input); + const void* data = nullptr; + + EXPECT_THROW(compressed_input.Next(&data, 1), CompressionError) + << "compressed size: " << compressed_size; + } +}