LCOV - code coverage report
Current view: top level - source/extensions/compression/brotli/decompressor - brotli_decompressor_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 41 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 3 0.0 %

          Line data    Source code
       1             : #include "source/extensions/compression/brotli/decompressor/brotli_decompressor_impl.h"
       2             : 
       3             : #include <memory>
       4             : 
       5             : #include "source/common/runtime/runtime_features.h"
       6             : 
       7             : namespace Envoy {
       8             : namespace Extensions {
       9             : namespace Compression {
      10             : namespace Brotli {
      11             : namespace Decompressor {
      12             : 
      13             : namespace {
      14             : 
      15             : // How many times the output buffer is allowed to be bigger than the input
      16             : // buffer. This value is used to detect compression bombs.
      17             : // TODO(rojkov): Re-design the Decompressor interface to handle compression
      18             : // bombs gracefully instead of this quick solution.
      19             : constexpr uint32_t MaxInflateRatio = 100;
      20             : 
      21             : } // namespace
      22             : 
      23             : BrotliDecompressorImpl::BrotliDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix,
      24             :                                                const uint32_t chunk_size,
      25             :                                                const bool disable_ring_buffer_reallocation)
      26             :     : chunk_size_{chunk_size},
      27             :       state_(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr), &BrotliDecoderDestroyInstance),
      28           0 :       stats_(generateStats(stats_prefix, scope)) {
      29           0 :   BROTLI_BOOL result =
      30           0 :       BrotliDecoderSetParameter(state_.get(), BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION,
      31           0 :                                 disable_ring_buffer_reallocation ? BROTLI_TRUE : BROTLI_FALSE);
      32           0 :   RELEASE_ASSERT(result == BROTLI_TRUE, "");
      33           0 : }
      34             : 
      35             : void BrotliDecompressorImpl::decompress(const Buffer::Instance& input_buffer,
      36           0 :                                         Buffer::Instance& output_buffer) {
      37           0 :   Common::BrotliContext ctx(chunk_size_, MaxInflateRatio * input_buffer.length());
      38             : 
      39           0 :   for (const Buffer::RawSlice& input_slice : input_buffer.getRawSlices()) {
      40           0 :     ctx.avail_in_ = input_slice.len_;
      41           0 :     ctx.next_in_ = static_cast<uint8_t*>(input_slice.mem_);
      42             : 
      43           0 :     while (ctx.avail_in_ > 0) {
      44           0 :       if (!process(ctx, output_buffer)) {
      45           0 :         ctx.finalizeOutput(output_buffer);
      46           0 :         return;
      47           0 :       }
      48           0 :     }
      49           0 :   }
      50             : 
      51             :   // Even though the input has been fully consumed by the decoder it still can
      52             :   // be unfolded into output not fitting the output chunk. Thus keep processing
      53             :   // until the decoder's output is fully depleted.
      54           0 :   bool success;
      55           0 :   do {
      56           0 :     success = process(ctx, output_buffer);
      57           0 :   } while (success && BrotliDecoderHasMoreOutput(state_.get()));
      58             : 
      59           0 :   ctx.finalizeOutput(output_buffer);
      60           0 : }
      61             : 
      62           0 : bool BrotliDecompressorImpl::process(Common::BrotliContext& ctx, Buffer::Instance& output_buffer) {
      63           0 :   BrotliDecoderResult result;
      64           0 :   result = BrotliDecoderDecompressStream(state_.get(), &ctx.avail_in_, &ctx.next_in_,
      65           0 :                                          &ctx.avail_out_, &ctx.next_out_, nullptr);
      66           0 :   if (result == BROTLI_DECODER_RESULT_ERROR) {
      67             :     // TODO(rojkov): currently the Brotli library doesn't specify possible errors in its API. Add
      68             :     // more detailed stats when they are documented.
      69           0 :     stats_.brotli_error_.inc();
      70           0 :     return false;
      71           0 :   }
      72             : 
      73           0 :   if (Runtime::runtimeFeatureEnabled(
      74           0 :           "envoy.reloadable_features.enable_compression_bomb_protection") &&
      75           0 :       (output_buffer.length() > ctx.max_output_size_)) {
      76           0 :     stats_.brotli_error_.inc();
      77           0 :     return false;
      78           0 :   }
      79             : 
      80           0 :   ctx.updateOutput(output_buffer);
      81             : 
      82           0 :   return true;
      83           0 : }
      84             : 
      85             : } // namespace Decompressor
      86             : } // namespace Brotli
      87             : } // namespace Compression
      88             : } // namespace Extensions
      89             : } // namespace Envoy

Generated by: LCOV version 1.15