Compare commits
4 commits
main
...
wheezy-bac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2867579bb | ||
|
|
6322b86a76 | ||
|
|
1079d2a277 | ||
|
|
04dd449771 |
9 changed files with 785 additions and 0 deletions
25
debian/changelog
vendored
25
debian/changelog
vendored
|
|
@ -1,3 +1,28 @@
|
|||
nginx (1.6.2-5+deb8u2~bpo70+1) wheezy-backports; urgency=high
|
||||
|
||||
[ Christos Trochalakis ]
|
||||
* Rebuild for wheezy-backports.
|
||||
|
||||
-- Christos Trochalakis <yatiohi@ideopolis.gr> Tue, 07 Jun 2016 11:18:23 +0300
|
||||
|
||||
nginx (1.6.2-5+deb8u2) jessie-security; urgency=high
|
||||
|
||||
[ Christos Trochalakis ]
|
||||
* Fixes CVE-2016-4450
|
||||
NULL pointer dereference while writing client request body.
|
||||
(Closes: #825960)
|
||||
|
||||
-- Christos Trochalakis <yatiohi@ideopolis.gr> Tue, 31 May 2016 22:55:34 +0300
|
||||
|
||||
nginx (1.6.2-5+deb8u1) jessie-security; urgency=high
|
||||
|
||||
[ Christos Trochalakis ]
|
||||
* Fixes multiple resolver CVEs,
|
||||
CVE-2016-0742, CVE-2016-0746, CVE-2016-0747
|
||||
Closes: #812806
|
||||
|
||||
-- Christos Trochalakis <yatiohi@ideopolis.gr> Wed, 27 Jan 2016 12:22:00 +0200
|
||||
|
||||
nginx (1.6.2-5) unstable; urgency=medium
|
||||
|
||||
[ Christos Trochalakis ]
|
||||
|
|
|
|||
21
debian/patches/0003-Resolver-fixed-possible-segmentation-fault-on-DNS-fo.patch
vendored
Normal file
21
debian/patches/0003-Resolver-fixed-possible-segmentation-fault-on-DNS-fo.patch
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
From: Roman Arutyunyan <arut@nginx.com>
|
||||
Date: Tue, 26 Jan 2016 16:46:18 +0300
|
||||
Subject: Resolver: fixed possible segmentation fault on DNS format error.
|
||||
|
||||
---
|
||||
src/core/ngx_resolver.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
|
||||
index 5a944fc..726de9b 100644
|
||||
--- a/src/core/ngx_resolver.c
|
||||
+++ b/src/core/ngx_resolver.c
|
||||
@@ -1287,7 +1287,7 @@ ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n)
|
||||
times = 0;
|
||||
|
||||
for (q = ngx_queue_head(&r->name_resend_queue);
|
||||
- q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100;
|
||||
+ q != ngx_queue_sentinel(&r->name_resend_queue) && times++ < 100;
|
||||
q = ngx_queue_next(q))
|
||||
{
|
||||
rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
|
||||
128
debian/patches/0004-Resolver-fixed-crashes-in-timeout-handler.patch
vendored
Normal file
128
debian/patches/0004-Resolver-fixed-crashes-in-timeout-handler.patch
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
From: Ruslan Ermilov <ru@nginx.com>
|
||||
Date: Tue, 26 Jan 2016 16:46:31 +0300
|
||||
Subject: Resolver: fixed crashes in timeout handler.
|
||||
|
||||
If one or more requests were waiting for a response, then after
|
||||
getting a CNAME response, the timeout event on the first request
|
||||
remained active, pointing to the wrong node with an empty
|
||||
rn->waiting list, and that could cause either null pointer
|
||||
dereference or use-after-free memory access if this timeout
|
||||
expired.
|
||||
|
||||
If several requests were waiting for a response, and the first
|
||||
request terminated (e.g., due to client closing a connection),
|
||||
other requests were left without a timeout and could potentially
|
||||
wait indefinitely.
|
||||
|
||||
This is fixed by introducing per-request independent timeouts.
|
||||
This change also reverts 954867a2f0a6 and 5004210e8c78.
|
||||
---
|
||||
src/core/ngx_resolver.c | 50 +++++++++++++++++++++++++++++++++----------------
|
||||
1 file changed, 34 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
|
||||
index 726de9b..8ebef6f 100644
|
||||
--- a/src/core/ngx_resolver.c
|
||||
+++ b/src/core/ngx_resolver.c
|
||||
@@ -417,7 +417,7 @@ ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)
|
||||
|
||||
/* lock name mutex */
|
||||
|
||||
- if (ctx->state == NGX_AGAIN) {
|
||||
+ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
|
||||
|
||||
hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
|
||||
|
||||
@@ -571,6 +571,20 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
if (rn->waiting) {
|
||||
|
||||
+ if (ctx->event == NULL) {
|
||||
+ ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));
|
||||
+ if (ctx->event == NULL) {
|
||||
+ return NGX_ERROR;
|
||||
+ }
|
||||
+
|
||||
+ ctx->event->handler = ngx_resolver_timeout_handler;
|
||||
+ ctx->event->data = ctx;
|
||||
+ ctx->event->log = r->log;
|
||||
+ ctx->ident = -1;
|
||||
+
|
||||
+ ngx_add_timer(ctx->event, ctx->timeout);
|
||||
+ }
|
||||
+
|
||||
ctx->next = rn->waiting;
|
||||
rn->waiting = ctx;
|
||||
ctx->state = NGX_AGAIN;
|
||||
@@ -664,7 +678,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
}
|
||||
|
||||
ctx->event->handler = ngx_resolver_timeout_handler;
|
||||
- ctx->event->data = rn;
|
||||
+ ctx->event->data = ctx;
|
||||
ctx->event->log = r->log;
|
||||
ctx->ident = -1;
|
||||
|
||||
@@ -794,6 +808,18 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
|
||||
|
||||
if (rn->waiting) {
|
||||
|
||||
+ ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));
|
||||
+ if (ctx->event == NULL) {
|
||||
+ return NGX_ERROR;
|
||||
+ }
|
||||
+
|
||||
+ ctx->event->handler = ngx_resolver_timeout_handler;
|
||||
+ ctx->event->data = ctx;
|
||||
+ ctx->event->log = r->log;
|
||||
+ ctx->ident = -1;
|
||||
+
|
||||
+ ngx_add_timer(ctx->event, ctx->timeout);
|
||||
+
|
||||
ctx->next = rn->waiting;
|
||||
rn->waiting = ctx;
|
||||
ctx->state = NGX_AGAIN;
|
||||
@@ -857,7 +883,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
|
||||
}
|
||||
|
||||
ctx->event->handler = ngx_resolver_timeout_handler;
|
||||
- ctx->event->data = rn;
|
||||
+ ctx->event->data = ctx;
|
||||
ctx->event->log = r->log;
|
||||
ctx->ident = -1;
|
||||
|
||||
@@ -949,7 +975,7 @@ ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
|
||||
|
||||
/* lock addr mutex */
|
||||
|
||||
- if (ctx->state == NGX_AGAIN) {
|
||||
+ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
|
||||
|
||||
switch (ctx->addr.sockaddr->sa_family) {
|
||||
|
||||
@@ -2791,21 +2817,13 @@ done:
|
||||
static void
|
||||
ngx_resolver_timeout_handler(ngx_event_t *ev)
|
||||
{
|
||||
- ngx_resolver_ctx_t *ctx, *next;
|
||||
- ngx_resolver_node_t *rn;
|
||||
+ ngx_resolver_ctx_t *ctx;
|
||||
|
||||
- rn = ev->data;
|
||||
- ctx = rn->waiting;
|
||||
- rn->waiting = NULL;
|
||||
+ ctx = ev->data;
|
||||
|
||||
- do {
|
||||
- ctx->state = NGX_RESOLVE_TIMEDOUT;
|
||||
- next = ctx->next;
|
||||
-
|
||||
- ctx->handler(ctx);
|
||||
+ ctx->state = NGX_RESOLVE_TIMEDOUT;
|
||||
|
||||
- ctx = next;
|
||||
- } while (ctx);
|
||||
+ ctx->handler(ctx);
|
||||
}
|
||||
|
||||
|
||||
78
debian/patches/0005-Resolver-fixed-CNAME-processing-for-several-requests.patch
vendored
Normal file
78
debian/patches/0005-Resolver-fixed-CNAME-processing-for-several-requests.patch
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
From: Ruslan Ermilov <ru@nginx.com>
|
||||
Date: Tue, 26 Jan 2016 16:46:38 +0300
|
||||
Subject: Resolver: fixed CNAME processing for several requests.
|
||||
|
||||
When several requests were waiting for a response, then after getting
|
||||
a CNAME response only the last request was properly processed, while
|
||||
others were left waiting.
|
||||
---
|
||||
src/core/ngx_resolver.c | 21 +++++++++++++++------
|
||||
1 file changed, 15 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
|
||||
index 8ebef6f..3c01584 100644
|
||||
--- a/src/core/ngx_resolver.c
|
||||
+++ b/src/core/ngx_resolver.c
|
||||
@@ -468,7 +468,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t naddrs;
|
||||
ngx_addr_t *addrs;
|
||||
- ngx_resolver_ctx_t *next;
|
||||
+ ngx_resolver_ctx_t *next, *last;
|
||||
ngx_resolver_node_t *rn;
|
||||
|
||||
ngx_strlow(ctx->name.data, ctx->name.data, ctx->name.len);
|
||||
@@ -479,6 +479,9 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
if (rn) {
|
||||
|
||||
+ /* ctx can be a list after NGX_RESOLVE_CNAME */
|
||||
+ for (last = ctx; last->next; last = last->next);
|
||||
+
|
||||
if (rn->valid >= ngx_time()) {
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached");
|
||||
@@ -506,7 +509,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
- ctx->next = rn->waiting;
|
||||
+ last->next = rn->waiting;
|
||||
rn->waiting = NULL;
|
||||
|
||||
/* unlock name mutex */
|
||||
@@ -552,7 +555,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
return ngx_resolve_name_locked(r, ctx);
|
||||
}
|
||||
|
||||
- ctx->next = rn->waiting;
|
||||
+ last->next = rn->waiting;
|
||||
rn->waiting = NULL;
|
||||
|
||||
/* unlock name mutex */
|
||||
@@ -585,7 +588,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
ngx_add_timer(ctx->event, ctx->timeout);
|
||||
}
|
||||
|
||||
- ctx->next = rn->waiting;
|
||||
+ last->next = rn->waiting;
|
||||
rn->waiting = ctx;
|
||||
ctx->state = NGX_AGAIN;
|
||||
|
||||
@@ -656,8 +659,14 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
ngx_resolver_free(r, rn->name);
|
||||
ngx_resolver_free(r, rn);
|
||||
|
||||
- ctx->state = NGX_RESOLVE_NXDOMAIN;
|
||||
- ctx->handler(ctx);
|
||||
+ do {
|
||||
+ ctx->state = NGX_RESOLVE_NXDOMAIN;
|
||||
+ next = ctx->next;
|
||||
+
|
||||
+ ctx->handler(ctx);
|
||||
+
|
||||
+ ctx = next;
|
||||
+ } while (ctx);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
179
debian/patches/0006-Resolver-changed-the-ngx_resolver_create_-_query-arg.patch
vendored
Normal file
179
debian/patches/0006-Resolver-changed-the-ngx_resolver_create_-_query-arg.patch
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
From: Roman Arutyunyan <arut@nginx.com>
|
||||
Date: Tue, 26 Jan 2016 16:46:48 +0300
|
||||
Subject: Resolver: changed the ngx_resolver_create_*_query() arguments.
|
||||
|
||||
No functional changes.
|
||||
|
||||
This is needed by the following change.
|
||||
---
|
||||
src/core/ngx_resolver.c | 57 +++++++++++++++++++++++--------------------------
|
||||
1 file changed, 27 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
|
||||
index 3c01584..1899564 100644
|
||||
--- a/src/core/ngx_resolver.c
|
||||
+++ b/src/core/ngx_resolver.c
|
||||
@@ -59,10 +59,10 @@ static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree,
|
||||
ngx_queue_t *queue);
|
||||
static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r,
|
||||
ngx_resolver_node_t *rn);
|
||||
-static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_node_t *rn,
|
||||
- ngx_resolver_ctx_t *ctx);
|
||||
-static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_node_t *rn,
|
||||
- ngx_resolver_ctx_t *ctx);
|
||||
+static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_t *r,
|
||||
+ ngx_resolver_node_t *rn, ngx_str_t *name);
|
||||
+static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_t *r,
|
||||
+ ngx_resolver_node_t *rn, ngx_addr_t *addr);
|
||||
static void ngx_resolver_resend_handler(ngx_event_t *ev);
|
||||
static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree,
|
||||
ngx_queue_t *queue);
|
||||
@@ -646,7 +646,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
ngx_rbtree_insert(&r->name_rbtree, &rn->node);
|
||||
}
|
||||
|
||||
- rc = ngx_resolver_create_name_query(rn, ctx);
|
||||
+ rc = ngx_resolver_create_name_query(r, rn, &ctx->name);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
goto failed;
|
||||
@@ -873,7 +873,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
|
||||
ngx_rbtree_insert(tree, &rn->node);
|
||||
}
|
||||
|
||||
- if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) {
|
||||
+ if (ngx_resolver_create_addr_query(r, rn, &ctx->addr) != NGX_OK) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
@@ -2506,27 +2506,23 @@ ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t *temp,
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
-ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
+ngx_resolver_create_name_query(ngx_resolver_t *r, ngx_resolver_node_t *rn,
|
||||
+ ngx_str_t *name)
|
||||
{
|
||||
u_char *p, *s;
|
||||
size_t len, nlen;
|
||||
ngx_uint_t ident;
|
||||
-#if (NGX_HAVE_INET6)
|
||||
- ngx_resolver_t *r;
|
||||
-#endif
|
||||
ngx_resolver_qs_t *qs;
|
||||
ngx_resolver_hdr_t *query;
|
||||
|
||||
- nlen = ctx->name.len ? (1 + ctx->name.len + 1) : 1;
|
||||
+ nlen = name->len ? (1 + name->len + 1) : 1;
|
||||
|
||||
len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t);
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
- r = ctx->resolver;
|
||||
-
|
||||
- p = ngx_resolver_alloc(ctx->resolver, r->ipv6 ? len * 2 : len);
|
||||
+ p = ngx_resolver_alloc(r, r->ipv6 ? len * 2 : len);
|
||||
#else
|
||||
- p = ngx_resolver_alloc(ctx->resolver, len);
|
||||
+ p = ngx_resolver_alloc(r, len);
|
||||
#endif
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
@@ -2545,8 +2541,8 @@ ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
ident = ngx_random();
|
||||
|
||||
- ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0,
|
||||
- "resolve: \"%V\" A %i", &ctx->name, ident & 0xffff);
|
||||
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
|
||||
+ "resolve: \"%V\" A %i", name, ident & 0xffff);
|
||||
|
||||
query->ident_hi = (u_char) ((ident >> 8) & 0xff);
|
||||
query->ident_lo = (u_char) (ident & 0xff);
|
||||
@@ -2576,11 +2572,11 @@ ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
p--;
|
||||
*p-- = '\0';
|
||||
|
||||
- if (ctx->name.len == 0) {
|
||||
+ if (name->len == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
- for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) {
|
||||
+ for (s = name->data + name->len - 1; s >= name->data; s--) {
|
||||
if (*s != '.') {
|
||||
*p = *s;
|
||||
len++;
|
||||
@@ -2616,8 +2612,8 @@ ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
ident = ngx_random();
|
||||
|
||||
- ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0,
|
||||
- "resolve: \"%V\" AAAA %i", &ctx->name, ident & 0xffff);
|
||||
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
|
||||
+ "resolve: \"%V\" AAAA %i", name, ident & 0xffff);
|
||||
|
||||
query->ident_hi = (u_char) ((ident >> 8) & 0xff);
|
||||
query->ident_lo = (u_char) (ident & 0xff);
|
||||
@@ -2634,11 +2630,12 @@ ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
-ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
+ngx_resolver_create_addr_query(ngx_resolver_t *r, ngx_resolver_node_t *rn,
|
||||
+ ngx_addr_t *addr)
|
||||
{
|
||||
u_char *p, *d;
|
||||
size_t len;
|
||||
- in_addr_t addr;
|
||||
+ in_addr_t inaddr;
|
||||
ngx_int_t n;
|
||||
ngx_uint_t ident;
|
||||
ngx_resolver_hdr_t *query;
|
||||
@@ -2647,7 +2644,7 @@ ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
struct sockaddr_in6 *sin6;
|
||||
#endif
|
||||
|
||||
- switch (ctx->addr.sockaddr->sa_family) {
|
||||
+ switch (addr->sockaddr->sa_family) {
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
@@ -2664,7 +2661,7 @@ ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
+ sizeof(ngx_resolver_qs_t);
|
||||
}
|
||||
|
||||
- p = ngx_resolver_alloc(ctx->resolver, len);
|
||||
+ p = ngx_resolver_alloc(r, len);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
@@ -2688,11 +2685,11 @@ ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
p += sizeof(ngx_resolver_hdr_t);
|
||||
|
||||
- switch (ctx->addr.sockaddr->sa_family) {
|
||||
+ switch (addr->sockaddr->sa_family) {
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
- sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;
|
||||
+ sin6 = (struct sockaddr_in6 *) addr->sockaddr;
|
||||
|
||||
for (n = 15; n >= 0; n--) {
|
||||
p = ngx_sprintf(p, "\1%xd\1%xd",
|
||||
@@ -2707,11 +2704,11 @@ ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
default: /* AF_INET */
|
||||
|
||||
- sin = (struct sockaddr_in *) ctx->addr.sockaddr;
|
||||
- addr = ntohl(sin->sin_addr.s_addr);
|
||||
+ sin = (struct sockaddr_in *) addr->sockaddr;
|
||||
+ inaddr = ntohl(sin->sin_addr.s_addr);
|
||||
|
||||
for (n = 0; n < 32; n += 8) {
|
||||
- d = ngx_sprintf(&p[1], "%ud", (addr >> n) & 0xff);
|
||||
+ d = ngx_sprintf(&p[1], "%ud", (inaddr >> n) & 0xff);
|
||||
*p = (u_char) (d - &p[1]);
|
||||
p = d;
|
||||
}
|
||||
248
debian/patches/0007-Resolver-fixed-use-after-free-memory-accesses-with-C.patch
vendored
Normal file
248
debian/patches/0007-Resolver-fixed-use-after-free-memory-accesses-with-C.patch
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
From: Roman Arutyunyan <arut@nginx.com>
|
||||
Date: Tue, 26 Jan 2016 16:46:59 +0300
|
||||
Subject: Resolver: fixed use-after-free memory accesses with CNAME.
|
||||
|
||||
When several requests were waiting for a response, then after getting
|
||||
a CNAME response only the last request's context had the name updated.
|
||||
Contexts of other requests had the wrong name. This name was used by
|
||||
ngx_resolve_name_done() to find the node to remove the request context
|
||||
from. When the name was wrong, the request could not be properly
|
||||
cancelled, its context was freed but stayed linked to the node's waiting
|
||||
list. This happened e.g. when the first request was aborted or timed
|
||||
out before the resolving completed. When it completed, this triggered
|
||||
a use-after-free memory access by calling ctx->handler of already freed
|
||||
request context. The bug manifests itself by
|
||||
"could not cancel <name> resolving" alerts in error_log.
|
||||
|
||||
When a request was responded with a CNAME, the request context kept
|
||||
the pointer to the original node's rn->u.cname. If the original node
|
||||
expired before the resolving timed out or completed with an error,
|
||||
this would trigger a use-after-free memory access via ctx->name in
|
||||
ctx->handler().
|
||||
|
||||
The fix is to keep ctx->name unmodified. The name from context
|
||||
is no longer used by ngx_resolve_name_done(). Instead, we now keep
|
||||
the pointer to resolver node to which this request is linked.
|
||||
Keeping the original name intact also improves logging.
|
||||
---
|
||||
src/core/ngx_resolver.c | 72 +++++++++++++++++++++++--------------------------
|
||||
src/core/ngx_resolver.h | 2 ++
|
||||
2 files changed, 35 insertions(+), 39 deletions(-)
|
||||
|
||||
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
|
||||
index 1899564..2bd754c 100644
|
||||
--- a/src/core/ngx_resolver.c
|
||||
+++ b/src/core/ngx_resolver.c
|
||||
@@ -54,7 +54,7 @@ ngx_int_t ngx_udp_connect(ngx_udp_connection_t *uc);
|
||||
static void ngx_resolver_cleanup(void *data);
|
||||
static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree);
|
||||
static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r,
|
||||
- ngx_resolver_ctx_t *ctx);
|
||||
+ ngx_resolver_ctx_t *ctx, ngx_str_t *name);
|
||||
static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree,
|
||||
ngx_queue_t *queue);
|
||||
static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r,
|
||||
@@ -370,7 +370,7 @@ ngx_resolve_name(ngx_resolver_ctx_t *ctx)
|
||||
|
||||
/* lock name mutex */
|
||||
|
||||
- rc = ngx_resolve_name_locked(r, ctx);
|
||||
+ rc = ngx_resolve_name_locked(r, ctx, &ctx->name);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
return NGX_OK;
|
||||
@@ -397,7 +397,6 @@ ngx_resolve_name(ngx_resolver_ctx_t *ctx)
|
||||
void
|
||||
ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)
|
||||
{
|
||||
- uint32_t hash;
|
||||
ngx_resolver_t *r;
|
||||
ngx_resolver_ctx_t *w, **p;
|
||||
ngx_resolver_node_t *rn;
|
||||
@@ -419,9 +418,7 @@ ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)
|
||||
|
||||
if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
|
||||
|
||||
- hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
|
||||
-
|
||||
- rn = ngx_resolver_lookup_name(r, &ctx->name, hash);
|
||||
+ rn = ctx->node;
|
||||
|
||||
if (rn) {
|
||||
p = &rn->waiting;
|
||||
@@ -462,20 +459,22 @@ done:
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
-ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
+ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx,
|
||||
+ ngx_str_t *name)
|
||||
{
|
||||
uint32_t hash;
|
||||
ngx_int_t rc;
|
||||
+ ngx_str_t cname;
|
||||
ngx_uint_t naddrs;
|
||||
ngx_addr_t *addrs;
|
||||
ngx_resolver_ctx_t *next, *last;
|
||||
ngx_resolver_node_t *rn;
|
||||
|
||||
- ngx_strlow(ctx->name.data, ctx->name.data, ctx->name.len);
|
||||
+ ngx_strlow(name->data, name->data, name->len);
|
||||
|
||||
- hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
|
||||
+ hash = ngx_crc32_short(name->data, name->len);
|
||||
|
||||
- rn = ngx_resolver_lookup_name(r, &ctx->name, hash);
|
||||
+ rn = ngx_resolver_lookup_name(r, name, hash);
|
||||
|
||||
if (rn) {
|
||||
|
||||
@@ -549,10 +548,10 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) {
|
||||
|
||||
- ctx->name.len = rn->cnlen;
|
||||
- ctx->name.data = rn->u.cname;
|
||||
+ cname.len = rn->cnlen;
|
||||
+ cname.data = rn->u.cname;
|
||||
|
||||
- return ngx_resolve_name_locked(r, ctx);
|
||||
+ return ngx_resolve_name_locked(r, ctx, &cname);
|
||||
}
|
||||
|
||||
last->next = rn->waiting;
|
||||
@@ -592,6 +591,11 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
rn->waiting = ctx;
|
||||
ctx->state = NGX_AGAIN;
|
||||
|
||||
+ do {
|
||||
+ ctx->node = rn;
|
||||
+ ctx = ctx->next;
|
||||
+ } while (ctx);
|
||||
+
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
@@ -630,14 +634,14 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
- rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len);
|
||||
+ rn->name = ngx_resolver_dup(r, name->data, name->len);
|
||||
if (rn->name == NULL) {
|
||||
ngx_resolver_free(r, rn);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
rn->node.key = hash;
|
||||
- rn->nlen = (u_short) ctx->name.len;
|
||||
+ rn->nlen = (u_short) name->len;
|
||||
rn->query = NULL;
|
||||
#if (NGX_HAVE_INET6)
|
||||
rn->query6 = NULL;
|
||||
@@ -646,7 +650,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
ngx_rbtree_insert(&r->name_rbtree, &rn->node);
|
||||
}
|
||||
|
||||
- rc = ngx_resolver_create_name_query(r, rn, &ctx->name);
|
||||
+ rc = ngx_resolver_create_name_query(r, rn, name);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
goto failed;
|
||||
@@ -710,6 +714,11 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
|
||||
|
||||
ctx->state = NGX_AGAIN;
|
||||
|
||||
+ do {
|
||||
+ ctx->node = rn;
|
||||
+ ctx = ctx->next;
|
||||
+ } while (ctx);
|
||||
+
|
||||
return NGX_AGAIN;
|
||||
|
||||
failed:
|
||||
@@ -832,6 +841,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
|
||||
ctx->next = rn->waiting;
|
||||
rn->waiting = ctx;
|
||||
ctx->state = NGX_AGAIN;
|
||||
+ ctx->node = rn;
|
||||
|
||||
/* unlock addr mutex */
|
||||
|
||||
@@ -917,6 +927,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
|
||||
/* unlock addr mutex */
|
||||
|
||||
ctx->state = NGX_AGAIN;
|
||||
+ ctx->node = rn;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
@@ -947,17 +958,11 @@ failed:
|
||||
void
|
||||
ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
|
||||
{
|
||||
- in_addr_t addr;
|
||||
ngx_queue_t *expire_queue;
|
||||
ngx_rbtree_t *tree;
|
||||
ngx_resolver_t *r;
|
||||
ngx_resolver_ctx_t *w, **p;
|
||||
- struct sockaddr_in *sin;
|
||||
ngx_resolver_node_t *rn;
|
||||
-#if (NGX_HAVE_INET6)
|
||||
- uint32_t hash;
|
||||
- struct sockaddr_in6 *sin6;
|
||||
-#endif
|
||||
|
||||
r = ctx->resolver;
|
||||
|
||||
@@ -986,21 +991,7 @@ ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
|
||||
|
||||
if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
|
||||
|
||||
- switch (ctx->addr.sockaddr->sa_family) {
|
||||
-
|
||||
-#if (NGX_HAVE_INET6)
|
||||
- case AF_INET6:
|
||||
- sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;
|
||||
- hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16);
|
||||
- rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash);
|
||||
- break;
|
||||
-#endif
|
||||
-
|
||||
- default: /* AF_INET */
|
||||
- sin = (struct sockaddr_in *) ctx->addr.sockaddr;
|
||||
- addr = ntohl(sin->sin_addr.s_addr);
|
||||
- rn = ngx_resolver_lookup_addr(r, addr);
|
||||
- }
|
||||
+ rn = ctx->node;
|
||||
|
||||
if (rn) {
|
||||
p = &rn->waiting;
|
||||
@@ -1989,9 +1980,12 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,
|
||||
rn->waiting = NULL;
|
||||
|
||||
if (ctx) {
|
||||
- ctx->name = name;
|
||||
|
||||
- (void) ngx_resolve_name_locked(r, ctx);
|
||||
+ for (next = ctx; next; next = next->next) {
|
||||
+ next->node = NULL;
|
||||
+ }
|
||||
+
|
||||
+ (void) ngx_resolve_name_locked(r, ctx, &name);
|
||||
}
|
||||
|
||||
ngx_resolver_free(r, rn->query);
|
||||
diff --git a/src/core/ngx_resolver.h b/src/core/ngx_resolver.h
|
||||
index 264c8c4..0186e22 100644
|
||||
--- a/src/core/ngx_resolver.h
|
||||
+++ b/src/core/ngx_resolver.h
|
||||
@@ -161,6 +161,8 @@ struct ngx_resolver_ctx_s {
|
||||
ngx_uint_t quick; /* unsigned quick:1; */
|
||||
ngx_uint_t recursion;
|
||||
ngx_event_t *event;
|
||||
+
|
||||
+ ngx_resolver_node_t *node;
|
||||
};
|
||||
|
||||
|
||||
60
debian/patches/0008-Resolver-limited-CNAME-recursion.patch
vendored
Normal file
60
debian/patches/0008-Resolver-limited-CNAME-recursion.patch
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
From: Ruslan Ermilov <ru@nginx.com>
|
||||
Date: Tue, 26 Jan 2016 16:47:14 +0300
|
||||
Subject: Resolver: limited CNAME recursion.
|
||||
|
||||
Previously, the recursion was only limited for cached responses.
|
||||
---
|
||||
src/core/ngx_resolver.c | 28 ++++++++++++++++++++++------
|
||||
1 file changed, 22 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
|
||||
index 2bd754c..25d643d 100644
|
||||
--- a/src/core/ngx_resolver.c
|
||||
+++ b/src/core/ngx_resolver.c
|
||||
@@ -1976,11 +1976,33 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,
|
||||
|
||||
ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
|
||||
|
||||
+ ngx_resolver_free(r, rn->query);
|
||||
+ rn->query = NULL;
|
||||
+#if (NGX_HAVE_INET6)
|
||||
+ rn->query6 = NULL;
|
||||
+#endif
|
||||
+
|
||||
ctx = rn->waiting;
|
||||
rn->waiting = NULL;
|
||||
|
||||
if (ctx) {
|
||||
|
||||
+ if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) {
|
||||
+
|
||||
+ /* unlock name mutex */
|
||||
+
|
||||
+ do {
|
||||
+ ctx->state = NGX_RESOLVE_NXDOMAIN;
|
||||
+ next = ctx->next;
|
||||
+
|
||||
+ ctx->handler(ctx);
|
||||
+
|
||||
+ ctx = next;
|
||||
+ } while (ctx);
|
||||
+
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
for (next = ctx; next; next = next->next) {
|
||||
next->node = NULL;
|
||||
}
|
||||
@@ -1988,12 +2010,6 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,
|
||||
(void) ngx_resolve_name_locked(r, ctx, &name);
|
||||
}
|
||||
|
||||
- ngx_resolver_free(r, rn->query);
|
||||
- rn->query = NULL;
|
||||
-#if (NGX_HAVE_INET6)
|
||||
- rn->query6 = NULL;
|
||||
-#endif
|
||||
-
|
||||
/* unlock name mutex */
|
||||
|
||||
return;
|
||||
39
debian/patches/0009-Core-skip-special-buffers-on-writing-ticket-981.patch
vendored
Normal file
39
debian/patches/0009-Core-skip-special-buffers-on-writing-ticket-981.patch
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
From: Maxim Dounin <mdounin@mdounin.ru>
|
||||
Date: Tue, 31 May 2016 05:13:30 +0300
|
||||
Subject: Core: skip special buffers on writing (ticket #981).
|
||||
|
||||
A special last buffer with cl->buf->pos set to NULL can be present in
|
||||
a chain when writing request body if chunked encoding was used. This
|
||||
resulted in a NULL pointer dereference if it happened to be the only
|
||||
buffer left after a do...while loop iteration in ngx_write_chain_to_file().
|
||||
|
||||
The problem originally appeared in nginx 1.3.9 with chunked encoding
|
||||
support. Additionally, rev. 3832b608dc8d (nginx 1.9.13) changed the
|
||||
minimum number of buffers to trigger this from IOV_MAX (typically 1024)
|
||||
to NGX_IOVS_PREALLOCATE (typically 64).
|
||||
|
||||
Fix is to skip such buffers in ngx_chain_to_iovec(), much like it is
|
||||
done in other places.
|
||||
|
||||
Backported by 1.11.1 for debian.
|
||||
---
|
||||
src/os/unix/ngx_files.c | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c
|
||||
index c3ae47f..1970184 100644
|
||||
--- a/src/os/unix/ngx_files.c
|
||||
+++ b/src/os/unix/ngx_files.c
|
||||
@@ -183,6 +183,12 @@ ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset,
|
||||
/* create the iovec and coalesce the neighbouring bufs */
|
||||
|
||||
while (cl && vec.nelts < IOV_MAX) {
|
||||
+
|
||||
+ if (ngx_buf_special(cl->buf)) {
|
||||
+ cl = cl->next;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
if (prev == cl->buf->pos) {
|
||||
iov->iov_len += cl->buf->last - cl->buf->pos;
|
||||
|
||||
7
debian/patches/series
vendored
7
debian/patches/series
vendored
|
|
@ -1 +1,8 @@
|
|||
perl-use-dpkg-buildflags.patch
|
||||
0003-Resolver-fixed-possible-segmentation-fault-on-DNS-fo.patch
|
||||
0004-Resolver-fixed-crashes-in-timeout-handler.patch
|
||||
0005-Resolver-fixed-CNAME-processing-for-several-requests.patch
|
||||
0006-Resolver-changed-the-ngx_resolver_create_-_query-arg.patch
|
||||
0007-Resolver-fixed-use-after-free-memory-accesses-with-C.patch
|
||||
0008-Resolver-limited-CNAME-recursion.patch
|
||||
0009-Core-skip-special-buffers-on-writing-ticket-981.patch
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue