From aed3f89c38e76d97ad70e47dc91a10c7ef635f95 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Sat, 21 May 2022 20:14:28 +0100 Subject: [PATCH] CVE-2020-9494 Origin: backport Applied-Upstream: https://github.com/apache/trafficserver/pull/6922 Last-Update: 2020-06-25 Last-Update: 2020-06-25 Gbp-Pq: Name 0017-CVE-2020-9494.patch --- proxy/http2/HPACK.cc | 6 +++- proxy/http2/Http2ConnectionState.cc | 44 ++++++++++++++++++----------- proxy/http2/Http2Stream.h | 6 ++-- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/proxy/http2/HPACK.cc b/proxy/http2/HPACK.cc index 1c45d1e1..6770eb29 100644 --- a/proxy/http2/HPACK.cc +++ b/proxy/http2/HPACK.cc @@ -944,7 +944,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 c4227ac4..8215bde5 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_request_header_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,20 @@ 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"); + } + if (header_block_fragment_length > 0) { stream->header_blocks = static_cast(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) { @@ -830,16 +839,17 @@ 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_request_header_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; + // 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(ats_realloc(stream->header_blocks, stream->header_blocks_length)); frame.reader()->memcpy(stream->header_blocks + header_blocks_offset, payload_length); diff --git a/proxy/http2/Http2Stream.h b/proxy/http2/Http2Stream.h index 6b40ccf1..4afb31c6 100644 --- a/proxy/http2/Http2Stream.h +++ b/proxy/http2/Http2Stream.h @@ -178,10 +178,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; -- 2.30.2