diff options
author | Dmitry V. Levin <ldv@altlinux.org> | 2015-06-18 21:40:46 +0000 |
---|---|---|
committer | Dmitry V. Levin <ldv@altlinux.org> | 2015-06-23 09:37:28 +0000 |
commit | b57525f1a376149840f740a31535681c07152ba4 (patch) | |
tree | c520a3234fda67b9c99562eb1b06e090e9cc2bb5 /resolv | |
parent | 47852c972d1ad80d8b38d9e94507b27df0ede421 (diff) | |
download | glibc-b57525f1a376149840f740a31535681c07152ba4.tar.gz |
Fix potential hanging of gethostbyaddr_r/gethostbyname_r
When "reorder" resolver option is enabled, threads of a multi-threaded process
could hang in gethostbyaddr_r, gethostbyname_r, or gethostbyname2_r.
Due to a trivial bug in _res_hconf_reorder_addrs, simultaneous
invocations of this function in a multi-threaded process could result to
_res_hconf_reorder_addrs returning without releasing the lock it holds,
causing other threads to block indefinitely while waiting for the lock
that is not going to be released.
[BZ #17977]
* resolv/res_hconf.c (_res_hconf_reorder_addrs): Fix unlocking
when initializing interface list, based on the bug analysis
and the patch proposed by Eric Newton.
* resolv/tst-res_hconf_reorder.c: New test.
* resolv/Makefile [$(have-thread-library) = yes] (tests): Add
tst-res_hconf_reorder.
($(objpfx)tst-res_hconf_reorder): Depend on $(libdl)
and $(shared-thread-library).
(tst-res_hconf_reorder-ENV): New variable.
Diffstat (limited to 'resolv')
-rw-r--r-- | resolv/Makefile | 4 | ||||
-rw-r--r-- | resolv/res_hconf.c | 4 | ||||
-rw-r--r-- | resolv/tst-res_hconf_reorder.c | 112 |
3 files changed, 118 insertions, 2 deletions
diff --git a/resolv/Makefile b/resolv/Makefile index f62eea4fb8..3509d98e2d 100644 --- a/resolv/Makefile +++ b/resolv/Makefile @@ -39,6 +39,7 @@ extra-libs := libresolv libnss_dns ifeq ($(have-thread-library),yes) extra-libs += libanl routines += gai_sigqueue +tests += tst-res_hconf_reorder endif extra-libs-others = $(extra-libs) libresolv-routines := gethnamaddr res_comp res_debug \ @@ -98,6 +99,9 @@ $(objpfx)libanl.so: $(shared-thread-library) $(objpfx)ga_test: $(objpfx)libanl.so $(shared-thread-library) +$(objpfx)tst-res_hconf_reorder: $(libdl) $(shared-thread-library) +tst-res_hconf_reorder-ENV = RESOLV_REORDER=on + $(objpfx)tst-leaks: $(objpfx)libresolv.so tst-leaks-ENV = MALLOC_TRACE=$(objpfx)tst-leaks.mtrace $(objpfx)mtrace-tst-leaks.out: $(objpfx)tst-leaks.out diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c index c9642ce583..0d4f3f45bc 100644 --- a/resolv/res_hconf.c +++ b/resolv/res_hconf.c @@ -473,10 +473,10 @@ _res_hconf_reorder_addrs (struct hostent *hp) errno = save; num_ifs = new_num_ifs; - - __libc_lock_unlock (lock); } + __libc_lock_unlock (lock); + __close (sd); } diff --git a/resolv/tst-res_hconf_reorder.c b/resolv/tst-res_hconf_reorder.c new file mode 100644 index 0000000000..1e7e0e2fa5 --- /dev/null +++ b/resolv/tst-res_hconf_reorder.c @@ -0,0 +1,112 @@ +/* BZ #17977 _res_hconf_reorder_addrs test. + + Copyright (C) 2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <dlfcn.h> +#include <pthread.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> + +static struct timespec ts; + +/* The first thread that gets a lock in _res_hconf_reorder_addrs() + should hold the lock long enough to make two other threads blocked. + This is achieved by slowing down realloc(3) that is called several times + by _res_hconf_reorder_addrs(). */ + +void * +realloc (void *ptr, size_t len) +{ + static void *(*fun) (void *, size_t); + + if (!fun) + fun = dlsym (RTLD_NEXT, "realloc"); + + if (ts.tv_nsec) + nanosleep (&ts, NULL); + + return (*fun) (ptr, len); +} + +static void * +resolve (void *arg) +{ + struct in_addr addr; + struct hostent ent; + struct hostent *result; + int err; + char buf[1024]; + + addr.s_addr = htonl (INADDR_LOOPBACK); + (void) gethostbyaddr_r ((void *) &addr, sizeof (addr), AF_INET, + &ent, buf, sizeof (buf), &result, &err); + return arg; +} + +static int +do_test (void) +{ + #define N 3 + pthread_t thr[N]; + unsigned int i; + int result = 0; + + /* turn on realloc slowdown */ + ts.tv_nsec = 100000000; + + for (i = 0; i < N; ++i) + { + int rc = pthread_create (&thr[i], NULL, resolve, NULL); + + if (rc) + { + printf ("pthread_create: %s\n", strerror(rc)); + exit (1); + } + } + + for (i = 0; i < N; ++i) + { + void *retval; + int rc = pthread_join (thr[i], &retval); + + if (rc) + { + printf ("pthread_join: %s\n", strerror(rc)); + exit (1); + } + if (retval) + { + printf ("thread %u exit status %p\n", i, retval); + result = 1; + } + } + + /* turn off realloc slowdown, no longer needed */ + ts.tv_nsec = 0; + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" |