LCOV - code coverage report
Current view: top level - source/extensions/compression/gzip/decompressor - zlib_decompressor_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 67 82 81.7 %
Date: 2024-01-05 06:35:25 Functions: 6 6 100.0 %

          Line data    Source code
       1             : #include "source/extensions/compression/gzip/decompressor/zlib_decompressor_impl.h"
       2             : 
       3             : #include <zlib.h>
       4             : 
       5             : #include <memory>
       6             : 
       7             : #include "envoy/common/exception.h"
       8             : 
       9             : #include "source/common/common/assert.h"
      10             : #include "source/common/runtime/runtime_features.h"
      11             : 
      12             : #include "absl/container/fixed_array.h"
      13             : 
      14             : namespace Envoy {
      15             : namespace Extensions {
      16             : namespace Compression {
      17             : namespace Gzip {
      18             : namespace Decompressor {
      19             : 
      20             : ZlibDecompressorImpl::ZlibDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix,
      21             :                                            uint64_t chunk_size, uint64_t max_inflate_ratio)
      22             :     : Common::Base(chunk_size,
      23         758 :                    [](z_stream* z) {
      24         758 :                      inflateEnd(z);
      25         758 :                      delete z;
      26         758 :                    }),
      27         758 :       stats_(generateStats(stats_prefix, scope)), max_inflate_ratio_(max_inflate_ratio) {
      28         758 :   zstream_ptr_->zalloc = Z_NULL;
      29         758 :   zstream_ptr_->zfree = Z_NULL;
      30         758 :   zstream_ptr_->opaque = Z_NULL;
      31         758 :   zstream_ptr_->avail_out = chunk_size_;
      32         758 :   zstream_ptr_->next_out = chunk_char_ptr_.get();
      33         758 : }
      34             : 
      35         758 : void ZlibDecompressorImpl::init(int64_t window_bits) {
      36         758 :   ASSERT(initialized_ == false);
      37         758 :   const int result = inflateInit2(zstream_ptr_.get(), window_bits);
      38         758 :   RELEASE_ASSERT(result >= 0, "");
      39         758 :   initialized_ = true;
      40         758 : }
      41             : 
      42             : void ZlibDecompressorImpl::decompress(const Buffer::Instance& input_buffer,
      43      168248 :                                       Buffer::Instance& output_buffer) {
      44      168248 :   uint64_t limit = max_inflate_ratio_ * input_buffer.length();
      45             : 
      46      168248 :   for (const Buffer::RawSlice& input_slice : input_buffer.getRawSlices()) {
      47      167517 :     zstream_ptr_->avail_in = input_slice.len_;
      48      167517 :     zstream_ptr_->next_in = static_cast<Bytef*>(input_slice.mem_);
      49      201254 :     while (inflateNext()) {
      50       38290 :       if (zstream_ptr_->avail_out == 0) {
      51         169 :         updateOutput(output_buffer);
      52         169 :       }
      53             : 
      54       38290 :       if (Runtime::runtimeFeatureEnabled(
      55       38290 :               "envoy.reloadable_features.enable_compression_bomb_protection") &&
      56       38290 :           (output_buffer.length() > limit)) {
      57        4553 :         stats_.zlib_data_error_.inc();
      58        4553 :         ENVOY_LOG(trace,
      59        4553 :                   "excessive decompression ratio detected: output "
      60        4553 :                   "size {} for input size {}",
      61        4553 :                   output_buffer.length(), input_buffer.length());
      62        4553 :         return;
      63        4553 :       }
      64       38290 :     }
      65      167517 :   }
      66             : 
      67             :   // Flush z_stream and reset its buffer. Otherwise the stale content of the buffer
      68             :   // will pollute output upon the next call to decompress().
      69      163695 :   updateOutput(output_buffer);
      70      163695 : }
      71             : 
      72      201254 : bool ZlibDecompressorImpl::inflateNext() {
      73      201254 :   const int result = inflate(zstream_ptr_.get(), Z_NO_FLUSH);
      74      201254 :   if (result == Z_STREAM_END) {
      75             :     // Z_FINISH informs inflate to not maintain a sliding window if the stream completes, which
      76             :     // reduces inflate's memory footprint. Ref: https://www.zlib.net/manual.html.
      77         756 :     inflate(zstream_ptr_.get(), Z_FINISH);
      78         756 :     return false;
      79         756 :   }
      80             : 
      81      200498 :   if (result == Z_BUF_ERROR && zstream_ptr_->avail_in == 0) {
      82       33569 :     return false; // This means that zlib needs more input, so stop here.
      83       33569 :   }
      84             : 
      85      166929 :   if (result < 0) {
      86      128639 :     decompression_error_ = result;
      87      128639 :     ENVOY_LOG(trace,
      88      128639 :               "zlib decompression error: {}, msg: {}. Error codes are defined in "
      89      128639 :               "https://www.zlib.net/manual.html",
      90      128639 :               result, zstream_ptr_->msg);
      91      128639 :     chargeErrorStats(result);
      92      128639 :     return false;
      93      128639 :   }
      94             : 
      95       38290 :   return true;
      96      166929 : }
      97             : 
      98      128639 : void ZlibDecompressorImpl::chargeErrorStats(const int result) {
      99      128639 :   switch (result) {
     100           0 :   case Z_ERRNO:
     101           0 :     stats_.zlib_errno_.inc();
     102           0 :     break;
     103           0 :   case Z_STREAM_ERROR:
     104           0 :     stats_.zlib_stream_error_.inc();
     105           0 :     break;
     106      128639 :   case Z_DATA_ERROR:
     107      128639 :     stats_.zlib_data_error_.inc();
     108      128639 :     break;
     109           0 :   case Z_MEM_ERROR:
     110           0 :     stats_.zlib_mem_error_.inc();
     111           0 :     break;
     112           0 :   case Z_BUF_ERROR:
     113           0 :     stats_.zlib_buf_error_.inc();
     114           0 :     break;
     115           0 :   case Z_VERSION_ERROR:
     116           0 :     stats_.zlib_version_error_.inc();
     117           0 :     break;
     118      128639 :   }
     119      128639 : }
     120             : 
     121             : } // namespace Decompressor
     122             : } // namespace Gzip
     123             : } // namespace Compression
     124             : } // namespace Extensions
     125             : } // namespace Envoy

Generated by: LCOV version 1.15