Commit 0e75d463 authored by Dridi Boukelmoune's avatar Dridi Boukelmoune

http2_hpack: Enforce h2_max_header_list_size

This parameter has a new role that consists in interrupting connections
when decoding an HPACK block leads to a header list so large that the
client must be stopped.

By default, too large is 150% of http_req_size.
parent f1fb85fe
...@@ -236,6 +236,7 @@ struct h2h_decode { ...@@ -236,6 +236,7 @@ struct h2h_decode {
enum vhd_ret_e vhd_ret; enum vhd_ret_e vhd_ret;
char *out; char *out;
char *reset; char *reset;
int64_t limit;
size_t out_l; size_t out_l;
size_t out_u; size_t out_u;
size_t namelen; size_t namelen;
......
...@@ -278,6 +278,14 @@ h2h_decode_init(const struct h2_sess *h2) ...@@ -278,6 +278,14 @@ h2h_decode_init(const struct h2_sess *h2)
XXXAN(d->out_l); XXXAN(d->out_l);
d->out = WS_Reservation(h2->new_req->http->ws); d->out = WS_Reservation(h2->new_req->http->ws);
d->reset = d->out; d->reset = d->out;
if (cache_param->h2_max_header_list_size == 0)
d->limit = h2->local_settings.max_header_list_size * 1.5;
else
d->limit = cache_param->h2_max_header_list_size;
if (d->limit < h2->local_settings.max_header_list_size)
d->limit = INT64_MAX;
} }
/* Possible error returns: /* Possible error returns:
...@@ -351,7 +359,7 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l) ...@@ -351,7 +359,7 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
if (d->error != NULL) if (d->error != NULL)
assert(H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)); assert(H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM));
while (1) { while (d->limit >= 0) {
AN(d->out); AN(d->out);
assert(d->out_u <= d->out_l); assert(d->out_u <= d->out_l);
d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u, d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u,
...@@ -369,6 +377,7 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l) ...@@ -369,6 +377,7 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
} }
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) { if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
d->limit -= d->out_u;
d->out_u = 0; d->out_u = 0;
assert(d->out_u < d->out_l); assert(d->out_u < d->out_l);
continue; continue;
...@@ -402,6 +411,7 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l) ...@@ -402,6 +411,7 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
d->out[d->out_u++] = '\0'; /* Zero guard */ d->out[d->out_u++] = '\0'; /* Zero guard */
d->out += d->out_u; d->out += d->out_u;
d->out_l -= d->out_u; d->out_l -= d->out_u;
d->limit -= d->out_u;
d->out_u = 0; d->out_u = 0;
d->namelen = 0; d->namelen = 0;
break; break;
...@@ -418,15 +428,25 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l) ...@@ -418,15 +428,25 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) { if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
d->out = d->reset; d->out = d->reset;
d->out_l = e - d->out; d->out_l = e - d->out;
d->limit -= d->out_u;
d->out_u = 0; d->out_u = 0;
assert(d->out_l > 0); assert(d->out_l > 0);
} else if (d->error) } else if (d->error)
break; break;
} }
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) if (d->limit < 0) {
return (0); /* Stream error, delay reporting until /* Fatal error, the client exceeded both http_req_size
h2h_decode_fini so that we can process the * and h2_max_header_list_size. */
complete header block */ VSLb(h2->vsl, SLT_SessError, "Header list too large");
return (H2CE_ENHANCE_YOUR_CALM);
}
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
/* Stream error, delay reporting until h2h_decode_fini so
* that we can process the complete header block. */
return (NULL);
}
return (d->error); return (d->error);
} }
varnishtest "h2 CONTINUATION flood"
varnish v1 -cliok "param.set feature +http2"
varnish v1 -cliok "param.set vsl_mask -H2RxHdr,-H2RxBody"
varnish v1 -vcl { backend be none; } -start
client c1 {
non_fatal
stream next {
txreq -nohdrend
loop 1000 {
txcont -nohdrend -hdr noise ${string,repeat,4096,x}
}
txcont -hdr last header
} -run
} -run
varnish v1 -expect MAIN.s_req_hdrbytes < 65536
varnish v1 -expect MAIN.sc_overload == 1
...@@ -1289,12 +1289,20 @@ PARAM_SIMPLE( ...@@ -1289,12 +1289,20 @@ PARAM_SIMPLE(
/* type */ bytes_u, /* type */ bytes_u,
/* min */ "0b", /* min */ "0b",
/* max */ "4294967295b", /* max */ "4294967295b",
/* def */ "4294967295b", /* def */ "0b",
/* units */ "bytes", /* units */ "bytes",
/* descr */ /* descr */
"HTTP2 maximum size of an uncompressed header list. This parameter " "HTTP2 maximum size of an uncompressed header list. This parameter "
"is not mapped to " H2_SETTING_NAME(MAX_HEADER_LIST_SIZE) " in the " "is not mapped to " H2_SETTING_NAME(MAX_HEADER_LIST_SIZE) " in the "
"initial SETTINGS frame, the http_req_size parameter is instead." "initial SETTINGS frame, the http_req_size parameter is instead.\n\n"
"The http_req_size advises HTTP2 clients of the maximum size for "
"the header list. Exceeding http_req_size results in a reset stream "
"after processing the HPACK block to perserve the connection, but "
"exceeding h2_max_header_list_size results in the HTTP2 connection "
"going away immediately.\n\n"
"If h2_max_header_list_size is lower than http_req_size, it has no "
"effect, except for the special value zero interpreted as 150% of "
"http_req_size."
) )
#undef H2_SETTING_DESCR #undef H2_SETTING_DESCR
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment