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