diff --git a/debian/changelog b/debian/changelog index be7399f..2209d99 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,28 @@ +nginx (1.6.2-5+deb8u2~bpo70+1) wheezy-backports; urgency=high + + [ Christos Trochalakis ] + * Rebuild for wheezy-backports. + + -- Christos Trochalakis 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 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 Wed, 27 Jan 2016 12:22:00 +0200 + nginx (1.6.2-5) unstable; urgency=medium [ Christos Trochalakis ] diff --git a/debian/patches/0003-Resolver-fixed-possible-segmentation-fault-on-DNS-fo.patch b/debian/patches/0003-Resolver-fixed-possible-segmentation-fault-on-DNS-fo.patch new file mode 100644 index 0000000..976e0ba --- /dev/null +++ b/debian/patches/0003-Resolver-fixed-possible-segmentation-fault-on-DNS-fo.patch @@ -0,0 +1,21 @@ +From: Roman Arutyunyan +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); diff --git a/debian/patches/0004-Resolver-fixed-crashes-in-timeout-handler.patch b/debian/patches/0004-Resolver-fixed-crashes-in-timeout-handler.patch new file mode 100644 index 0000000..8ec666a --- /dev/null +++ b/debian/patches/0004-Resolver-fixed-crashes-in-timeout-handler.patch @@ -0,0 +1,128 @@ +From: Ruslan Ermilov +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); + } + + diff --git a/debian/patches/0005-Resolver-fixed-CNAME-processing-for-several-requests.patch b/debian/patches/0005-Resolver-fixed-CNAME-processing-for-several-requests.patch new file mode 100644 index 0000000..d8759fe --- /dev/null +++ b/debian/patches/0005-Resolver-fixed-CNAME-processing-for-several-requests.patch @@ -0,0 +1,78 @@ +From: Ruslan Ermilov +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; + } diff --git a/debian/patches/0006-Resolver-changed-the-ngx_resolver_create_-_query-arg.patch b/debian/patches/0006-Resolver-changed-the-ngx_resolver_create_-_query-arg.patch new file mode 100644 index 0000000..4364ccf --- /dev/null +++ b/debian/patches/0006-Resolver-changed-the-ngx_resolver_create_-_query-arg.patch @@ -0,0 +1,179 @@ +From: Roman Arutyunyan +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; + } diff --git a/debian/patches/0007-Resolver-fixed-use-after-free-memory-accesses-with-C.patch b/debian/patches/0007-Resolver-fixed-use-after-free-memory-accesses-with-C.patch new file mode 100644 index 0000000..c2f7b07 --- /dev/null +++ b/debian/patches/0007-Resolver-fixed-use-after-free-memory-accesses-with-C.patch @@ -0,0 +1,248 @@ +From: Roman Arutyunyan +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 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; + }; + + diff --git a/debian/patches/0008-Resolver-limited-CNAME-recursion.patch b/debian/patches/0008-Resolver-limited-CNAME-recursion.patch new file mode 100644 index 0000000..dbe95a2 --- /dev/null +++ b/debian/patches/0008-Resolver-limited-CNAME-recursion.patch @@ -0,0 +1,60 @@ +From: Ruslan Ermilov +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; diff --git a/debian/patches/0009-Core-skip-special-buffers-on-writing-ticket-981.patch b/debian/patches/0009-Core-skip-special-buffers-on-writing-ticket-981.patch new file mode 100644 index 0000000..149dab0 --- /dev/null +++ b/debian/patches/0009-Core-skip-special-buffers-on-writing-ticket-981.patch @@ -0,0 +1,39 @@ +From: Maxim Dounin +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; + diff --git a/debian/patches/series b/debian/patches/series index 73f535e..f204855 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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