| # |
| # Based on this upstream commit: |
| # |
| # commit d8dd00805b8f3a011735d7a407097fb1c408d867 |
| # Author: H.J. Lu <hjl.tools@gmail.com> |
| # Date: Fri Nov 28 07:54:07 2014 -0800 |
| # |
| # Resize DTV if the current DTV isn't big enough |
| # |
| # This patch changes _dl_allocate_tls_init to resize DTV if the current DTV |
| # isn't big enough. Tested on X86-64, x32 and ia32. |
| # |
| # [BZ #13862] |
| # * elf/dl-tls.c: Include <atomic.h>. |
| # (oom): Remove #ifdef SHARED/#endif. |
| # (_dl_static_dtv, _dl_initial_dtv): Moved before ... |
| # (_dl_resize_dtv): This. Extracted from _dl_update_slotinfo. |
| # (_dl_allocate_tls_init): Resize DTV if the current DTV isn't |
| # big enough. |
| # (_dl_update_slotinfo): Call _dl_resize_dtv to resize DTV. |
| # * nptl/Makefile (tests): Add tst-stack4. |
| # (modules-names): Add tst-stack4mod. |
| # ($(objpfx)tst-stack4): New. |
| # (tst-stack4mod.sos): Likewise. |
| # ($(objpfx)tst-stack4.out): Likewise. |
| # ($(tst-stack4mod.sos)): Likewise. |
| # (clean): Likewise. |
| # * nptl/tst-stack4.c: New file. |
| # * nptl/tst-stack4mod.c: Likewise. |
| # |
| diff -urN glibc-2.17-c758a686/elf/dl-tls.c glibc-2.17-c758a686/elf/dl-tls.c |
| |
| |
| @@ -24,6 +24,7 @@ |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/param.h> |
| +#include <atomic.h> |
| |
| #include <tls.h> |
| #include <dl-tls.h> |
| @@ -35,14 +36,12 @@ |
| |
| |
| /* Out-of-memory handler. */ |
| -#ifdef SHARED |
| static void |
| __attribute__ ((__noreturn__)) |
| oom (void) |
| { |
| _dl_fatal_printf ("cannot allocate memory for thread-local data: ABORT\n"); |
| } |
| -#endif |
| |
| |
| size_t |
| @@ -392,6 +391,52 @@ |
| return result; |
| } |
| |
| +static dtv_t * |
| +_dl_resize_dtv (dtv_t *dtv) |
| +{ |
| + /* Resize the dtv. */ |
| + dtv_t *newp; |
| + /* Load GL(dl_tls_max_dtv_idx) atomically since it may be written to by |
| + other threads concurrently. -- We don't have the required atomic |
| + infrastructure to load dl_tls_max_dtv_idx atomically, but on all the |
| + architectures we care about it should load atomically. If this had |
| + an atomic_load_acquire we would still be missing the releases for |
| + the writes. */ |
| + size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS; |
| + size_t oldsize = dtv[-1].counter; |
| + |
| +#if SHARED |
| + if (dtv == GL(dl_initial_dtv)) |
| + { |
| + /* This is the initial dtv that was either statically allocated in |
| + __libc_setup_tls or allocated during rtld startup using the |
| + dl-minimal.c malloc instead of the real malloc. We can't free |
| + it, we have to abandon the old storage. */ |
| + |
| + newp = malloc ((2 + newsize) * sizeof (dtv_t)); |
| + if (newp == NULL) |
| + oom (); |
| + memcpy (newp, &dtv[-1], (2 + oldsize) * sizeof (dtv_t)); |
| + } |
| + else |
| +#endif |
| + { |
| + newp = realloc (&dtv[-1], |
| + (2 + newsize) * sizeof (dtv_t)); |
| + if (newp == NULL) |
| + oom (); |
| + } |
| + |
| + newp[0].counter = newsize; |
| + |
| + /* Clear the newly allocated part. */ |
| + memset (newp + 2 + oldsize, '\0', |
| + (newsize - oldsize) * sizeof (dtv_t)); |
| + |
| + /* Return the generation counter. */ |
| + return &newp[1]; |
| +} |
| + |
| |
| void * |
| internal_function |
| @@ -406,6 +451,16 @@ |
| size_t total = 0; |
| size_t maxgen = 0; |
| |
| + /* Check if the current dtv is big enough. */ |
| + if (dtv[-1].counter < GL(dl_tls_max_dtv_idx)) |
| + { |
| + /* Resize the dtv. */ |
| + dtv = _dl_resize_dtv (dtv); |
| + |
| + /* Install this new dtv in the thread data structures. */ |
| + INSTALL_DTV (result, &dtv[-1]); |
| + } |
| + |
| /* We have to prepare the dtv for all currently loaded modules using |
| TLS. For those which are dynamically loaded we add the values |
| indicating deferred allocation. */ |
| @@ -637,41 +692,10 @@ |
| assert (total + cnt == modid); |
| if (dtv[-1].counter < modid) |
| { |
| - /* Reallocate the dtv. */ |
| - dtv_t *newp; |
| - size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS; |
| - size_t oldsize = dtv[-1].counter; |
| - |
| - assert (map->l_tls_modid <= newsize); |
| - |
| - if (dtv == GL(dl_initial_dtv)) |
| - { |
| - /* This is the initial dtv that was allocated |
| - during rtld startup using the dl-minimal.c |
| - malloc instead of the real malloc. We can't |
| - free it, we have to abandon the old storage. */ |
| - |
| - newp = malloc ((2 + newsize) * sizeof (dtv_t)); |
| - if (newp == NULL) |
| - oom (); |
| - memcpy (newp, &dtv[-1], (2 + oldsize) * sizeof (dtv_t)); |
| - } |
| - else |
| - { |
| - newp = realloc (&dtv[-1], |
| - (2 + newsize) * sizeof (dtv_t)); |
| - if (newp == NULL) |
| - oom (); |
| - } |
| - |
| - newp[0].counter = newsize; |
| - |
| - /* Clear the newly allocated part. */ |
| - memset (newp + 2 + oldsize, '\0', |
| - (newsize - oldsize) * sizeof (dtv_t)); |
| + /* Resize the dtv. */ |
| + dtv = _dl_resize_dtv (dtv); |
| |
| - /* Point dtv to the generation counter. */ |
| - dtv = &newp[1]; |
| + assert (modid <= dtv[-1].counter); |
| |
| /* Install this new dtv in the thread data |
| structures. */ |
| diff -urN glibc-2.17-c758a686/nptl/Makefile glibc-2.17-c758a686/nptl/Makefile |
| |
| |
| @@ -251,7 +251,7 @@ |
| tst-exec1 tst-exec2 tst-exec3 tst-exec4 \ |
| tst-exit1 tst-exit2 tst-exit3 \ |
| tst-stdio1 tst-stdio2 \ |
| - tst-stack1 tst-stack2 tst-stack3 tst-pthread-getattr \ |
| + tst-stack1 tst-stack2 tst-stack3 tst-stack4 tst-pthread-getattr \ |
| tst-unload \ |
| tst-dlsym1 \ |
| tst-sysconf \ |
| @@ -297,7 +297,7 @@ |
| |
| modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \ |
| tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \ |
| - tst-tls5modd tst-tls5mode tst-tls5modf \ |
| + tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \ |
| tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod |
| extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) tst-cleanup4aux.o |
| test-extras += $(modules-names) tst-cleanup4aux |
| @@ -459,6 +459,19 @@ |
| $(common-objpfx)malloc/mtrace $(objpfx)tst-stack3.mtrace > $@ |
| generated += tst-stack3-mem tst-stack3.mtrace |
| |
| +$(objpfx)tst-stack4: $(libdl) $(shared-thread-library) |
| +tst-stack4mod.sos=$(shell for i in 0 1 2 3 4 5 6 7 8 9 10 \ |
| + 11 12 13 14 15 16 17 18 19; do \ |
| + for j in 0 1 2 3 4 5 6 7 8 9 10 \ |
| + 11 12 13 14 15 16 17 18 19; do \ |
| + echo $(objpfx)tst-stack4mod-$$i-$$j.so; \ |
| + done; done) |
| +$(objpfx)tst-stack4.out: $(tst-stack4mod.sos) |
| +$(tst-stack4mod.sos): $(objpfx)tst-stack4mod.so |
| + cp -f $< $@ |
| +clean: |
| + rm -f $(tst-stack4mod.sos) |
| + |
| $(objpfx)tst-cleanup4: $(objpfx)tst-cleanup4aux.o $(shared-thread-library) |
| $(objpfx)tst-cleanupx4: $(objpfx)tst-cleanup4aux.o $(shared-thread-library) |
| |
| diff -urN glibc-2.17-c758a686/nptl/tst-stack4.c glibc-2.17-c758a686/nptl/tst-stack4.c |
| |
| |
| @@ -0,0 +1,159 @@ |
| +/* Test DTV size oveflow when pthread_create reuses old DTV and TLS is |
| + used by dlopened shared object. |
| + Copyright (C) 2014 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 <stdio.h> |
| +#include <stdint.h> |
| +#include <dlfcn.h> |
| +#include <assert.h> |
| +#include <pthread.h> |
| + |
| +/* The choices of thread count, and file counts are arbitary. |
| + The point is simply to run enough threads that an exiting |
| + thread has it's stack reused by another thread at the same |
| + time as new libraries have been loaded. */ |
| +#define DSO_SHARED_FILES 20 |
| +#define DSO_OPEN_THREADS 20 |
| +#define DSO_EXEC_THREADS 2 |
| + |
| +/* Used to make sure that only one thread is calling dlopen and dlclose |
| + at a time. */ |
| +pthread_mutex_t g_lock; |
| + |
| +typedef void (*function) (void); |
| + |
| +void * |
| +dso_invoke(void *dso_fun) |
| +{ |
| + function *fun_vec = (function *) dso_fun; |
| + int dso; |
| + |
| + for (dso = 0; dso < DSO_SHARED_FILES; dso++) |
| + (*fun_vec[dso]) (); |
| + |
| + pthread_exit (NULL); |
| +} |
| + |
| +void * |
| +dso_process (void * p) |
| +{ |
| + void *handle[DSO_SHARED_FILES]; |
| + function fun_vec[DSO_SHARED_FILES]; |
| + char dso_path[DSO_SHARED_FILES][100]; |
| + int dso; |
| + uintptr_t t = (uintptr_t) p; |
| + |
| + /* Open DSOs and get a function. */ |
| + for (dso = 0; dso < DSO_SHARED_FILES; dso++) |
| + { |
| + sprintf (dso_path[dso], "tst-stack4mod-%i-%i.so", t, dso); |
| + |
| + pthread_mutex_lock (&g_lock); |
| + |
| + handle[dso] = dlopen (dso_path[dso], RTLD_NOW); |
| + assert (handle[dso]); |
| + |
| + fun_vec[dso] = (function) dlsym (handle[dso], "function"); |
| + assert (fun_vec[dso]); |
| + |
| + pthread_mutex_unlock (&g_lock); |
| + } |
| + |
| + /* Spawn workers. */ |
| + pthread_t thread[DSO_EXEC_THREADS]; |
| + int i, ret; |
| + uintptr_t result = 0; |
| + for (i = 0; i < DSO_EXEC_THREADS; i++) |
| + { |
| + pthread_mutex_lock (&g_lock); |
| + ret = pthread_create (&thread[i], NULL, dso_invoke, (void *) fun_vec); |
| + if (ret != 0) |
| + { |
| + printf ("pthread_create failed: %d\n", ret); |
| + result = 1; |
| + } |
| + pthread_mutex_unlock (&g_lock); |
| + } |
| + |
| + if (!result) |
| + for (i = 0; i < DSO_EXEC_THREADS; i++) |
| + { |
| + ret = pthread_join (thread[i], NULL); |
| + if (ret != 0) |
| + { |
| + printf ("pthread_join failed: %d\n", ret); |
| + result = 1; |
| + } |
| + } |
| + |
| + /* Close all DSOs. */ |
| + for (dso = 0; dso < DSO_SHARED_FILES; dso++) |
| + { |
| + pthread_mutex_lock (&g_lock); |
| + dlclose (handle[dso]); |
| + pthread_mutex_unlock (&g_lock); |
| + } |
| + |
| + /* Exit. */ |
| + pthread_exit ((void *) result); |
| +} |
| + |
| +static int |
| +do_test (void) |
| +{ |
| + pthread_t thread[DSO_OPEN_THREADS]; |
| + int i,j; |
| + int ret; |
| + int result = 0; |
| + |
| + pthread_mutex_init (&g_lock, NULL); |
| + |
| + /* 100 is arbitrary here and is known to trigger PR 13862. */ |
| + for (j = 0; j < 100; j++) |
| + { |
| + for (i = 0; i < DSO_OPEN_THREADS; i++) |
| + { |
| + ret = pthread_create (&thread[i], NULL, dso_process, |
| + (void *) (uintptr_t) i); |
| + if (ret != 0) |
| + { |
| + printf ("pthread_create failed: %d\n", ret); |
| + result = 1; |
| + } |
| + } |
| + |
| + if (result) |
| + break; |
| + |
| + for (i = 0; i < DSO_OPEN_THREADS; i++) |
| + { |
| + ret = pthread_join (thread[i], NULL); |
| + if (ret != 0) |
| + { |
| + printf ("pthread_join failed: %d\n", ret); |
| + result = 1; |
| + } |
| + } |
| + } |
| + |
| + return result; |
| +} |
| + |
| +#define TEST_FUNCTION do_test () |
| +#define TIMEOUT 100 |
| +#include "../test-skeleton.c" |
| diff -urN glibc-2.17-c758a686/nptl/tst-stack4mod.c glibc-2.17-c758a686/nptl/tst-stack4mod.c |
| |
| |
| @@ -0,0 +1,28 @@ |
| +/* This tests DTV usage with TLS in dlopened shared object. |
| + Copyright (C) 2014 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/>. */ |
| + |
| +/* 256 is arbitrary here and is known to trigger PR 13862. */ |
| +__thread int var[256] attribute_hidden = {0}; |
| + |
| +void |
| +function (void) |
| +{ |
| + int i; |
| + for (i = 0; i < sizeof (var) / sizeof (int); i++) |
| + var[i] = i; |
| +} |