1/* Test that pthread_exit does not clobber callee-saved registers.
2 Copyright (C) 2018-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <stdio.h>
20#include <support/check.h>
21#include <support/xthread.h>
22
23/* This test attempts to check that callee-saved registers are
24 restored to their original values when destructors are run after
25 pthread_exit is called. GCC PR 83641 causes this test to fail.
26
27 The constants have been chosen randomly and are magic values which
28 are used to detect whether registers have been clobbered. The idea
29 is that these values are hidden behind a compiler barrier and only
30 present in .rodata initially, so that it is less likely that they
31 are in a register by accident.
32
33 The checker class can be stored in registers, and the magic values
34 are directly loaded into these registers. The checker destructor
35 is eventually invoked by pthread_exit and calls one of the
36 check_magic functions to verify that the class contents (that is,
37 register value) is correct.
38
39 These tests are performed both for unsigned int and double values,
40 to cover different calling conventions. */
41
42template <class T>
43struct values
44{
45 T v0;
46 T v1;
47 T v2;
48 T v3;
49 T v4;
50};
51
52static const values<unsigned int> magic_values =
53 {
54 .v0: 0x57f7fc72,
55 .v1: 0xe582daba,
56 .v2: 0x5f6ac994,
57 .v3: 0x35efddb7,
58 .v4: 0x1fbf5a74,
59 };
60
61static const values<double> magic_values_double =
62 {
63 .v0: 0.6764041905675465,
64 .v1: 0.9533336788140494,
65 .v2: 0.6091161359041452,
66 .v3: 0.7668653957125336,
67 .v4: 0.010374520235509666,
68 };
69
70/* Special index value which tells check_magic that no check should be
71 performed. */
72enum { no_check = -1 };
73
74/* Check that VALUE is the magic value for INDEX, behind a compiler
75 barrier. */
76__attribute__ ((noinline, noclone, weak))
77void
78check_magic (int index, unsigned int value)
79{
80 switch (index)
81 {
82 case 0:
83 TEST_COMPARE (value, magic_values.v0);
84 break;
85 case 1:
86 TEST_COMPARE (value, magic_values.v1);
87 break;
88 case 2:
89 TEST_COMPARE (value, magic_values.v2);
90 break;
91 case 3:
92 TEST_COMPARE (value, magic_values.v3);
93 break;
94 case 4:
95 TEST_COMPARE (value, magic_values.v4);
96 break;
97 case no_check:
98 break;
99 default:
100 FAIL_EXIT1 ("invalid magic value index %d", index);
101 }
102}
103
104/* Check that VALUE is the magic value for INDEX, behind a compiler
105 barrier. Double variant. */
106__attribute__ ((noinline, noclone, weak))
107void
108check_magic (int index, double value)
109{
110 switch (index)
111 {
112 case 0:
113 TEST_VERIFY (value == magic_values_double.v0);
114 break;
115 case 1:
116 TEST_VERIFY (value == magic_values_double.v1);
117 break;
118 case 2:
119 TEST_VERIFY (value == magic_values_double.v2);
120 break;
121 case 3:
122 TEST_VERIFY (value == magic_values_double.v3);
123 break;
124 case 4:
125 TEST_VERIFY (value == magic_values_double.v4);
126 break;
127 case no_check:
128 break;
129 default:
130 FAIL_EXIT1 ("invalid magic value index %d", index);
131 }
132}
133
134/* Store a magic value and check, via the destructor, that it has the
135 expected value. */
136template <class T, int I>
137struct checker
138{
139 T value;
140
141 checker (T v)
142 : value (v)
143 {
144 }
145
146 ~checker ()
147 {
148 check_magic (I, value);
149 }
150};
151
152/* The functions call_pthread_exit_0, call_pthread_exit_1,
153 call_pthread_exit are used to call pthread_exit indirectly, with
154 the intent of clobbering the register values. */
155
156__attribute__ ((noinline, noclone, weak))
157void
158call_pthread_exit_0 (const values<unsigned int> *pvalues)
159{
160 checker<unsigned int, no_check> c0 (pvalues->v0);
161 checker<unsigned int, no_check> c1 (pvalues->v1);
162 checker<unsigned int, no_check> c2 (pvalues->v2);
163 checker<unsigned int, no_check> c3 (pvalues->v3);
164 checker<unsigned int, no_check> c4 (pvalues->v4);
165
166 pthread_exit (NULL);
167}
168
169__attribute__ ((noinline, noclone, weak))
170void
171call_pthread_exit_1 (const values<double> *pvalues)
172{
173 checker<double, no_check> c0 (pvalues->v0);
174 checker<double, no_check> c1 (pvalues->v1);
175 checker<double, no_check> c2 (pvalues->v2);
176 checker<double, no_check> c3 (pvalues->v3);
177 checker<double, no_check> c4 (pvalues->v4);
178
179 values<unsigned int> other_values = { .v0: 0, };
180 call_pthread_exit_0 (pvalues: &other_values);
181}
182
183__attribute__ ((noinline, noclone, weak))
184void
185call_pthread_exit ()
186{
187 values<double> other_values = { .v0: 0, };
188 call_pthread_exit_1 (pvalues: &other_values);
189}
190
191/* Create on-stack objects and check that their values are restored by
192 pthread_exit. If Nested is true, call pthread_exit indirectly via
193 call_pthread_exit. */
194template <class T, bool Nested>
195__attribute__ ((noinline, noclone, weak))
196void *
197threadfunc (void *closure)
198{
199 const values<T> *pvalues = static_cast<const values<T> *> (closure);
200
201 checker<T, 0> c0 (pvalues->v0);
202 checker<T, 1> c1 (pvalues->v1);
203 checker<T, 2> c2 (pvalues->v2);
204 checker<T, 3> c3 (pvalues->v3);
205 checker<T, 4> c4 (pvalues->v4);
206
207 if (Nested)
208 call_pthread_exit ();
209 else
210 pthread_exit (NULL);
211
212 /* This should not be reached. */
213 return const_cast<char *> ("");
214}
215
216static int
217do_test ()
218{
219 puts (s: "info: unsigned int, direct pthread_exit call");
220 pthread_t thr
221 = xpthread_create (NULL, thread_func: &threadfunc<unsigned int, false>,
222 closure: const_cast<values<unsigned int> *> (&magic_values));
223 TEST_VERIFY (xpthread_join (thr) == NULL);
224
225 puts (s: "info: double, direct pthread_exit call");
226 thr = xpthread_create (NULL, thread_func: &threadfunc<double, false>,
227 closure: const_cast<values<double> *> (&magic_values_double));
228 TEST_VERIFY (xpthread_join (thr) == NULL);
229
230 puts (s: "info: unsigned int, indirect pthread_exit call");
231 thr = xpthread_create (NULL, thread_func: &threadfunc<unsigned int, true>,
232 closure: const_cast<values<unsigned int> *> (&magic_values));
233 TEST_VERIFY (xpthread_join (thr) == NULL);
234
235 puts (s: "info: double, indirect pthread_exit call");
236 thr = xpthread_create (NULL, thread_func: &threadfunc<double, true>,
237 closure: const_cast<values<double> *> (&magic_values_double));
238 TEST_VERIFY (xpthread_join (thr) == NULL);
239
240 return 0;
241}
242
243#include <support/test-driver.c>
244

source code of glibc/nptl/tst-thread-exit-clobber.cc