The required TEST_COMPARE and C++ changes are included in the support/ rebase in glibc-rh1418978-1.patch commit 579396ee082565ab5f42ff166a264891223b7b82 Author: Florian Weimer Date: Mon Jan 8 14:57:25 2018 +0100 nptl: Add test for callee-saved register restore in pthread_exit GCC PR 83641 results in a miscompilation of libpthread, which causes pthread_exit not to restore callee-saved registers before running destructors for objects on the stack. This test detects this situation: info: unsigned int, direct pthread_exit call tst-thread-exit-clobber.cc:80: numeric comparison failure left: 4148288912 (0xf741dd90); from: value right: 1600833940 (0x5f6ac994); from: magic_values.v2 info: double, direct pthread_exit call info: unsigned int, indirect pthread_exit call info: double, indirect pthread_exit call error: 1 test failures Index: glibc-2.17-c758a686/nptl/Makefile =================================================================== --- glibc-2.17-c758a686.orig/nptl/Makefile +++ glibc-2.17-c758a686/nptl/Makefile @@ -199,6 +199,7 @@ CFLAGS-send.c = -fexceptions -fasynchron CFLAGS-pt-system.c = -fexceptions CFLAGS-tst-minstack-throw.cc = -std=gnu++11 +CFLAGS-tst-thread-exit-clobber.o = -std=gnu++11 tests = tst-typesizes \ tst-attr1 tst-attr2 tst-attr3 tst-default-attr \ @@ -269,7 +270,8 @@ tests = tst-typesizes \ tst-getpid1 tst-getpid2 tst-getpid3 \ tst-initializers1 $(patsubst %,tst-initializers1-%,c89 gnu89 c99 gnu99) \ tst-mutex-errorcheck \ - tst-minstack-cancel tst-minstack-exit tst-minstack-throw + tst-minstack-cancel tst-minstack-exit tst-minstack-throw \ + tst-thread-exit-clobber xtests = tst-setuid1 tst-setuid1-static tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 test-srcs = tst-oddstacklimit @@ -529,6 +531,7 @@ $(objpfx)tst-_res1: $(objpfx)tst-_res1mo LDLIBS-tst-cancel24 = $(no-as-needed) -lstdc++ LDLIBS-tst-cancel24-static = $(LDLIBS-tst-cancel24) LDLIBS-tst-minstack-throw = -lstdc++ +LDLIBS-tst-thread-exit-clobber = -lstdc++ extra-B-pthread.so = -B$(common-objpfx)nptl/ $(objpfx)libpthread.so: $(addprefix $(objpfx),$(crti-objs) $(crtn-objs)) Index: glibc-2.17-c758a686/nptl/tst-thread-exit-clobber.cc =================================================================== --- /dev/null +++ glibc-2.17-c758a686/nptl/tst-thread-exit-clobber.cc @@ -0,0 +1,243 @@ +/* Test that pthread_exit does not clobber callee-saved registers. + 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 + . */ + +#include +#include +#include + +/* This test attempts to check that callee-saved registers are + restored to their original values when destructors are run after + pthread_exit is called. GCC PR 83641 causes this test to fail. + + The constants have been chosen randomly and are magic values which + are used to detect whether registers have been clobbered. The idea + is that these values are hidden behind a compiler barrier and only + present in .rodata initially, so that it is less likely that they + are in a register by accident. + + The checker class can be stored in registers, and the magic values + are directly loaded into these registers. The checker destructor + is eventually invoked by pthread_exit and calls one of the + check_magic functions to verify that the class contents (that is, + register value) is correct. + + These tests are performed both for unsigned int and double values, + to cover different calling conventions. */ + +template +struct values +{ + T v0; + T v1; + T v2; + T v3; + T v4; +}; + +static const values magic_values = + { + 0x57f7fc72, + 0xe582daba, + 0x5f6ac994, + 0x35efddb7, + 0x1fbf5a74, + }; + +static const values magic_values_double = + { + 0.6764041905675465, + 0.9533336788140494, + 0.6091161359041452, + 0.7668653957125336, + 0.010374520235509666, + }; + +/* Special index value which tells check_magic that no check should be + performed. */ +enum { no_check = -1 }; + +/* Check that VALUE is the magic value for INDEX, behind a compiler + barrier. */ +__attribute__ ((noinline, noclone, weak)) +void +check_magic (int index, unsigned int value) +{ + switch (index) + { + case 0: + TEST_COMPARE (value, magic_values.v0); + break; + case 1: + TEST_COMPARE (value, magic_values.v1); + break; + case 2: + TEST_COMPARE (value, magic_values.v2); + break; + case 3: + TEST_COMPARE (value, magic_values.v3); + break; + case 4: + TEST_COMPARE (value, magic_values.v4); + break; + case no_check: + break; + default: + FAIL_EXIT1 ("invalid magic value index %d", index); + } +} + +/* Check that VALUE is the magic value for INDEX, behind a compiler + barrier. Double variant. */ +__attribute__ ((noinline, noclone, weak)) +void +check_magic (int index, double value) +{ + switch (index) + { + case 0: + TEST_VERIFY (value == magic_values_double.v0); + break; + case 1: + TEST_VERIFY (value == magic_values_double.v1); + break; + case 2: + TEST_VERIFY (value == magic_values_double.v2); + break; + case 3: + TEST_VERIFY (value == magic_values_double.v3); + break; + case 4: + TEST_VERIFY (value == magic_values_double.v4); + break; + case no_check: + break; + default: + FAIL_EXIT1 ("invalid magic value index %d", index); + } +} + +/* Store a magic value and check, via the destructor, that it has the + expected value. */ +template +struct checker +{ + T value; + + checker (T v) + : value (v) + { + } + + ~checker () + { + check_magic (I, value); + } +}; + +/* The functions call_pthread_exit_0, call_pthread_exit_1, + call_pthread_exit are used to call pthread_exit indirectly, with + the intent of clobbering the register values. */ + +__attribute__ ((noinline, noclone, weak)) +void +call_pthread_exit_0 (const values *pvalues) +{ + checker c0 (pvalues->v0); + checker c1 (pvalues->v1); + checker c2 (pvalues->v2); + checker c3 (pvalues->v3); + checker c4 (pvalues->v4); + + pthread_exit (NULL); +} + +__attribute__ ((noinline, noclone, weak)) +void +call_pthread_exit_1 (const values *pvalues) +{ + checker c0 (pvalues->v0); + checker c1 (pvalues->v1); + checker c2 (pvalues->v2); + checker c3 (pvalues->v3); + checker c4 (pvalues->v4); + + values other_values = { 0, }; + call_pthread_exit_0 (&other_values); +} + +__attribute__ ((noinline, noclone, weak)) +void +call_pthread_exit () +{ + values other_values = { 0, }; + call_pthread_exit_1 (&other_values); +} + +/* Create on-stack objects and check that their values are restored by + pthread_exit. If Nested is true, call pthread_exit indirectly via + call_pthread_exit. */ +template +__attribute__ ((noinline, noclone, weak)) +void * +threadfunc (void *closure) +{ + const values *pvalues = static_cast *> (closure); + + checker c0 (pvalues->v0); + checker c1 (pvalues->v1); + checker c2 (pvalues->v2); + checker c3 (pvalues->v3); + checker c4 (pvalues->v4); + + if (Nested) + call_pthread_exit (); + else + pthread_exit (NULL); + + /* This should not be reached. */ + return const_cast (""); +} + +static int +do_test () +{ + puts ("info: unsigned int, direct pthread_exit call"); + pthread_t thr + = xpthread_create (NULL, &threadfunc, + const_cast *> (&magic_values)); + TEST_VERIFY (xpthread_join (thr) == NULL); + + puts ("info: double, direct pthread_exit call"); + thr = xpthread_create (NULL, &threadfunc, + const_cast *> (&magic_values_double)); + TEST_VERIFY (xpthread_join (thr) == NULL); + + puts ("info: unsigned int, indirect pthread_exit call"); + thr = xpthread_create (NULL, &threadfunc, + const_cast *> (&magic_values)); + TEST_VERIFY (xpthread_join (thr) == NULL); + + puts ("info: double, indirect pthread_exit call"); + thr = xpthread_create (NULL, &threadfunc, + const_cast *> (&magic_values_double)); + TEST_VERIFY (xpthread_join (thr) == NULL); + + return 0; +} + +#include