Mercurial > hg > nginx
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) |