1 | /* Restartable Sequences NPTL test. |
2 | Copyright (C) 2021-2024 Free Software Foundation, Inc. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | /* These tests validate that rseq is registered from various execution |
19 | contexts (main thread, destructor, other threads, other threads created |
20 | from destructor, forked process (without exec), pthread_atfork handlers, |
21 | pthread setspecific destructors, signal handlers, atexit handlers). |
22 | |
23 | See the Linux kernel selftests for extensive rseq stress-tests. */ |
24 | |
25 | #include <stdio.h> |
26 | #include <support/check.h> |
27 | #include <support/xthread.h> |
28 | #include <sys/rseq.h> |
29 | #include <unistd.h> |
30 | |
31 | #ifdef RSEQ_SIG |
32 | # include <array_length.h> |
33 | # include <errno.h> |
34 | # include <error.h> |
35 | # include <pthread.h> |
36 | # include <signal.h> |
37 | # include <stdlib.h> |
38 | # include <string.h> |
39 | # include <support/namespace.h> |
40 | # include <support/xsignal.h> |
41 | # include <syscall.h> |
42 | # include <sys/types.h> |
43 | # include <sys/wait.h> |
44 | # include "tst-rseq.h" |
45 | |
46 | static pthread_key_t rseq_test_key; |
47 | |
48 | static void |
49 | atfork_prepare (void) |
50 | { |
51 | if (!rseq_thread_registered ()) |
52 | { |
53 | printf (format: "error: rseq not registered in pthread atfork prepare\n" ); |
54 | support_record_failure (); |
55 | } |
56 | } |
57 | |
58 | static void |
59 | atfork_parent (void) |
60 | { |
61 | if (!rseq_thread_registered ()) |
62 | { |
63 | printf (format: "error: rseq not registered in pthread atfork parent\n" ); |
64 | support_record_failure (); |
65 | } |
66 | } |
67 | |
68 | static void |
69 | atfork_child (void) |
70 | { |
71 | if (!rseq_thread_registered ()) |
72 | { |
73 | printf (format: "error: rseq not registered in pthread atfork child\n" ); |
74 | support_record_failure (); |
75 | } |
76 | } |
77 | |
78 | static void |
79 | rseq_key_destructor (void *arg) |
80 | { |
81 | /* Cannot use deferred failure reporting after main returns. */ |
82 | if (!rseq_thread_registered ()) |
83 | FAIL_EXIT1 ("rseq not registered in pthread key destructor" ); |
84 | } |
85 | |
86 | static void |
87 | atexit_handler (void) |
88 | { |
89 | /* Cannot use deferred failure reporting after main returns. */ |
90 | if (!rseq_thread_registered ()) |
91 | FAIL_EXIT1 ("rseq not registered in atexit handler" ); |
92 | } |
93 | |
94 | /* Used to avoid -Werror=stringop-overread warning with |
95 | pthread_setspecific and GCC 11. */ |
96 | static char one = 1; |
97 | |
98 | static void |
99 | do_rseq_main_test (void) |
100 | { |
101 | TEST_COMPARE (atexit (atexit_handler), 0); |
102 | rseq_test_key = xpthread_key_create (destr_function: rseq_key_destructor); |
103 | TEST_COMPARE (pthread_atfork (atfork_prepare, atfork_parent, atfork_child), 0); |
104 | xraise (SIGUSR1); |
105 | TEST_COMPARE (pthread_setspecific (rseq_test_key, &one), 0); |
106 | TEST_VERIFY_EXIT (rseq_thread_registered ()); |
107 | } |
108 | |
109 | static void |
110 | cancel_routine (void *arg) |
111 | { |
112 | if (!rseq_thread_registered ()) |
113 | { |
114 | printf (format: "error: rseq not registered in cancel routine\n" ); |
115 | support_record_failure (); |
116 | } |
117 | } |
118 | |
119 | static pthread_barrier_t cancel_thread_barrier; |
120 | static pthread_cond_t cancel_thread_cond = PTHREAD_COND_INITIALIZER; |
121 | static pthread_mutex_t cancel_thread_mutex = PTHREAD_MUTEX_INITIALIZER; |
122 | |
123 | static void |
124 | test_cancel_thread (void) |
125 | { |
126 | pthread_cleanup_push (cancel_routine, NULL); |
127 | (void) xpthread_barrier_wait (barrier: &cancel_thread_barrier); |
128 | /* Wait forever until cancellation. */ |
129 | xpthread_cond_wait (cond: &cancel_thread_cond, mutex: &cancel_thread_mutex); |
130 | pthread_cleanup_pop (0); |
131 | } |
132 | |
133 | static void * |
134 | thread_function (void * arg) |
135 | { |
136 | int i = (int) (intptr_t) arg; |
137 | |
138 | xraise (SIGUSR1); |
139 | if (i == 0) |
140 | test_cancel_thread (); |
141 | TEST_COMPARE (pthread_setspecific (rseq_test_key, &one), 0); |
142 | return rseq_thread_registered () ? NULL : (void *) 1l; |
143 | } |
144 | |
145 | static void |
146 | sighandler (int sig) |
147 | { |
148 | if (!rseq_thread_registered ()) |
149 | { |
150 | printf (format: "error: rseq not registered in signal handler\n" ); |
151 | support_record_failure (); |
152 | } |
153 | } |
154 | |
155 | static void |
156 | setup_signals (void) |
157 | { |
158 | struct sigaction sa; |
159 | |
160 | sigemptyset (&sa.sa_mask); |
161 | sigaddset (&sa.sa_mask, SIGUSR1); |
162 | sa.sa_flags = 0; |
163 | sa.sa_handler = sighandler; |
164 | xsigaction (SIGUSR1, newact: &sa, NULL); |
165 | } |
166 | |
167 | static int |
168 | do_rseq_threads_test (int nr_threads) |
169 | { |
170 | pthread_t th[nr_threads]; |
171 | int i; |
172 | int result = 0; |
173 | |
174 | xpthread_barrier_init (barrier: &cancel_thread_barrier, NULL, count: 2); |
175 | |
176 | for (i = 0; i < nr_threads; ++i) |
177 | th[i] = xpthread_create (NULL, thread_func: thread_function, |
178 | closure: (void *) (intptr_t) i); |
179 | |
180 | (void) xpthread_barrier_wait (barrier: &cancel_thread_barrier); |
181 | |
182 | xpthread_cancel (thr: th[0]); |
183 | |
184 | for (i = 0; i < nr_threads; ++i) |
185 | { |
186 | void *v; |
187 | |
188 | v = xpthread_join (thr: th[i]); |
189 | if (i != 0 && v != NULL) |
190 | { |
191 | printf (format: "error: join %d successful, but child failed\n" , i); |
192 | result = 1; |
193 | } |
194 | else if (i == 0 && v == NULL) |
195 | { |
196 | printf (format: "error: join %d successful, child did not fail as expected\n" , i); |
197 | result = 1; |
198 | } |
199 | } |
200 | |
201 | xpthread_barrier_destroy (barrier: &cancel_thread_barrier); |
202 | |
203 | return result; |
204 | } |
205 | |
206 | static void |
207 | subprocess_callback (void *closure) |
208 | { |
209 | do_rseq_main_test (); |
210 | } |
211 | |
212 | static void |
213 | do_rseq_fork_test (void) |
214 | { |
215 | support_isolate_in_subprocess (callback: subprocess_callback, NULL); |
216 | } |
217 | |
218 | static int |
219 | do_rseq_test (void) |
220 | { |
221 | int t[] = { 1, 2, 6, 5, 4, 3, 50 }; |
222 | int i, result = 0; |
223 | |
224 | if (!rseq_available ()) |
225 | FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test" ); |
226 | setup_signals (); |
227 | xraise (SIGUSR1); |
228 | do_rseq_main_test (); |
229 | for (i = 0; i < array_length (t); i++) |
230 | if (do_rseq_threads_test (nr_threads: t[i])) |
231 | result = 1; |
232 | do_rseq_fork_test (); |
233 | return result; |
234 | } |
235 | |
236 | static void __attribute__ ((destructor)) |
237 | do_rseq_destructor_test (void) |
238 | { |
239 | /* Cannot use deferred failure reporting after main returns. */ |
240 | if (do_rseq_test ()) |
241 | FAIL_EXIT1 ("rseq not registered within destructor" ); |
242 | xpthread_key_delete (key: rseq_test_key); |
243 | } |
244 | |
245 | #else /* RSEQ_SIG */ |
246 | static int |
247 | do_rseq_test (void) |
248 | { |
249 | FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test" ); |
250 | return 0; |
251 | } |
252 | #endif /* RSEQ_SIG */ |
253 | |
254 | static int |
255 | do_test (void) |
256 | { |
257 | return do_rseq_test (); |
258 | } |
259 | |
260 | #include <support/test-driver.c> |
261 | |