comparison src/core/ngx_resolver.c @ 6351:497d0cff8ace

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.
author Roman Arutyunyan <arut@nginx.com>
date Tue, 26 Jan 2016 16:46:59 +0300
parents a5767988c022
children ff9b32c0e141
comparison
equal deleted inserted replaced
6350:a5767988c022 6351:497d0cff8ace
57 57
58 58
59 static void ngx_resolver_cleanup(void *data); 59 static void ngx_resolver_cleanup(void *data);
60 static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree); 60 static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree);
61 static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, 61 static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r,
62 ngx_resolver_ctx_t *ctx); 62 ngx_resolver_ctx_t *ctx, ngx_str_t *name);
63 static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, 63 static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree,
64 ngx_queue_t *queue); 64 ngx_queue_t *queue);
65 static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, 65 static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r,
66 ngx_resolver_node_t *rn); 66 ngx_resolver_node_t *rn);
67 static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_t *r, 67 static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_t *r,
374 return NGX_OK; 374 return NGX_OK;
375 } 375 }
376 376
377 /* lock name mutex */ 377 /* lock name mutex */
378 378
379 rc = ngx_resolve_name_locked(r, ctx); 379 rc = ngx_resolve_name_locked(r, ctx, &ctx->name);
380 380
381 if (rc == NGX_OK) { 381 if (rc == NGX_OK) {
382 return NGX_OK; 382 return NGX_OK;
383 } 383 }
384 384
401 401
402 402
403 void 403 void
404 ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) 404 ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)
405 { 405 {
406 uint32_t hash;
407 ngx_resolver_t *r; 406 ngx_resolver_t *r;
408 ngx_resolver_ctx_t *w, **p; 407 ngx_resolver_ctx_t *w, **p;
409 ngx_resolver_node_t *rn; 408 ngx_resolver_node_t *rn;
410 409
411 r = ctx->resolver; 410 r = ctx->resolver;
423 422
424 /* lock name mutex */ 423 /* lock name mutex */
425 424
426 if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { 425 if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
427 426
428 hash = ngx_crc32_short(ctx->name.data, ctx->name.len); 427 rn = ctx->node;
429
430 rn = ngx_resolver_lookup_name(r, &ctx->name, hash);
431 428
432 if (rn) { 429 if (rn) {
433 p = &rn->waiting; 430 p = &rn->waiting;
434 w = rn->waiting; 431 w = rn->waiting;
435 432
470 } 467 }
471 } 468 }
472 469
473 470
474 static ngx_int_t 471 static ngx_int_t
475 ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) 472 ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx,
473 ngx_str_t *name)
476 { 474 {
477 uint32_t hash; 475 uint32_t hash;
478 ngx_int_t rc; 476 ngx_int_t rc;
477 ngx_str_t cname;
479 ngx_uint_t naddrs; 478 ngx_uint_t naddrs;
480 ngx_addr_t *addrs; 479 ngx_addr_t *addrs;
481 ngx_resolver_ctx_t *next, *last; 480 ngx_resolver_ctx_t *next, *last;
482 ngx_resolver_node_t *rn; 481 ngx_resolver_node_t *rn;
483 482
484 ngx_strlow(ctx->name.data, ctx->name.data, ctx->name.len); 483 ngx_strlow(name->data, name->data, name->len);
485 484
486 hash = ngx_crc32_short(ctx->name.data, ctx->name.len); 485 hash = ngx_crc32_short(name->data, name->len);
487 486
488 rn = ngx_resolver_lookup_name(r, &ctx->name, hash); 487 rn = ngx_resolver_lookup_name(r, name, hash);
489 488
490 if (rn) { 489 if (rn) {
491 490
492 /* ctx can be a list after NGX_RESOLVE_CNAME */ 491 /* ctx can be a list after NGX_RESOLVE_CNAME */
493 for (last = ctx; last->next; last = last->next); 492 for (last = ctx; last->next; last = last->next);
557 556
558 /* NGX_RESOLVE_CNAME */ 557 /* NGX_RESOLVE_CNAME */
559 558
560 if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { 559 if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) {
561 560
562 ctx->name.len = rn->cnlen; 561 cname.len = rn->cnlen;
563 ctx->name.data = rn->u.cname; 562 cname.data = rn->u.cname;
564 563
565 return ngx_resolve_name_locked(r, ctx); 564 return ngx_resolve_name_locked(r, ctx, &cname);
566 } 565 }
567 566
568 last->next = rn->waiting; 567 last->next = rn->waiting;
569 rn->waiting = NULL; 568 rn->waiting = NULL;
570 569
600 599
601 last->next = rn->waiting; 600 last->next = rn->waiting;
602 rn->waiting = ctx; 601 rn->waiting = ctx;
603 ctx->state = NGX_AGAIN; 602 ctx->state = NGX_AGAIN;
604 603
604 do {
605 ctx->node = rn;
606 ctx = ctx->next;
607 } while (ctx);
608
605 return NGX_AGAIN; 609 return NGX_AGAIN;
606 } 610 }
607 611
608 ngx_queue_remove(&rn->queue); 612 ngx_queue_remove(&rn->queue);
609 613
638 rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); 642 rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));
639 if (rn == NULL) { 643 if (rn == NULL) {
640 return NGX_ERROR; 644 return NGX_ERROR;
641 } 645 }
642 646
643 rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); 647 rn->name = ngx_resolver_dup(r, name->data, name->len);
644 if (rn->name == NULL) { 648 if (rn->name == NULL) {
645 ngx_resolver_free(r, rn); 649 ngx_resolver_free(r, rn);
646 return NGX_ERROR; 650 return NGX_ERROR;
647 } 651 }
648 652
649 rn->node.key = hash; 653 rn->node.key = hash;
650 rn->nlen = (u_short) ctx->name.len; 654 rn->nlen = (u_short) name->len;
651 rn->query = NULL; 655 rn->query = NULL;
652 #if (NGX_HAVE_INET6) 656 #if (NGX_HAVE_INET6)
653 rn->query6 = NULL; 657 rn->query6 = NULL;
654 #endif 658 #endif
655 659
656 ngx_rbtree_insert(&r->name_rbtree, &rn->node); 660 ngx_rbtree_insert(&r->name_rbtree, &rn->node);
657 } 661 }
658 662
659 rc = ngx_resolver_create_name_query(r, rn, &ctx->name); 663 rc = ngx_resolver_create_name_query(r, rn, name);
660 664
661 if (rc == NGX_ERROR) { 665 if (rc == NGX_ERROR) {
662 goto failed; 666 goto failed;
663 } 667 }
664 668
717 rn->valid = 0; 721 rn->valid = 0;
718 rn->ttl = NGX_MAX_UINT32_VALUE; 722 rn->ttl = NGX_MAX_UINT32_VALUE;
719 rn->waiting = ctx; 723 rn->waiting = ctx;
720 724
721 ctx->state = NGX_AGAIN; 725 ctx->state = NGX_AGAIN;
726
727 do {
728 ctx->node = rn;
729 ctx = ctx->next;
730 } while (ctx);
722 731
723 return NGX_AGAIN; 732 return NGX_AGAIN;
724 733
725 failed: 734 failed:
726 735
840 ngx_add_timer(ctx->event, ctx->timeout); 849 ngx_add_timer(ctx->event, ctx->timeout);
841 850
842 ctx->next = rn->waiting; 851 ctx->next = rn->waiting;
843 rn->waiting = ctx; 852 rn->waiting = ctx;
844 ctx->state = NGX_AGAIN; 853 ctx->state = NGX_AGAIN;
854 ctx->node = rn;
845 855
846 /* unlock addr mutex */ 856 /* unlock addr mutex */
847 857
848 return NGX_OK; 858 return NGX_OK;
849 } 859 }
925 rn->waiting = ctx; 935 rn->waiting = ctx;
926 936
927 /* unlock addr mutex */ 937 /* unlock addr mutex */
928 938
929 ctx->state = NGX_AGAIN; 939 ctx->state = NGX_AGAIN;
940 ctx->node = rn;
930 941
931 return NGX_OK; 942 return NGX_OK;
932 943
933 failed: 944 failed:
934 945
955 966
956 967
957 void 968 void
958 ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx) 969 ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
959 { 970 {
960 in_addr_t addr;
961 ngx_queue_t *expire_queue; 971 ngx_queue_t *expire_queue;
962 ngx_rbtree_t *tree; 972 ngx_rbtree_t *tree;
963 ngx_resolver_t *r; 973 ngx_resolver_t *r;
964 ngx_resolver_ctx_t *w, **p; 974 ngx_resolver_ctx_t *w, **p;
965 struct sockaddr_in *sin;
966 ngx_resolver_node_t *rn; 975 ngx_resolver_node_t *rn;
967 #if (NGX_HAVE_INET6)
968 uint32_t hash;
969 struct sockaddr_in6 *sin6;
970 #endif
971 976
972 r = ctx->resolver; 977 r = ctx->resolver;
973 978
974 switch (ctx->addr.sockaddr->sa_family) { 979 switch (ctx->addr.sockaddr->sa_family) {
975 980
994 999
995 /* lock addr mutex */ 1000 /* lock addr mutex */
996 1001
997 if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { 1002 if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
998 1003
999 switch (ctx->addr.sockaddr->sa_family) { 1004 rn = ctx->node;
1000
1001 #if (NGX_HAVE_INET6)
1002 case AF_INET6:
1003 sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;
1004 hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16);
1005 rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash);
1006 break;
1007 #endif
1008
1009 default: /* AF_INET */
1010 sin = (struct sockaddr_in *) ctx->addr.sockaddr;
1011 addr = ntohl(sin->sin_addr.s_addr);
1012 rn = ngx_resolver_lookup_addr(r, addr);
1013 }
1014 1005
1015 if (rn) { 1006 if (rn) {
1016 p = &rn->waiting; 1007 p = &rn->waiting;
1017 w = rn->waiting; 1008 w = rn->waiting;
1018 1009
2012 2003
2013 ctx = rn->waiting; 2004 ctx = rn->waiting;
2014 rn->waiting = NULL; 2005 rn->waiting = NULL;
2015 2006
2016 if (ctx) { 2007 if (ctx) {
2017 ctx->name = name; 2008
2018 2009 for (next = ctx; next; next = next->next) {
2019 (void) ngx_resolve_name_locked(r, ctx); 2010 next->node = NULL;
2011 }
2012
2013 (void) ngx_resolve_name_locked(r, ctx, &name);
2020 } 2014 }
2021 2015
2022 ngx_resolver_free(r, rn->query); 2016 ngx_resolver_free(r, rn->query);
2023 rn->query = NULL; 2017 rn->query = NULL;
2024 #if (NGX_HAVE_INET6) 2018 #if (NGX_HAVE_INET6)