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 }