diff options
author | Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com> | 2018-11-30 18:05:32 -0200 |
---|---|---|
committer | Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com> | 2018-11-30 18:05:32 -0200 |
commit | e5d262effe3a87164308a3f37e61b32d0348692a (patch) | |
tree | 2024005bb7e21dd547b7b25d0484c26b29bf43ee /nptl/tst-audit-threads.h | |
parent | 7e1d42400c1b8f03316fe14176133c8853cd3bbe (diff) | |
download | glibc-e5d262effe3a87164308a3f37e61b32d0348692a.tar.gz glibc-e5d262effe3a87164308a3f37e61b32d0348692a.tar.xz glibc-e5d262effe3a87164308a3f37e61b32d0348692a.zip |
Fix _dl_profile_fixup data-dependency issue (Bug 23690)
There is a data-dependency between the fields of struct l_reloc_result and the field used as the initialization guard. Users of the guard expect writes to the structure to be observable when they also observe the guard initialized. The solution for this problem is to use an acquire and release load and store to ensure previous writes to the structure are observable if the guard is initialized. The previous implementation used DL_FIXUP_VALUE_ADDR (l_reloc_result->addr) as the initialization guard, making it impossible for some architectures to load and store it atomically, i.e. hppa and ia64, due to its larger size. This commit adds an unsigned int to l_reloc_result to be used as the new initialization guard of the struct, making it possible to load and store it atomically in all architectures. The fix ensures that the values observed in l_reloc_result are consistent and do not lead to crashes. The algorithm is documented in the code in elf/dl-runtime.c (_dl_profile_fixup). Not all data races have been eliminated. Tested with build-many-glibcs and on powerpc, powerpc64, and powerpc64le. [BZ #23690] * elf/dl-runtime.c (_dl_profile_fixup): Guarantee memory modification order when accessing reloc_result->addr. * include/link.h (reloc_result): Add field init. * nptl/Makefile (tests): Add tst-audit-threads. (modules-names): Add tst-audit-threads-mod1 and tst-audit-threads-mod2. Add rules to build tst-audit-threads. * nptl/tst-audit-threads-mod1.c: New file. * nptl/tst-audit-threads-mod2.c: Likewise. * nptl/tst-audit-threads.c: Likewise. * nptl/tst-audit-threads.h: Likewise. Signed-off-by: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com> Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'nptl/tst-audit-threads.h')
-rw-r--r-- | nptl/tst-audit-threads.h | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/nptl/tst-audit-threads.h b/nptl/tst-audit-threads.h new file mode 100644 index 0000000000..1c9ecc08df --- /dev/null +++ b/nptl/tst-audit-threads.h @@ -0,0 +1,92 @@ +/* Helper header for test-audit-threads. + + Copyright (C) 2018 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/>. */ + +/* We use this helper to create a large number of functions, all of + which will be resolved lazily and thus have their PLT updated. + This is done to provide enough functions that we can statistically + observe a thread vs. PLT resolution failure if one exists. */ + +#define CONCAT(a, b) a ## b +#define NUM(x, y) CONCAT (x, y) + +#define FUNC10(x) \ + FUNC (NUM (x, 0)); \ + FUNC (NUM (x, 1)); \ + FUNC (NUM (x, 2)); \ + FUNC (NUM (x, 3)); \ + FUNC (NUM (x, 4)); \ + FUNC (NUM (x, 5)); \ + FUNC (NUM (x, 6)); \ + FUNC (NUM (x, 7)); \ + FUNC (NUM (x, 8)); \ + FUNC (NUM (x, 9)) + +#define FUNC100(x) \ + FUNC10 (NUM (x, 0)); \ + FUNC10 (NUM (x, 1)); \ + FUNC10 (NUM (x, 2)); \ + FUNC10 (NUM (x, 3)); \ + FUNC10 (NUM (x, 4)); \ + FUNC10 (NUM (x, 5)); \ + FUNC10 (NUM (x, 6)); \ + FUNC10 (NUM (x, 7)); \ + FUNC10 (NUM (x, 8)); \ + FUNC10 (NUM (x, 9)) + +#define FUNC1000(x) \ + FUNC100 (NUM (x, 0)); \ + FUNC100 (NUM (x, 1)); \ + FUNC100 (NUM (x, 2)); \ + FUNC100 (NUM (x, 3)); \ + FUNC100 (NUM (x, 4)); \ + FUNC100 (NUM (x, 5)); \ + FUNC100 (NUM (x, 6)); \ + FUNC100 (NUM (x, 7)); \ + FUNC100 (NUM (x, 8)); \ + FUNC100 (NUM (x, 9)) + +#define FUNC7000() \ + FUNC1000 (1); \ + FUNC1000 (2); \ + FUNC1000 (3); \ + FUNC1000 (4); \ + FUNC1000 (5); \ + FUNC1000 (6); \ + FUNC1000 (7); + +#ifdef FUNC +# undef FUNC +#endif + +#ifdef externnum +# define FUNC(x) extern int CONCAT (retNum, x) (void) +#endif + +#ifdef definenum +# define FUNC(x) int CONCAT (retNum, x) (void) { return x; } +#endif + +#ifdef callnum +# define FUNC(x) CONCAT (retNum, x) (); sync_all (x) +#endif + +/* A value of 7000 functions is chosen as an arbitrarily large + number of functions that will allow us enough attempts to + verify lazy resolution operation. */ +FUNC7000 (); |