This is an automated email from the ASF dual-hosted git repository.

bcall pushed a commit to branch 8.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/8.0.x by this push:
     new 4cf5b88  Fixed bug in the calculation of the header block fragment 
length
4cf5b88 is described below

commit 4cf5b887b3f7dc081988f1b7d71d37bee2f35c96
Author: Bryan Call <bc...@apache.org>
AuthorDate: Wed Apr 22 13:35:00 2020 -0700

    Fixed bug in the calculation of the header block fragment length
    
    Co-authored-by: Masaori Koshiba <masa...@apache.org>
---
 proxy/http2/HPACK.cc                |  6 ++++-
 proxy/http2/Http2ConnectionState.cc | 49 +++++++++++++++++++++----------------
 proxy/http2/Http2Stream.h           |  6 ++---
 3 files changed, 35 insertions(+), 26 deletions(-)

diff --git a/proxy/http2/HPACK.cc b/proxy/http2/HPACK.cc
index 70a6805..93090d6 100644
--- a/proxy/http2/HPACK.cc
+++ b/proxy/http2/HPACK.cc
@@ -946,7 +946,11 @@ hpack_decode_header_block(HpackIndexingTable 
&indexing_table, HTTPHdr *hdr, cons
 
     field->name_get(&name_len);
     field->value_get(&value_len);
-    total_header_size += name_len + value_len;
+
+    // [RFC 7540] 6.5.2. SETTINGS_MAX_HEADER_LIST_SIZE:
+    // The value is based on the uncompressed size of header fields, including 
the length of the name and value in octets plus an
+    // overhead of 32 octets for each header field.
+    total_header_size += name_len + value_len + ADDITIONAL_OCTETS;
 
     if (total_header_size > max_header_size) {
       return HPACK_ERROR_SIZE_EXCEEDED_ERROR;
diff --git a/proxy/http2/Http2ConnectionState.cc 
b/proxy/http2/Http2ConnectionState.cc
index 3eaa558..5bc1408 100644
--- a/proxy/http2/Http2ConnectionState.cc
+++ b/proxy/http2/Http2ConnectionState.cc
@@ -227,13 +227,6 @@ rcv_headers_frame(Http2ConnectionState &cstate, const 
Http2Frame &frame)
     return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE);
   }
 
-  // keep track of how many bytes we get in the frame
-  stream->request_header_length += payload_length;
-  if (stream->request_header_length > Http2::max_header_list_size) {
-    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_STREAM, 
Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
-                      "recv headers payload for headers greater than header 
length");
-  }
-
   Http2HeadersParameter params;
   uint32_t header_block_fragment_offset = 0;
   uint32_t header_block_fragment_length = payload_length;
@@ -252,7 +245,8 @@ rcv_headers_frame(Http2ConnectionState &cstate, const 
Http2Frame &frame)
                         "recv headers failed to parse");
     }
 
-    if (params.pad_length > payload_length) {
+    // Payload length can't be smaller than the pad length
+    if ((params.pad_length + HTTP2_HEADERS_PADLEN_LEN) > 
header_block_fragment_length) {
       return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, 
Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
                         "recv headers pad > payload length");
     }
@@ -268,7 +262,7 @@ rcv_headers_frame(Http2ConnectionState &cstate, const 
Http2Frame &frame)
     frame.reader()->memcpy(buf, HTTP2_PRIORITY_LEN, 
header_block_fragment_offset);
     if (!http2_parse_priority_parameter(make_iovec(buf, HTTP2_PRIORITY_LEN), 
params.priority)) {
       return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, 
Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
-                        "recv headers prioirity parameters failed parse");
+                        "recv headers priority parameters failed parse");
     }
     // Protocol error if the stream depends on itself
     if (stream_id == params.priority.stream_dependency) {
@@ -276,6 +270,12 @@ rcv_headers_frame(Http2ConnectionState &cstate, const 
Http2Frame &frame)
                         "recv headers self dependency");
     }
 
+    // Payload length can't be smaller than the priority length
+    if (HTTP2_PRIORITY_LEN > header_block_fragment_length) {
+      return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, 
Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
+                        "recv priority length > payload length");
+    }
+
     header_block_fragment_offset += HTTP2_PRIORITY_LEN;
     header_block_fragment_length -= HTTP2_PRIORITY_LEN;
   }
