diff --git a/contrib/brotli b/contrib/brotli index 63be8a9940199..3646606894cd8 160000 --- a/contrib/brotli +++ b/contrib/brotli @@ -1 +1 @@ -Subproject commit 63be8a99401992075c23e99f7c84de1c653e39e2 +Subproject commit 3646606894cd8b8c75d79a569b6c95a33c699d63 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81c74234473c8..3ec11ec716b01 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -600,6 +600,10 @@ if (ENABLE_TESTS) string_utils hilite_comparator) + if (TARGET ch_contrib::brotli) + target_link_libraries(unit_tests_dbms PRIVATE ch_contrib::brotli) + endif() + if (TARGET ch_contrib::simdjson) target_link_libraries(unit_tests_dbms PRIVATE ch_contrib::simdjson) endif() diff --git a/src/IO/tests/gtest_brotli_decoder.cpp b/src/IO/tests/gtest_brotli_decoder.cpp new file mode 100644 index 0000000000000..d7b8ade6a3f3a --- /dev/null +++ b/src/IO/tests/gtest_brotli_decoder.cpp @@ -0,0 +1,58 @@ +#include "config.h" + +#if USE_BROTLI + +#include +#include + +#include +#include +#include +#include +#include + +TEST(BrotliDecoder, RejectInvalidBlockSwitch) +{ + // A block switch is invalid when the stream declares only one block type. + // From https://github.com/nodejs/node/blob/main/test/parallel/test-zlib-invalid-input.js. + constexpr std::array input = { + 0x20, 0x00, 0x10, 0x41, 0x41, 0x41, 0xfc, 0xff, 0xff, 0x07, 0x00, + 0x22, 0x28, 0xf0, 0x0b, 0x60, 0xf7, 0xf4, 0x1f, 0x00, 0x00, 0x00}; + + for (size_t chunk_size : {input.size(), size_t{1}}) + { + std::unique_ptr state( + BrotliDecoderCreateInstance(nullptr, nullptr, nullptr), &BrotliDecoderDestroyInstance); + ASSERT_NE(state.get(), nullptr); + + std::array output{}; + size_t total_output = 0; + BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; + + for (size_t offset = 0; offset < input.size() && result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;) + { + size_t available_input = std::min(chunk_size, input.size() - offset); + const size_t provided_input = available_input; + const uint8_t * next_input = input.data() + offset; + do + { + uint8_t * next_output = output.data(); + size_t available_output = output.size(); + result = BrotliDecoderDecompressStream( + state.get(), &available_input, &next_input, &available_output, &next_output, nullptr); + total_output += output.size() - available_output; + ASSERT_LT(total_output, 64 * 1024 * 1024); + } + while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT); + offset += provided_input - available_input; + if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) + ASSERT_GT(provided_input - available_input, 0); + } + + EXPECT_EQ(result, BROTLI_DECODER_RESULT_ERROR); + // -17 is the `BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH` code added by the fix. + EXPECT_EQ(BrotliDecoderGetErrorCode(state.get()), static_cast(-17)); + } +} + +#endif