Skip to content

Commit

Permalink
TS-5019: Add header length checks in HPACK
Browse files Browse the repository at this point in the history
  • Loading branch information
masaori335 committed Oct 27, 2016
1 parent 33cd156 commit 959975f
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 15 deletions.
16 changes: 15 additions & 1 deletion proxy/http2/HPACK.cc
Expand Up @@ -851,14 +851,16 @@ update_dynamic_table_size(const uint8_t *buf_start, const uint8_t *buf_end, Hpac
}

int64_t
hpack_decode_header_block(HpackIndexingTable &indexing_table, HTTPHdr *hdr, const uint8_t *in_buf, const size_t in_buf_len)
hpack_decode_header_block(HpackIndexingTable &indexing_table, HTTPHdr *hdr, const uint8_t *in_buf, const size_t in_buf_len,
uint32_t max_header_size)
{
const uint8_t *cursor = in_buf;
const uint8_t *const in_buf_end = in_buf + in_buf_len;
HdrHeap *heap = hdr->m_heap;
HTTPHdrImpl *hh = hdr->m_http;
bool header_field_started = false;
bool has_http2_violation = false;
uint32_t total_header_size = 0;

while (cursor < in_buf_end) {
int64_t read_bytes = 0;
Expand Down Expand Up @@ -902,6 +904,18 @@ hpack_decode_header_block(HpackIndexingTable &indexing_table, HTTPHdr *hdr, cons
cursor += read_bytes;
continue;
}

int name_len = 0;
int value_len = 0;

field->name_get(&name_len);
field->value_get(&value_len);
total_header_size += name_len + value_len;

if (total_header_size > max_header_size) {
return HPACK_ERROR_SIZE_EXCEEDED_ERROR;
}

// Store to HdrHeap
mime_hdr_field_attach(hh->m_fields_impl, field, 1, NULL);
}
Expand Down
6 changes: 4 additions & 2 deletions proxy/http2/HPACK.h
Expand Up @@ -30,7 +30,8 @@
#include "HTTP.h"

// It means that any header field can be compressed/decompressed by ATS
const static int HPACK_ERROR_COMPRESSION_ERROR = -1;
const static int HPACK_ERROR_COMPRESSION_ERROR = -1;
const static int HPACK_ERROR_SIZE_EXCEEDED_ERROR = -2;

enum HpackFieldType {
HPACK_FIELD_INDEX, // [RFC 7541] 6.1. Indexed Header Field Representation
Expand Down Expand Up @@ -170,7 +171,8 @@ int64_t update_dynamic_table_size(const uint8_t *buf_start, const uint8_t *buf_e

// High level interfaces
typedef HpackIndexingTable HpackHandle;
int64_t hpack_decode_header_block(HpackHandle &handle, HTTPHdr *hdr, const uint8_t *in_buf, const size_t in_buf_len);
int64_t hpack_decode_header_block(HpackHandle &handle, HTTPHdr *hdr, const uint8_t *in_buf, const size_t in_buf_len,
uint32_t max_header_size);
int64_t hpack_encode_header_block(HpackHandle &handle, uint8_t *out_buf, const size_t out_buf_len, HTTPHdr *hdr);

#endif /* __HPACK_H__ */
5 changes: 4 additions & 1 deletion proxy/http2/HTTP2.cc
Expand Up @@ -621,12 +621,15 @@ http2_decode_header_blocks(HTTPHdr *hdr, const uint8_t *buf_start, const uint32_
const char *value;
int len;
bool is_trailing_header = trailing_header;
int64_t result = hpack_decode_header_block(handle, hdr, buf_start, buf_len);
int64_t result = hpack_decode_header_block(handle, hdr, buf_start, buf_len, Http2::max_request_header_size);

if (result < 0) {
if (result == HPACK_ERROR_COMPRESSION_ERROR) {
return HTTP2_ERROR_COMPRESSION_ERROR;
} else if (result == HPACK_ERROR_SIZE_EXCEEDED_ERROR) {
return HTTP2_ERROR_ENHANCE_YOUR_CALM;
}

return HTTP2_ERROR_PROTOCOL_ERROR;
}
if (len_read) {
Expand Down
4 changes: 4 additions & 0 deletions proxy/http2/Http2ConnectionState.cc
Expand Up @@ -302,6 +302,8 @@ rcv_headers_frame(Http2ConnectionState &cstate, const Http2Frame &frame)
if (result != HTTP2_ERROR_NO_ERROR) {
if (result == HTTP2_ERROR_COMPRESSION_ERROR) {
return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_COMPRESSION_ERROR);
} else if (result == HTTP2_ERROR_ENHANCE_YOUR_CALM) {
return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_ENHANCE_YOUR_CALM);
} else {
return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_PROTOCOL_ERROR);
}
Expand Down Expand Up @@ -734,6 +736,8 @@ rcv_continuation_frame(Http2ConnectionState &cstate, const Http2Frame &frame)
if (result != HTTP2_ERROR_NO_ERROR) {
if (result == HTTP2_ERROR_COMPRESSION_ERROR) {
return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_COMPRESSION_ERROR);
} else if (result == HTTP2_ERROR_ENHANCE_YOUR_CALM) {
return Http2Error(HTTP2_ERROR_CLASS_CONNECTION, HTTP2_ERROR_ENHANCE_YOUR_CALM);
} else {
return Http2Error(HTTP2_ERROR_CLASS_STREAM, HTTP2_ERROR_PROTOCOL_ERROR);
}
Expand Down
7 changes: 7 additions & 0 deletions proxy/http2/Http2Stream.cc
Expand Up @@ -135,6 +135,13 @@ Http2Stream::main_event_handler(int event, void *edata)
return 0;
}

Http2ErrorCode
Http2Stream::decode_header_blocks(HpackHandle &hpack_handle)
{
return http2_decode_header_blocks(&_req_header, (const uint8_t *)header_blocks, header_blocks_length, NULL, hpack_handle,
trailing_header);
}

void
Http2Stream::send_request(Http2ConnectionState &cstate)
{
Expand Down
8 changes: 1 addition & 7 deletions proxy/http2/Http2Stream.h
Expand Up @@ -149,13 +149,6 @@ class Http2Stream : public ProxyClientTransaction
return trailing_header;
}

Http2ErrorCode
decode_header_blocks(HpackHandle &hpack_handle)
{
return http2_decode_header_blocks(&_req_header, (const uint8_t *)header_blocks, header_blocks_length, NULL, hpack_handle,
trailing_header);
}

void
set_request_headers(HTTPHdr &h2_headers)
{
Expand All @@ -176,6 +169,7 @@ class Http2Stream : public ProxyClientTransaction
return content_length == 0 || content_length == data_length;
}

Http2ErrorCode decode_header_blocks(HpackHandle &hpack_handle);
void send_request(Http2ConnectionState &cstate);
VIO *do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf);
VIO *do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *abuffer, bool owner = false);
Expand Down
3 changes: 2 additions & 1 deletion proxy/http2/RegressionHPACK.cc
Expand Up @@ -29,6 +29,7 @@
const static int DYNAMIC_TABLE_SIZE_FOR_REGRESSION_TEST = 256;
const static int BUFSIZE_FOR_REGRESSION_TEST = 128;
const static int MAX_TEST_FIELD_NUM = 8;
const static int MAX_REQUEST_HEADER_SIZE = 131072;

