comparison src/core/ngx_resolver.c @ 6359:dac6eda40475 stable-1.8

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 5557bf31e25d
children 93d70d87914c
comparison
equal deleted inserted replaced
6358:5557bf31e25d 6359:dac6eda40475
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,
373 return NGX_OK; 373 return NGX_OK;
374 } 374 }
375 375
376 /* lock name mutex */ 376 /* lock name mutex */
377 377
378 rc = ngx_resolve_name_locked(r, ctx); 378 rc = ngx_resolve_name_locked(r, ctx, &ctx->name);
379 379
380 if (rc == NGX_OK) { 380 if (rc == NGX_OK) {
381 return NGX_OK; 381 return NGX_OK;
382 } 382 }
383 383
400 400
401 401
402 void 402 void
403 ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) 403 ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)
404 { 404 {
405 uint32_t hash;
406 ngx_resolver_t *r; 405 ngx_resolver_t *r;
407 ngx_resolver_ctx_t *w, **p; 406 ngx_resolver_ctx_t *w, **p;
408 ngx_resolver_node_t *rn; 407 ngx_resolver_node_t *rn;
409 408
410 r = ctx->resolver; 409 r = ctx->resolver;
422 421
423 /* lock name mutex */ 422 /* lock name mutex */
424 423
425 if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { 424 if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
426 425
427 hash = ngx_crc32_short(ctx->name.data, ctx->name.len); 426 rn = ctx->node;
428
429 rn = ngx_resolver_lookup_name(r, &ctx->name, hash);
430 427
431 if (rn) { 428 if (rn) {
432 p = &rn->waiting; 429 p = &rn->waiting;
433 w = rn->waiting; 430 w = rn->waiting;
434 431
465 /* unlock alloc mutex */ 462 /* unlock alloc mutex */
466 } 463 }
467 464
468 465
469 static ngx_int_t 466 static ngx_int_t
470 ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) 467 ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx,
468 ngx_str_t *name)
471 { 469 {
472 uint32_t hash; 470 uint32_t hash;
473 ngx_int_t rc; 471 ngx_int_t rc;
472 ngx_str_t cname;
474 ngx_uint_t naddrs; 473 ngx_uint_t naddrs;
475 ngx_addr_t *addrs; 474 ngx_addr_t *addrs;
476 ngx_resolver_ctx_t *next, *last; 475 ngx_resolver_ctx_t *next, *last;
477 ngx_resolver_node_t *rn; 476 ngx_resolver_node_t *rn;
478 477
479 ngx_strlow(ctx->name.data, ctx->name.data, ctx->name.len); 478 ngx_strlow(name->data, name->data, name->len);
480 479
481 hash = ngx_crc32_short(ctx->name.data, ctx->name.len); 480 hash = ngx_crc32_short(name->data, name->len);
482 481
483 rn = ngx_resolver_lookup_name(r, &ctx->name, hash); 482 rn = ngx_resolver_lookup_name(r, name, hash);
484 483
485 if (rn) { 484 if (rn) {
486 485
487 /* ctx can be a list after NGX_RESOLVE_CNAME */ 486 /* ctx can be a list after NGX_RESOLVE_CNAME */
488 for (last = ctx; last->next; last = last->next); 487 for (last = ctx; last->next; last = last->next);
552 551
553 /* NGX_RESOLVE_CNAME */ 552 /* NGX_RESOLVE_CNAME */
554 553
555 if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { 554 if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) {
556 555
557 ctx->name.len = rn->cnlen; 556 cname.len = rn->cnlen;
558 ctx->name.data = rn->u.cname; 557 cname.data = rn->u.cname;
559 558
560 return ngx_resolve_name_locked(r, ctx); 559 return ngx_resolve_name_locked(r, ctx, &cname);
561 } 560 }
562 561
563 last->next = rn->waiting; 562 last->next = rn->waiting;
564 rn->waiting = NULL; 563 rn->waiting = NULL;
565 564
595 594
596 last->next = rn->waiting; 595 last->next = rn->waiting;
597 rn->waiting = ctx; 596 rn->waiting = ctx;
598 ctx->state = NGX_AGAIN; 597 ctx->state = NGX_AGAIN;
599 598
599 do {
600 ctx->node = rn;
601 ctx = ctx->next;
602 } while (ctx);
603
600 return NGX_AGAIN; 604 return NGX_AGAIN;
601 } 605 }
602 606
603 ngx_queue_remove(&rn->queue); 607 ngx_queue_remove(&rn->queue);
604 608
633 rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); 637 rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));
634 if (rn == NULL) { 638 if (rn == NULL) {
635 return NGX_ERROR; 639 return NGX_ERROR;
636 } 640 }
637 641
638 rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); 642 rn->name = ngx_resolver_dup(r, name->data, name->len);
639 if (rn->name == NULL) { 643 if (rn->name == NULL) {
640 ngx_resolver_free(r, rn); 644 ngx_resolver_free(r, rn);
641 return NGX_ERROR; 645 return NGX_ERROR;
642 } 646 }
643 647
644 rn->node.key = hash; 648 rn->node.key = hash;
645 rn->nlen = (u_short) ctx->name.len; 649 rn->nlen = (u_short) name->len;
646 rn->query = NULL; 650 rn->query = NULL;
647 #if (NGX_HAVE_INET6) 651 #if (NGX_HAVE_INET6)
648 rn->query6 = NULL; 652 rn->query6 = NULL;
649 #endif 653 #endif
650 654
651 ngx_rbtree_insert(&r->name_rbtree, &rn->node); 655 ngx_rbtree_insert(&r->name_rbtree, &rn->node);
652 } 656 }
653 657
654 rc = ngx_resolver_create_name_query(r, rn, &ctx->name); 658 rc = ngx_resolver_create_name_query(r, rn, name);
655 659
656 if (rc == NGX_ERROR) { 660 if (rc == NGX_ERROR) {
657 goto failed; 661 goto failed;
658 } 662 }
659 663
712 rn->valid = 0; 716 rn->valid = 0;
713 rn->ttl = NGX_MAX_UINT32_VALUE; 717 rn->ttl = NGX_MAX_UINT32_VALUE;
714 rn->waiting = ctx; 718 rn->waiting = ctx;
715 719
716 ctx->state = NGX_AGAIN; 720 ctx->state = NGX_AGAIN;
721
722 do {
723 ctx->node = rn;
724 ctx = ctx->next;
725 } while (ctx);
717 726
718 return NGX_AGAIN; 727 return NGX_AGAIN;
719 728
720 failed: 729 failed:
721 730
835 ngx_add_timer(ctx->event, ctx->timeout); 844 ngx_add_timer(ctx->event, ctx->timeout);
836 845
837 ctx->next = rn->waiting; 846 ctx->next = rn->waiting;
838 rn->waiting = ctx; 847 rn->waiting = ctx;
839 ctx->state = NGX_AGAIN; 848 ctx->state = NGX_AGAIN;
849 ctx->node = rn;
840 850
841 /* unlock addr mutex */ 851 /* unlock addr mutex */
842 852
843 return NGX_OK; 853 return NGX_OK;
844 } 854 }
920 rn->waiting = ctx; 930 rn->waiting = ctx;
921 931
922 /* unlock addr mutex */ 932 /* unlock addr mutex */
923 933
924 ctx->state = NGX_AGAIN; 934 ctx->state = NGX_AGAIN;
935 ctx->node = rn;
925 936
926 return NGX_OK; 937 return NGX_OK;
927 938
928 failed: 939 failed:
929 940
950 961
951 962
952 void 963 void
953 ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx) 964 ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
954 { 965 {
955 in_addr_t addr;
956 ngx_queue_t *expire_queue; 966 ngx_queue_t *expire_queue;
957 ngx_rbtree_t *tree; 967 ngx_rbtree_t *tree;
958 ngx_resolver_t *r; 968 ngx_resolver_t *r;
959 ngx_resolver_ctx_t *w, **p; 969 ngx_resolver_ctx_t *w, **p;
960 struct sockaddr_in *sin;
961 ngx_resolver_node_t *rn; 970 ngx_resolver_node_t *rn;
962 #if (NGX_HAVE_INET6)
963 uint32_t hash;
964 struct sockaddr_in6 *sin6;
965 #endif
966 971
967 r = ctx->resolver; 972 r = ctx->resolver;
968 973
969 switch (ctx->addr.sockaddr->sa_family) { 974 switch (ctx->addr.sockaddr->sa_family) {
970 975
989 994
990 /* lock addr mutex */ 995 /* lock addr mutex */
991 996
992 if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { 997 if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
993 998
994 switch (ctx->addr.sockaddr->sa_family) { 999 rn = ctx->node;
995
996 #if (NGX_HAVE_INET6)
997 case AF_INET6:
998 sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;
999 hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16);
1000 rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash);
1001 break;
1002 #endif
1003
1004 default: /* AF_INET */
1005 sin = (struct sockaddr_in *) ctx->addr.sockaddr;
1006 addr = ntohl(sin->sin_addr.s_addr);
1007 rn = ngx_resolver_lookup_addr(r, addr);
1008 }
1009 1000
1010 if (rn) { 1001 if (rn) {
1011 p = &rn->waiting; 1002 p = &rn->waiting;
1012 w = rn->waiting; 1003 w = rn->waiting;
1013 1004
1992 1983
1993 ctx = rn->waiting; 1984 ctx = rn->waiting;
1994 rn->waiting = NULL; 1985 rn->waiting = NULL;
1995 1986
1996 if (ctx) { 1987 if (ctx) {
1997 ctx->name = name; 1988
1998 1989 for (next = ctx; next; next = next->next) {
1999 (void) ngx_resolve_name_locked(r, ctx); 1990 next->node = NULL;
1991 }
1992
1993 (void) ngx_resolve_name_locked(r, ctx, &name);
2000 } 1994 }
2001 1995
2002 ngx_resolver_free(r, rn->query); 1996 ngx_resolver_free(r, rn->query);
2003 rn->query = NULL; 1997 rn->query = NULL;
2004 #if (NGX_HAVE_INET6) 1998 #if (NGX_HAVE_INET6)