comparison src/http/ngx_http_request.c @ 6926:e662cbf1b932

Converted hc->busy/hc->free to use chain links. Most notably, this fixes possible buffer overflows if number of large client header buffers in a virtual server is different from the one in the default server. Reported by Daniil Bondarev.
author Maxim Dounin <mdounin@mdounin.ru>
date Tue, 07 Mar 2017 18:49:31 +0300
parents 72bb626484a4
children 1c43ac026c1d
comparison
equal deleted inserted replaced
6925:b3c5b4312667 6926:e662cbf1b932
547 547
548 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 548 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
549 549
550 ngx_set_connection_log(r->connection, clcf->error_log); 550 ngx_set_connection_log(r->connection, clcf->error_log);
551 551
552 r->header_in = hc->nbusy ? hc->busy[0] : c->buffer; 552 r->header_in = hc->busy ? hc->busy->buf : c->buffer;
553 553
554 if (ngx_list_init(&r->headers_out.headers, r->pool, 20, 554 if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
555 sizeof(ngx_table_elt_t)) 555 sizeof(ngx_table_elt_t))
556 != NGX_OK) 556 != NGX_OK)
557 { 557 {
1429 ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, 1429 ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
1430 ngx_uint_t request_line) 1430 ngx_uint_t request_line)
1431 { 1431 {
1432 u_char *old, *new; 1432 u_char *old, *new;
1433 ngx_buf_t *b; 1433 ngx_buf_t *b;
1434 ngx_chain_t *cl;
1434 ngx_http_connection_t *hc; 1435 ngx_http_connection_t *hc;
1435 ngx_http_core_srv_conf_t *cscf; 1436 ngx_http_core_srv_conf_t *cscf;
1436 1437
1437 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1438 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1438 "http alloc large header buffer"); 1439 "http alloc large header buffer");
1458 return NGX_DECLINED; 1459 return NGX_DECLINED;
1459 } 1460 }
1460 1461
1461 hc = r->http_connection; 1462 hc = r->http_connection;
1462 1463
1463 if (hc->nfree) { 1464 if (hc->free) {
1464 b = hc->free[--hc->nfree]; 1465 cl = hc->free;
1466 hc->free = cl->next;
1467
1468 b = cl->buf;
1465 1469
1466 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1470 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1467 "http large header free: %p %uz", 1471 "http large header free: %p %uz",
1468 b->pos, b->end - b->last); 1472 b->pos, b->end - b->last);
1469 1473
1470 } else if (hc->nbusy < cscf->large_client_header_buffers.num) { 1474 } else if (hc->nbusy < cscf->large_client_header_buffers.num) {
1471
1472 if (hc->busy == NULL) {
1473 hc->busy = ngx_palloc(r->connection->pool,
1474 cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
1475 if (hc->busy == NULL) {
1476 return NGX_ERROR;
1477 }
1478 }
1479 1475
1480 b = ngx_create_temp_buf(r->connection->pool, 1476 b = ngx_create_temp_buf(r->connection->pool,
1481 cscf->large_client_header_buffers.size); 1477 cscf->large_client_header_buffers.size);
1482 if (b == NULL) { 1478 if (b == NULL) {
1483 return NGX_ERROR; 1479 return NGX_ERROR;
1484 } 1480 }
1485 1481
1482 cl = ngx_alloc_chain_link(r->connection->pool);
1483 if (cl == NULL) {
1484 return NGX_ERROR;
1485 }
1486
1487 cl->buf = b;
1488
1486 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1489 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1487 "http large header alloc: %p %uz", 1490 "http large header alloc: %p %uz",
1488 b->pos, b->end - b->last); 1491 b->pos, b->end - b->last);
1489 1492
1490 } else { 1493 } else {
1491 return NGX_DECLINED; 1494 return NGX_DECLINED;
1492 } 1495 }
1493 1496
1494 hc->busy[hc->nbusy++] = b; 1497 cl->next = hc->busy;
1498 hc->busy = cl;
1499 hc->nbusy++;
1495 1500
1496 if (r->state == 0) { 1501 if (r->state == 0) {
1497 /* 1502 /*
1498 * r->state == 0 means that a header line was parsed successfully 1503 * r->state == 0 means that a header line was parsed successfully
1499 * and we do not need to copy incomplete header line and 1504 * and we do not need to copy incomplete header line and
2833 2838
2834 static void 2839 static void
2835 ngx_http_set_keepalive(ngx_http_request_t *r) 2840 ngx_http_set_keepalive(ngx_http_request_t *r)
2836 { 2841 {
2837 int tcp_nodelay; 2842 int tcp_nodelay;
2838 ngx_int_t i;
2839 ngx_buf_t *b, *f; 2843 ngx_buf_t *b, *f;
2844 ngx_chain_t *cl, *ln;
2840 ngx_event_t *rev, *wev; 2845 ngx_event_t *rev, *wev;
2841 ngx_connection_t *c; 2846 ngx_connection_t *c;
2842 ngx_http_connection_t *hc; 2847 ngx_http_connection_t *hc;
2843 ngx_http_core_srv_conf_t *cscf;
2844 ngx_http_core_loc_conf_t *clcf; 2848 ngx_http_core_loc_conf_t *clcf;
2845 2849
2846 c = r->connection; 2850 c = r->connection;
2847 rev = c->read; 2851 rev = c->read;
2848 2852
2874 * the pipelined request (see ngx_http_create_request()). 2878 * the pipelined request (see ngx_http_create_request()).
2875 * 2879 *
2876 * Now we would move the large header buffers to the free list. 2880 * Now we would move the large header buffers to the free list.
2877 */ 2881 */
2878 2882
2879 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); 2883 for (cl = hc->busy; cl; /* void */) {
2880 2884 ln = cl;
2881 if (hc->free == NULL) { 2885 cl = cl->next;
2882 hc->free = ngx_palloc(c->pool, 2886
2883 cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *)); 2887 if (ln->buf == b) {
2884 2888 ngx_free_chain(c->pool, ln);
2885 if (hc->free == NULL) { 2889 continue;
2886 ngx_http_close_request(r, 0);
2887 return;
2888 } 2890 }
2889 } 2891
2890 2892 f = ln->buf;
2891 for (i = 0; i < hc->nbusy - 1; i++) {
2892 f = hc->busy[i];
2893 hc->free[hc->nfree++] = f;
2894 f->pos = f->start; 2893 f->pos = f->start;
2895 f->last = f->start; 2894 f->last = f->start;
2896 } 2895
2897 2896 ln->next = hc->free;
2898 hc->busy[0] = b; 2897 hc->free = ln;
2898 }
2899
2900 cl = ngx_alloc_chain_link(c->pool);
2901 if (cl == NULL) {
2902 ngx_http_close_request(r, 0);
2903 return;
2904 }
2905
2906 cl->buf = b;
2907
2908 hc->busy = cl;
2899 hc->nbusy = 1; 2909 hc->nbusy = 1;
2900 } 2910 }
2901 } 2911 }
2902 2912
2903 /* guard against recursive call from ngx_http_finalize_connection() */ 2913 /* guard against recursive call from ngx_http_finalize_connection() */
2964 } else { 2974 } else {
2965 b->pos = b->start; 2975 b->pos = b->start;
2966 b->last = b->start; 2976 b->last = b->start;
2967 } 2977 }
2968 2978
2969 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc free: %p %i", 2979 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc free: %p",
2970 hc->free, hc->nfree); 2980 hc->free);
2971 2981
2972 if (hc->free) { 2982 if (hc->free) {
2973 for (i = 0; i < hc->nfree; i++) { 2983 for (cl = hc->free; cl; /* void */) {
2974 ngx_pfree(c->pool, hc->free[i]->start); 2984 ln = cl;
2975 hc->free[i] = NULL; 2985 cl = cl->next;
2976 } 2986 ngx_pfree(c->pool, ln->buf->start);
2977 2987 ngx_free_chain(c->pool, ln);
2978 hc->nfree = 0; 2988 }
2989
2990 hc->free = NULL;
2979 } 2991 }
2980 2992
2981 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc busy: %p %i", 2993 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc busy: %p %i",
2982 hc->busy, hc->nbusy); 2994 hc->busy, hc->nbusy);
2983 2995
2984 if (hc->busy) { 2996 if (hc->busy) {
2985 for (i = 0; i < hc->nbusy; i++) { 2997 for (cl = hc->busy; cl; /* void */) {
2986 ngx_pfree(c->pool, hc->busy[i]->start); 2998 ln = cl;
2987 hc->busy[i] = NULL; 2999 cl = cl->next;
2988 } 3000 ngx_pfree(c->pool, ln->buf->start);
2989 3001 ngx_free_chain(c->pool, ln);
3002 }
3003
3004 hc->busy = NULL;
2990 hc->nbusy = 0; 3005 hc->nbusy = 0;
2991 } 3006 }
2992 3007
2993 #if (NGX_HTTP_SSL) 3008 #if (NGX_HTTP_SSL)
2994 if (c->ssl) { 3009 if (c->ssl) {