/***********************************************************************************
* *
Expand Down Expand Up @@ -564,7 +565,7 @@ REGRESSION_TEST(HPACK_Decode)(RegressionTest *t, int, int *pstatus)
headers->create(HTTP_TYPE_REQUEST);

hpack_decode_header_block(indexing_table, headers, encoded_field_request_test_case[i].encoded_field,
encoded_field_request_test_case[i].encoded_field_len);
encoded_field_request_test_case[i].encoded_field_len, MAX_REQUEST_HEADER_SIZE);

for (unsigned int j = 0; j < sizeof(raw_field_request_test_case[i]) / sizeof(raw_field_request_test_case[i][0]); j++) {
const char *expected_name = raw_field_request_test_case[i][j].raw_name;
Expand Down
8 changes: 5 additions & 3 deletions proxy/http2/test_HPACK.cc
Expand Up @@ -32,6 +32,8 @@
#include "ts/ink_args.h"
#include "ts/TestBox.h"

const static int MAX_REQUEST_HEADER_SIZE = 131072;

using namespace std;

AppVersionInfo appVersionInfo;
Expand Down Expand Up @@ -195,7 +197,7 @@ test_decoding(const string filename)
case 'w':
parse_line(line, 6, name, value);
unpacked_len = unpack(value, unpacked);
hpack_decode_header_block(indexing_table, &decoded, unpacked, unpacked_len);
hpack_decode_header_block(indexing_table, &decoded, unpacked, unpacked_len, MAX_REQUEST_HEADER_SIZE);
break;
}
break;
Expand Down Expand Up @@ -250,7 +252,7 @@ test_encoding(const string filename_in, const string filename_out)
result = seqnum;
break;
}
hpack_decode_header_block(indexing_table_for_decoding, &decoded, encoded, written);
hpack_decode_header_block(indexing_table_for_decoding, &decoded, encoded, written, MAX_REQUEST_HEADER_SIZE);
if (compare_header_fields(&decoded, &original) != 0) {
result = seqnum;
break;
Expand Down Expand Up @@ -295,7 +297,7 @@ test_encoding(const string filename_in, const string filename_out)
result = seqnum;
return result;
}
hpack_decode_header_block(indexing_table_for_decoding, &decoded, encoded, written);
hpack_decode_header_block(indexing_table_for_decoding, &decoded, encoded, written, MAX_REQUEST_HEADER_SIZE);
if (compare_header_fields(&decoded, &original) != 0) {
result = seqnum;
return result;
Expand Down

0 comments on commit 959975f

Please sign in to comment.