@@ -295,11 +295,19 @@ rcv_headers_frame(Http2ConnectionState &cstate, const 
Http2Frame &frame)
     }
   }
 
+  stream->header_blocks_length = header_block_fragment_length;
+
+  // ATS advertises SETTINGS_MAX_HEADER_LIST_SIZE as a limit of total header 
blocks length. (Details in [RFC 7560] 10.5.1.)
+  // Make it double to relax the limit in cases of 1) HPACK is used naively, 
or 2) Huffman Encoding generates large header blocks.
+  // The total "decoded" header length is strictly checked by 
hpack_decode_header_block().
+  if (stream->header_blocks_length > std::max(Http2::max_header_list_size, 
Http2::max_header_list_size * 2)) {
+    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, 
Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+                      "header blocks too large");
+  }
+
   stream->header_blocks = static_cast<uint8_t 
*>(ats_malloc(header_block_fragment_length));
   frame.reader()->memcpy(stream->header_blocks, header_block_fragment_length, 
header_block_fragment_offset);
 
-  stream->header_blocks_length = header_block_fragment_length;
-
   if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
     // NOTE: If there are END_HEADERS flag, decode stored Header Blocks.
     if (!stream->change_state(HTTP2_FRAME_TYPE_HEADERS, frame.header().flags) 
&& stream->has_trailing_header() == false) {
@@ -846,21 +854,20 @@ rcv_continuation_frame(Http2ConnectionState &cstate, 
const Http2Frame &frame)
     }
   }
 
-  // keep track of how many bytes we get in the frame
-  stream->request_header_length += payload_length;
-  if (stream->request_header_length > Http2::max_header_list_size) {
-    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, 
Http2ErrorCode::HTTP2_ERROR_PROTOCOL_ERROR,
-                      "continuation payload for headers exceeded");
-  }
-
   uint32_t header_blocks_offset = stream->header_blocks_length;
   stream->header_blocks_length += payload_length;
 
-  if (stream->header_blocks_length > 0) {
-    stream->header_blocks = static_cast<uint8_t 
*>(ats_realloc(stream->header_blocks, stream->header_blocks_length));
-    frame.reader()->memcpy(stream->header_blocks + header_blocks_offset, 
payload_length);
+  // ATS advertises SETTINGS_MAX_HEADER_LIST_SIZE as a limit of total header 
blocks length. (Details in [RFC 7560] 10.5.1.)
+  // Make it double to relax the limit in cases of 1) HPACK is used naively, 
or 2) Huffman Encoding generates large header blocks.
+  // The total "decoded" header length is strictly checked by 
hpack_decode_header_block().
+  if (stream->header_blocks_length > std::max(Http2::max_header_list_size, 
Http2::max_header_list_size * 2)) {
+    return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_CONNECTION, 
Http2ErrorCode::HTTP2_ERROR_ENHANCE_YOUR_CALM,
+                      "header blocks too large");
   }
 
+  stream->header_blocks = static_cast<uint8_t 
*>(ats_realloc(stream->header_blocks, stream->header_blocks_length));
+  frame.reader()->memcpy(stream->header_blocks + header_blocks_offset, 
payload_length);
+
   if (frame.header().flags & HTTP2_FLAGS_HEADERS_END_HEADERS) {
     // NOTE: If there are END_HEADERS flag, decode stored Header Blocks.
     cstate.clear_continued_stream_id();
diff --git a/proxy/http2/Http2Stream.h b/proxy/http2/Http2Stream.h
index f46404e..0c22288 100644
--- a/proxy/http2/Http2Stream.h
+++ b/proxy/http2/Http2Stream.h
@@ -161,10 +161,8 @@ public:
   int64_t read_vio_read_avail();
 
   uint8_t *header_blocks        = nullptr;
-  uint32_t header_blocks_length = 0;  // total length of header blocks (not 
include
-                                      // Padding or other fields)
-  uint32_t request_header_length = 0; // total length of payload (include 
Padding
-                                      // and other fields)
+  uint32_t header_blocks_length = 0; // total length of header blocks (not 
include Padding or other fields)
+
   bool recv_end_stream = false;
   bool send_end_stream = false;
 

Reply via email to