Mercurial > hg > nginx
comparison src/http/ngx_http_spdy.c @ 5590:6808ea2d69e4
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.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Mon, 03 Mar 2014 19:24:55 +0400 |
parents | a9116d9b63f9 |
children | 3a72b1805c52 |
comparison
equal
deleted
inserted
replaced
5589:a9116d9b63f9 | 5590:6808ea2d69e4 |
---|---|
2323 | 2323 |
2324 static ngx_int_t | 2324 static ngx_int_t |
2325 ngx_http_spdy_parse_header(ngx_http_request_t *r) | 2325 ngx_http_spdy_parse_header(ngx_http_request_t *r) |
2326 { | 2326 { |
2327 u_char *p, *end, ch; | 2327 u_char *p, *end, ch; |
2328 ngx_uint_t len, hash; | 2328 ngx_uint_t hash; |
2329 ngx_http_core_srv_conf_t *cscf; | 2329 ngx_http_core_srv_conf_t *cscf; |
2330 | 2330 |
2331 enum { | 2331 enum { |
2332 sw_name_len = 0, | 2332 sw_name_len = 0, |
2333 sw_name, | 2333 sw_name, |
2346 | 2346 |
2347 if (end - p < NGX_SPDY_NV_NLEN_SIZE) { | 2347 if (end - p < NGX_SPDY_NV_NLEN_SIZE) { |
2348 return NGX_AGAIN; | 2348 return NGX_AGAIN; |
2349 } | 2349 } |
2350 | 2350 |
2351 len = ngx_spdy_frame_parse_uint32(p); | 2351 r->lowcase_index = ngx_spdy_frame_parse_uint32(p); |
2352 | 2352 |
2353 if (!len) { | 2353 if (r->lowcase_index == 0) { |
2354 return NGX_HTTP_PARSE_INVALID_HEADER; | 2354 return NGX_HTTP_PARSE_INVALID_HEADER; |
2355 } | 2355 } |
2356 | 2356 |
2357 /* null-terminate the previous header value */ | 2357 /* null-terminate the previous header value */ |
2358 *p = '\0'; | 2358 *p = '\0'; |
2359 | 2359 |
2360 p += NGX_SPDY_NV_NLEN_SIZE; | 2360 p += NGX_SPDY_NV_NLEN_SIZE; |
2361 | 2361 |
2362 r->header_name_end = p + len; | |
2363 r->lowcase_index = len; | |
2364 r->invalid_header = 0; | 2362 r->invalid_header = 0; |
2365 | 2363 |
2366 state = sw_name; | 2364 state = sw_name; |
2367 | 2365 |
2368 /* fall through */ | 2366 /* fall through */ |
2369 | 2367 |
2370 case sw_name: | 2368 case sw_name: |
2371 | 2369 |
2372 if (r->header_name_end > end) { | 2370 if ((ngx_uint_t) (end - p) < r->lowcase_index) { |
2373 break; | 2371 break; |
2374 } | 2372 } |
2375 | 2373 |
2376 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | 2374 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); |
2377 | 2375 |
2378 r->header_name_start = p; | 2376 r->header_name_start = p; |
2377 r->header_name_end = p + r->lowcase_index; | |
2379 | 2378 |
2380 if (p[0] == ':') { | 2379 if (p[0] == ':') { |
2381 r->lowcase_index--; | |
2382 p++; | 2380 p++; |
2383 } | 2381 } |
2384 | 2382 |
2385 hash = 0; | 2383 hash = 0; |
2386 | 2384 |
2423 | 2421 |
2424 if (end - p < NGX_SPDY_NV_VLEN_SIZE) { | 2422 if (end - p < NGX_SPDY_NV_VLEN_SIZE) { |
2425 break; | 2423 break; |
2426 } | 2424 } |
2427 | 2425 |
2428 len = ngx_spdy_frame_parse_uint32(p); | 2426 r->lowcase_index = ngx_spdy_frame_parse_uint32(p); |
2429 | 2427 |
2430 /* null-terminate header name */ | 2428 /* null-terminate header name */ |
2431 *p = '\0'; | 2429 *p = '\0'; |
2432 | 2430 |
2433 p += NGX_SPDY_NV_VLEN_SIZE; | 2431 p += NGX_SPDY_NV_VLEN_SIZE; |
2434 | 2432 |
2435 r->header_end = p + len; | |
2436 | |
2437 state = sw_value; | 2433 state = sw_value; |
2438 | 2434 |
2439 /* fall through */ | 2435 /* fall through */ |
2440 | 2436 |
2441 case sw_value: | 2437 case sw_value: |
2442 | 2438 |
2443 if (r->header_end > end) { | 2439 if ((ngx_uint_t) (end - p) < r->lowcase_index) { |
2444 break; | 2440 break; |
2445 } | 2441 } |
2446 | 2442 |
2447 r->header_start = p; | 2443 r->header_start = p; |
2448 | 2444 |
2449 for ( /* void */ ; p != r->header_end; p++) { | 2445 while (r->lowcase_index--) { |
2450 | |
2451 ch = *p; | 2446 ch = *p; |
2452 | 2447 |
2453 if (ch == '\0') { | 2448 if (ch == '\0') { |
2454 | 2449 |
2455 if (p == r->header_start) { | 2450 if (p == r->header_start) { |
2456 return NGX_ERROR; | 2451 return NGX_ERROR; |
2457 } | 2452 } |
2458 | 2453 |
2459 r->header_size = p - r->header_start; | 2454 r->header_end = p; |
2460 r->header_in->pos = p + 1; | 2455 r->header_in->pos = p + 1; |
2461 | 2456 |
2462 return NGX_OK; | 2457 return NGX_OK; |
2463 } | 2458 } |
2464 | 2459 |
2465 if (ch == CR || ch == LF) { | 2460 if (ch == CR || ch == LF) { |
2466 return NGX_HTTP_PARSE_INVALID_HEADER; | 2461 return NGX_HTTP_PARSE_INVALID_HEADER; |
2467 } | 2462 } |
2468 } | 2463 |
2469 | 2464 p++; |
2470 r->header_size = p - r->header_start; | 2465 } |
2466 | |
2467 r->header_end = p; | |
2471 r->header_in->pos = p; | 2468 r->header_in->pos = p; |
2472 | 2469 |
2473 r->state = 0; | 2470 r->state = 0; |
2474 | 2471 |
2475 return NGX_DONE; | 2472 return NGX_DONE; |
2522 old = r->header_in->pos; | 2519 old = r->header_in->pos; |
2523 new = buf->pos; | 2520 new = buf->pos; |
2524 | 2521 |
2525 if (rest) { | 2522 if (rest) { |
2526 buf->last = ngx_cpymem(new, old, rest); | 2523 buf->last = ngx_cpymem(new, old, rest); |
2527 } | |
2528 | |
2529 if (r->header_name_end > old) { | |
2530 r->header_name_end = new + (r->header_name_end - old); | |
2531 | |
2532 } else if (r->header_end > old) { | |
2533 r->header_end = new + (r->header_end - old); | |
2534 } | 2524 } |
2535 | 2525 |
2536 r->header_in = buf; | 2526 r->header_in = buf; |
2537 | 2527 |
2538 stream->header_buffers++; | 2528 stream->header_buffers++; |
2561 } | 2551 } |
2562 | 2552 |
2563 } | 2553 } |
2564 | 2554 |
2565 if (r->header_name_start[0] == ':') { | 2555 if (r->header_name_start[0] == ':') { |
2556 r->header_name_start++; | |
2557 | |
2566 for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { | 2558 for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { |
2567 sh = &ngx_http_spdy_request_headers[i]; | 2559 sh = &ngx_http_spdy_request_headers[i]; |
2568 | 2560 |
2569 if (sh->hash != r->header_hash | 2561 if (sh->hash != r->header_hash |
2570 || sh->len != r->lowcase_index | 2562 || sh->len != r->header_name_end - r->header_name_start |
2571 || ngx_strncmp(sh->header, &r->header_name_start[1], | 2563 || ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0) |
2572 r->lowcase_index) | |
2573 != 0) | |
2574 { | 2564 { |
2575 continue; | 2565 continue; |
2576 } | 2566 } |
2577 | 2567 |
2578 return sh->handler(r); | 2568 return sh->handler(r); |
2588 return NGX_ERROR; | 2578 return NGX_ERROR; |
2589 } | 2579 } |
2590 | 2580 |
2591 h->hash = r->header_hash; | 2581 h->hash = r->header_hash; |
2592 | 2582 |
2593 h->key.len = r->lowcase_index; | 2583 h->key.len = r->header_name_end - r->header_name_start; |
2594 h->key.data = r->header_name_start; | 2584 h->key.data = r->header_name_start; |
2595 | 2585 |
2596 h->value.len = r->header_size; | 2586 h->value.len = r->header_end - r->header_start; |
2597 h->value.data = r->header_start; | 2587 h->value.data = r->header_start; |
2598 | 2588 |
2599 h->lowcase_key = h->key.data; | 2589 h->lowcase_key = h->key.data; |
2600 | 2590 |
2601 return NGX_OK; | 2591 return NGX_OK; |
2651 | 2641 |
2652 if (r->method_name.len) { | 2642 if (r->method_name.len) { |
2653 return NGX_HTTP_PARSE_INVALID_HEADER; | 2643 return NGX_HTTP_PARSE_INVALID_HEADER; |
2654 } | 2644 } |
2655 | 2645 |
2656 len = r->header_size; | 2646 len = r->header_end - r->header_start; |
2657 | 2647 |
2658 r->method_name.len = len; | 2648 r->method_name.len = len; |
2659 r->method_name.data = r->header_start; | 2649 r->method_name.data = r->header_start; |
2660 | 2650 |
2661 test = tests; | 2651 test = tests; |
2731 | 2721 |
2732 r->headers_in.host = h; | 2722 r->headers_in.host = h; |
2733 | 2723 |
2734 h->hash = r->header_hash; | 2724 h->hash = r->header_hash; |
2735 | 2725 |
2736 h->key.len = r->lowcase_index; | 2726 h->key.len = r->header_name_end - r->header_name_start; |
2737 h->key.data = &r->header_name_start[1]; | 2727 h->key.data = r->header_name_start; |
2738 | 2728 |
2739 h->value.len = r->header_size; | 2729 h->value.len = r->header_end - r->header_start; |
2740 h->value.data = r->header_start; | 2730 h->value.data = r->header_start; |
2741 | 2731 |
2742 h->lowcase_key = h->key.data; | 2732 h->lowcase_key = h->key.data; |
2743 | 2733 |
2744 return NGX_OK; | 2734 return NGX_OK; |
2776 return NGX_HTTP_PARSE_INVALID_HEADER; | 2766 return NGX_HTTP_PARSE_INVALID_HEADER; |
2777 } | 2767 } |
2778 | 2768 |
2779 p = r->header_start; | 2769 p = r->header_start; |
2780 | 2770 |
2781 if (r->header_size < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { | 2771 if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { |
2782 return NGX_HTTP_PARSE_INVALID_REQUEST; | 2772 return NGX_HTTP_PARSE_INVALID_REQUEST; |
2783 } | 2773 } |
2784 | 2774 |
2785 ch = *(p + 5); | 2775 ch = *(p + 5); |
2786 | 2776 |
2826 } | 2816 } |
2827 | 2817 |
2828 r->http_minor = r->http_minor * 10 + ch - '0'; | 2818 r->http_minor = r->http_minor * 10 + ch - '0'; |
2829 } | 2819 } |
2830 | 2820 |
2831 r->http_protocol.len = r->header_size; | 2821 r->http_protocol.len = r->header_end - r->header_start; |
2832 r->http_protocol.data = r->header_start; | 2822 r->http_protocol.data = r->header_start; |
2833 r->http_version = r->http_major * 1000 + r->http_minor; | 2823 r->http_version = r->http_major * 1000 + r->http_minor; |
2834 | 2824 |
2835 return NGX_OK; | 2825 return NGX_OK; |
2836 } | 2826 } |