[PATCH] Always use default server configs for large buffers allocation
Maxim Dounin
mdounin at mdounin.ru
Sun Feb 19 02:10:24 UTC 2017
Hello!
On Thu, Feb 16, 2017 at 05:00:20PM -0800, Daniil Bondarev wrote:
> # HG changeset patch
> # User Daniil Bondarev <bondarev at amazon.com>
> # Date 1485286710 28800
> # Tue Jan 24 11:38:30 2017 -0800
> # Node ID 8cd694e06443aaa1ed0601108681fa1c6f7297e0
> # Parent d84f48e571e449ee6c072a8d52cdea8e06b88ef7
> Always use default server configs for large buffers allocation
>
> Single http connection can flip between default server and virtual
> servers: depending on parsed Host header for a current request nginx
> changes current request configs. Currently this behavior might cause
> buffer overrun while adding new buffer to hc->busy, if hc->busy was
> allocated with config containing fewer large_client_header_buffers than
> the current one.
>
> This change makes nginx to always use large_client_header_buffers from
> http_connection config, which is default server config.
>
> diff -r d84f48e571e4 -r 8cd694e06443 src/http/ngx_http_request.c
> --- a/src/http/ngx_http_request.c Tue Jan 24 17:02:19 2017 +0300
> +++ b/src/http/ngx_http_request.c Tue Jan 24 11:38:30 2017 -0800
> @@ -1447,7 +1447,9 @@ ngx_http_alloc_large_header_buffer(ngx_h
>
> old = request_line ? r->request_start : r->header_name_start;
>
> - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
> + hc = r->http_connection;
> +
> + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
>
> if (r->state != 0
> && (size_t) (r->header_in->pos - old)
> @@ -1456,8 +1458,6 @@ ngx_http_alloc_large_header_buffer(ngx_h
> return NGX_DECLINED;
> }
>
> - hc = r->http_connection;
> -
> if (hc->nfree) {
> b = hc->free[--hc->nfree];
Thanks for spotting this.
Another part of this problem is with hc->free. If a number of
large header buffers configured in a virtual server is less than
the one in the default server, saving buffers from hc->busy to
hc->free for pipelined requests will also overflow allocated
buffer. To fix this as well, the patch should be extend with
something like this:
@@ -2876,7 +2875,7 @@ ngx_http_set_keepalive(ngx_http_request_
* Now we would move the large header buffers to the free list.
*/
- cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
if (hc->free == NULL) {
hc->free = ngx_palloc(c->pool,
An alternative approach would be to convert hc->busy / hc->free to
use chain links. This will preserve the possibility to have
different large_client_header_buffers in different virtual hosts
as long as the host is known before the limit is reached. Patch:
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1487467695 -10800
# Sun Feb 19 04:28:15 2017 +0300
# Node ID 3412c0c019f0f24432bac8b3e65ec03ba69073cb
# Parent 87cf6ddb41c216876d13cffa5e637a61b159362c
Converted hc->busy/hc->free to use chain links.
Most notably, this fixes possible buffer overflows if number of buffers
in a virtual server is different from the one in the default server.
Reported by Daniil Bondarev.
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -549,7 +549,7 @@ ngx_http_create_request(ngx_connection_t
ngx_set_connection_log(r->connection, clcf->error_log);
- r->header_in = hc->nbusy ? hc->busy[0] : c->buffer;
+ r->header_in = hc->busy ? hc->busy->buf : c->buffer;
if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
@@ -1431,6 +1431,7 @@ ngx_http_alloc_large_header_buffer(ngx_h
{
u_char *old, *new;
ngx_buf_t *b;
+ ngx_chain_t *cl;
ngx_http_connection_t *hc;
ngx_http_core_srv_conf_t *cscf;
@@ -1460,8 +1461,11 @@ ngx_http_alloc_large_header_buffer(ngx_h
hc = r->http_connection;
- if (hc->nfree) {
- b = hc->free[--hc->nfree];
+ if (hc->free) {
+ cl = hc->free;
+ hc->free = cl->next;
+
+ b = cl->buf;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http large header free: %p %uz",
@@ -1469,20 +1473,19 @@ ngx_http_alloc_large_header_buffer(ngx_h
} else if (hc->nbusy < cscf->large_client_header_buffers.num) {
- if (hc->busy == NULL) {
- hc->busy = ngx_palloc(r->connection->pool,
- cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
- if (hc->busy == NULL) {
- return NGX_ERROR;
- }
- }
-
b = ngx_create_temp_buf(r->connection->pool,
cscf->large_client_header_buffers.size);
if (b == NULL) {
return NGX_ERROR;
}
+ cl = ngx_alloc_chain_link(r->connection->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http large header alloc: %p %uz",
b->pos, b->end - b->last);
@@ -1491,7 +1494,9 @@ ngx_http_alloc_large_header_buffer(ngx_h
return NGX_DECLINED;
}
- hc->busy[hc->nbusy++] = b;
+ cl->next = hc->busy;
+ hc->busy = cl;
+ hc->nbusy++;
if (r->state == 0) {
/*
@@ -2835,12 +2840,11 @@ static void
ngx_http_set_keepalive(ngx_http_request_t *r)
{
int tcp_nodelay;
- ngx_int_t i;
ngx_buf_t *b, *f;
+ ngx_chain_t *cl, *ln;
ngx_event_t *rev, *wev;
ngx_connection_t *c;
ngx_http_connection_t *hc;
- ngx_http_core_srv_conf_t *cscf;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
@@ -2876,27 +2880,15 @@ ngx_http_set_keepalive(ngx_http_request_
* Now we would move the large header buffers to the free list.
*/
- cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
-
- if (hc->free == NULL) {
- hc->free = ngx_palloc(c->pool,
- cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
-
- if (hc->free == NULL) {
- ngx_http_close_request(r, 0);
- return;
- }
- }
-
- for (i = 0; i < hc->nbusy - 1; i++) {
- f = hc->busy[i];
- hc->free[hc->nfree++] = f;
+ for (cl = hc->busy; cl->next; cl = cl->next) {
+ f = cl->next->buf;
f->pos = f->start;
f->last = f->start;
}
- hc->busy[0] = b;
- hc->nbusy = 1;
+ cl->next = hc->free;
+ hc->free = hc->busy->next;
+ hc->busy->next = NULL;
}
}
@@ -2966,28 +2958,24 @@ ngx_http_set_keepalive(ngx_http_request_
b->last = b->start;
}
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc free: %p %i",
- hc->free, hc->nfree);
-
- if (hc->free) {
- for (i = 0; i < hc->nfree; i++) {
- ngx_pfree(c->pool, hc->free[i]->start);
- hc->free[i] = NULL;
- }
-
- hc->nfree = 0;
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc free: %p",
+ hc->free);
+
+ for (cl = hc->free; cl; /* void */) {
+ ln = cl;
+ cl = cl->next;
+ ngx_pfree(c->pool, ln->buf->start);
+ ngx_free_chain(r->pool, ln);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc busy: %p %i",
hc->busy, hc->nbusy);
- if (hc->busy) {
- for (i = 0; i < hc->nbusy; i++) {
- ngx_pfree(c->pool, hc->busy[i]->start);
- hc->busy[i] = NULL;
- }
-
- hc->nbusy = 0;
+ for (cl = hc->busy; cl; /* void */) {
+ ln = cl;
+ cl = cl->next;
+ ngx_pfree(c->pool, ln->buf->start);
+ ngx_free_chain(r->pool, ln);
}
#if (NGX_HTTP_SSL)
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -309,11 +309,10 @@ typedef struct {
#endif
#endif
- ngx_buf_t **busy;
+ ngx_chain_t *busy;
ngx_int_t nbusy;
- ngx_buf_t **free;
- ngx_int_t nfree;
+ ngx_chain_t *free;
unsigned ssl:1;
unsigned proxy_protocol:1;
--
Maxim Dounin
http://nginx.org/
More information about the nginx-devel
mailing list