gfe-relnote: Abandons HPACK decompression for overly large header blocks. Protected by default-true --gfe2_reloadable_flag_http2_reject_too_large_header_blocks. PiperOrigin-RevId: 259607264 Change-Id: I718fc440535289e1d8fb6d6e4febdbf8ed8d9d75
diff --git a/spdy/core/hpack/hpack_decoder_adapter.cc b/spdy/core/hpack/hpack_decoder_adapter.cc index ad05b1f..278eaab 100644 --- a/spdy/core/hpack/hpack_decoder_adapter.cc +++ b/spdy/core/hpack/hpack_decoder_adapter.cc
@@ -21,6 +21,7 @@ HpackDecoderAdapter::HpackDecoderAdapter() : hpack_decoder_(&listener_adapter_, kMaxDecodeBufferSizeBytes), max_decode_buffer_size_bytes_(kMaxDecodeBufferSizeBytes), + max_header_block_bytes_(0), header_block_started_(false) {} HpackDecoderAdapter::~HpackDecoderAdapter() = default; @@ -64,6 +65,10 @@ return false; } listener_adapter_.AddToTotalHpackBytes(headers_data_length); + if (max_header_block_bytes_ != 0 && + listener_adapter_.total_hpack_bytes() > max_header_block_bytes_) { + return false; + } http2::DecodeBuffer db(headers_data, headers_data_length); bool ok = hpack_decoder_.DecodeFragment(&db); DCHECK(!ok || db.Empty()) << "Remaining=" << db.Remaining(); @@ -109,6 +114,11 @@ hpack_decoder_.set_max_string_size_bytes(max_decode_buffer_size_bytes); } +void HpackDecoderAdapter::set_max_header_block_bytes( + size_t max_header_block_bytes) { + max_header_block_bytes_ = max_header_block_bytes; +} + size_t HpackDecoderAdapter::EstimateMemoryUsage() const { return SpdyEstimateMemoryUsage(hpack_decoder_); }
diff --git a/spdy/core/hpack/hpack_decoder_adapter.h b/spdy/core/hpack/hpack_decoder_adapter.h index d54cf1e..17c4461 100644 --- a/spdy/core/hpack/hpack_decoder_adapter.h +++ b/spdy/core/hpack/hpack_decoder_adapter.h
@@ -79,6 +79,10 @@ // of individual transport buffers. void set_max_decode_buffer_size_bytes(size_t max_decode_buffer_size_bytes); + // Specifies the maximum size of an on-the-wire header block that will be + // accepted. + void set_max_header_block_bytes(size_t max_header_block_bytes); + size_t EstimateMemoryUsage() const; private: @@ -147,6 +151,9 @@ // How much encoded data this decoder is willing to buffer. size_t max_decode_buffer_size_bytes_; + // How much encoded data this decoder is willing to process. + size_t max_header_block_bytes_; + // Flag to keep track of having seen the header block start. Needed at the // moment because HandleControlFrameHeadersStart won't be called if a handler // is not being provided by the caller.
diff --git a/spdy/core/hpack/hpack_decoder_adapter_test.cc b/spdy/core/hpack/hpack_decoder_adapter_test.cc index 335920e..d016841 100644 --- a/spdy/core/hpack/hpack_decoder_adapter_test.cc +++ b/spdy/core/hpack/hpack_decoder_adapter_test.cc
@@ -331,6 +331,33 @@ EXPECT_FALSE(HandleControlFrameHeadersData(fragment)); } +// Verify that a header block that exceeds the maximum length is rejected. +TEST_P(HpackDecoderAdapterTest, HeaderBlockTooLong) { + const SpdyString name = "some-key"; + const SpdyString value = "some-value"; + const size_t kMaxBufferSizeBytes = 1024; + + HpackBlockBuilder hbb; + hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false, + name, false, value); + while (hbb.size() < kMaxBufferSizeBytes) { + hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false, + "", false, ""); + } + // With no limit on the maximum header block size, the decoder handles the + // entire block successfully. + HandleControlFrameHeadersStart(); + EXPECT_TRUE(HandleControlFrameHeadersData(hbb.buffer())); + size_t total_bytes; + EXPECT_TRUE(HandleControlFrameHeadersComplete(&total_bytes)); + + // When a total byte limit is imposed, the decoder bails before the end of the + // block. + decoder_.set_max_header_block_bytes(kMaxBufferSizeBytes); + HandleControlFrameHeadersStart(); + EXPECT_FALSE(HandleControlFrameHeadersData(hbb.buffer())); +} + // Decode with incomplete data in buffer. TEST_P(HpackDecoderAdapterTest, DecodeWithIncompleteData) { HandleControlFrameHeadersStart();