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();