mirror of
https://github.com/nginx/nginx.git
synced 2024-12-04 13:59:00 +08:00
SPDY: fixed potential integer overflow while parsing headers.
Previously r->header_size was used to store length for a part of value that represents an individual already parsed HTTP header, while r->header_end pointed to the end of the whole value. Instead of storing length of a following name or value as pointer to a potential end address (r->header_name_end and r->header_end) that might be overflowed, now r->lowercase_index counter is used to store remaining length of a following unparsed field. It also fixes incorrect $body_bytes_sent value if a request is closed while parsing of the request header. Since r->header_size is intended for counting header size, thus abusing it for header parsing purpose was certainly a bad idea.
This commit is contained in:
parent
3925c1b110
commit
0c05e5b55f
@ -2325,7 +2325,7 @@ static ngx_int_t
|
|||||||
ngx_http_spdy_parse_header(ngx_http_request_t *r)
|
ngx_http_spdy_parse_header(ngx_http_request_t *r)
|
||||||
{
|
{
|
||||||
u_char *p, *end, ch;
|
u_char *p, *end, ch;
|
||||||
ngx_uint_t len, hash;
|
ngx_uint_t hash;
|
||||||
ngx_http_core_srv_conf_t *cscf;
|
ngx_http_core_srv_conf_t *cscf;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -2348,9 +2348,9 @@ ngx_http_spdy_parse_header(ngx_http_request_t *r)
|
|||||||
return NGX_AGAIN;
|
return NGX_AGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = ngx_spdy_frame_parse_uint32(p);
|
r->lowcase_index = ngx_spdy_frame_parse_uint32(p);
|
||||||
|
|
||||||
if (!len) {
|
if (r->lowcase_index == 0) {
|
||||||
return NGX_HTTP_PARSE_INVALID_HEADER;
|
return NGX_HTTP_PARSE_INVALID_HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2359,8 +2359,6 @@ ngx_http_spdy_parse_header(ngx_http_request_t *r)
|
|||||||
|
|
||||||
p += NGX_SPDY_NV_NLEN_SIZE;
|
p += NGX_SPDY_NV_NLEN_SIZE;
|
||||||
|
|
||||||
r->header_name_end = p + len;
|
|
||||||
r->lowcase_index = len;
|
|
||||||
r->invalid_header = 0;
|
r->invalid_header = 0;
|
||||||
|
|
||||||
state = sw_name;
|
state = sw_name;
|
||||||
@ -2369,16 +2367,16 @@ ngx_http_spdy_parse_header(ngx_http_request_t *r)
|
|||||||
|
|
||||||
case sw_name:
|
case sw_name:
|
||||||
|
|
||||||
if (r->header_name_end > end) {
|
if ((ngx_uint_t) (end - p) < r->lowcase_index) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
|
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
|
||||||
|
|
||||||
r->header_name_start = p;
|
r->header_name_start = p;
|
||||||
|
r->header_name_end = p + r->lowcase_index;
|
||||||
|
|
||||||
if (p[0] == ':') {
|
if (p[0] == ':') {
|
||||||
r->lowcase_index--;
|
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2425,29 +2423,26 @@ ngx_http_spdy_parse_header(ngx_http_request_t *r)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = ngx_spdy_frame_parse_uint32(p);
|
r->lowcase_index = ngx_spdy_frame_parse_uint32(p);
|
||||||
|
|
||||||
/* null-terminate header name */
|
/* null-terminate header name */
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
p += NGX_SPDY_NV_VLEN_SIZE;
|
p += NGX_SPDY_NV_VLEN_SIZE;
|
||||||
|
|
||||||
r->header_end = p + len;
|
|
||||||
|
|
||||||
state = sw_value;
|
state = sw_value;
|
||||||
|
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
||||||
case sw_value:
|
case sw_value:
|
||||||
|
|
||||||
if (r->header_end > end) {
|
if ((ngx_uint_t) (end - p) < r->lowcase_index) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->header_start = p;
|
r->header_start = p;
|
||||||
|
|
||||||
for ( /* void */ ; p != r->header_end; p++) {
|
while (r->lowcase_index--) {
|
||||||
|
|
||||||
ch = *p;
|
ch = *p;
|
||||||
|
|
||||||
if (ch == '\0') {
|
if (ch == '\0') {
|
||||||
@ -2456,7 +2451,7 @@ ngx_http_spdy_parse_header(ngx_http_request_t *r)
|
|||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->header_size = p - r->header_start;
|
r->header_end = p;
|
||||||
r->header_in->pos = p + 1;
|
r->header_in->pos = p + 1;
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
@ -2465,9 +2460,11 @@ ngx_http_spdy_parse_header(ngx_http_request_t *r)
|
|||||||
if (ch == CR || ch == LF) {
|
if (ch == CR || ch == LF) {
|
||||||
return NGX_HTTP_PARSE_INVALID_HEADER;
|
return NGX_HTTP_PARSE_INVALID_HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->header_size = p - r->header_start;
|
r->header_end = p;
|
||||||
r->header_in->pos = p;
|
r->header_in->pos = p;
|
||||||
|
|
||||||
r->state = 0;
|
r->state = 0;
|
||||||
@ -2526,13 +2523,6 @@ ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r)
|
|||||||
buf->last = ngx_cpymem(new, old, rest);
|
buf->last = ngx_cpymem(new, old, rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r->header_name_end > old) {
|
|
||||||
r->header_name_end = new + (r->header_name_end - old);
|
|
||||||
|
|
||||||
} else if (r->header_end > old) {
|
|
||||||
r->header_end = new + (r->header_end - old);
|
|
||||||
}
|
|
||||||
|
|
||||||
r->header_in = buf;
|
r->header_in = buf;
|
||||||
|
|
||||||
stream->header_buffers++;
|
stream->header_buffers++;
|
||||||
@ -2563,14 +2553,14 @@ ngx_http_spdy_handle_request_header(ngx_http_request_t *r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (r->header_name_start[0] == ':') {
|
if (r->header_name_start[0] == ':') {
|
||||||
|
r->header_name_start++;
|
||||||
|
|
||||||
for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {
|
for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {
|
||||||
sh = &ngx_http_spdy_request_headers[i];
|
sh = &ngx_http_spdy_request_headers[i];
|
||||||
|
|
||||||
if (sh->hash != r->header_hash
|
if (sh->hash != r->header_hash
|
||||||
|| sh->len != r->lowcase_index
|
|| sh->len != r->header_name_end - r->header_name_start
|
||||||
|| ngx_strncmp(sh->header, &r->header_name_start[1],
|
|| ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0)
|
||||||
r->lowcase_index)
|
|
||||||
!= 0)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2590,10 +2580,10 @@ ngx_http_spdy_handle_request_header(ngx_http_request_t *r)
|
|||||||
|
|
||||||
h->hash = r->header_hash;
|
h->hash = r->header_hash;
|
||||||
|
|
||||||
h->key.len = r->lowcase_index;
|
h->key.len = r->header_name_end - r->header_name_start;
|
||||||
h->key.data = r->header_name_start;
|
h->key.data = r->header_name_start;
|
||||||
|
|
||||||
h->value.len = r->header_size;
|
h->value.len = r->header_end - r->header_start;
|
||||||
h->value.data = r->header_start;
|
h->value.data = r->header_start;
|
||||||
|
|
||||||
h->lowcase_key = h->key.data;
|
h->lowcase_key = h->key.data;
|
||||||
@ -2653,7 +2643,7 @@ ngx_http_spdy_parse_method(ngx_http_request_t *r)
|
|||||||
return NGX_HTTP_PARSE_INVALID_HEADER;
|
return NGX_HTTP_PARSE_INVALID_HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = r->header_size;
|
len = r->header_end - r->header_start;
|
||||||
|
|
||||||
r->method_name.len = len;
|
r->method_name.len = len;
|
||||||
r->method_name.data = r->header_start;
|
r->method_name.data = r->header_start;
|
||||||
@ -2733,10 +2723,10 @@ ngx_http_spdy_parse_host(ngx_http_request_t *r)
|
|||||||
|
|
||||||
h->hash = r->header_hash;
|
h->hash = r->header_hash;
|
||||||
|
|
||||||
h->key.len = r->lowcase_index;
|
h->key.len = r->header_name_end - r->header_name_start;
|
||||||
h->key.data = &r->header_name_start[1];
|
h->key.data = r->header_name_start;
|
||||||
|
|
||||||
h->value.len = r->header_size;
|
h->value.len = r->header_end - r->header_start;
|
||||||
h->value.data = r->header_start;
|
h->value.data = r->header_start;
|
||||||
|
|
||||||
h->lowcase_key = h->key.data;
|
h->lowcase_key = h->key.data;
|
||||||
@ -2778,7 +2768,7 @@ ngx_http_spdy_parse_version(ngx_http_request_t *r)
|
|||||||
|
|
||||||
p = r->header_start;
|
p = r->header_start;
|
||||||
|
|
||||||
if (r->header_size < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) {
|
if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) {
|
||||||
return NGX_HTTP_PARSE_INVALID_REQUEST;
|
return NGX_HTTP_PARSE_INVALID_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2828,7 +2818,7 @@ ngx_http_spdy_parse_version(ngx_http_request_t *r)
|
|||||||
r->http_minor = r->http_minor * 10 + ch - '0';
|
r->http_minor = r->http_minor * 10 + ch - '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
r->http_protocol.len = r->header_size;
|
r->http_protocol.len = r->header_end - r->header_start;
|
||||||
r->http_protocol.data = r->header_start;
|
r->http_protocol.data = r->header_start;
|
||||||
r->http_version = r->http_major * 1000 + r->http_minor;
|
r->http_version = r->http_major * 1000 + r->http_minor;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user