t_sigstack.c revision 1.25 1 1.25 christos /* $NetBSD: t_sigstack.c,v 1.25 2025/05/12 14:46:19 christos Exp $ */
2 1.1 riastrad
3 1.1 riastrad /*-
4 1.1 riastrad * Copyright (c) 2024 The NetBSD Foundation, Inc.
5 1.1 riastrad * All rights reserved.
6 1.1 riastrad *
7 1.1 riastrad * Redistribution and use in source and binary forms, with or without
8 1.1 riastrad * modification, are permitted provided that the following conditions
9 1.1 riastrad * are met:
10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright
11 1.1 riastrad * notice, this list of conditions and the following disclaimer.
12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the
14 1.1 riastrad * documentation and/or other materials provided with the distribution.
15 1.1 riastrad *
16 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE.
27 1.1 riastrad */
28 1.1 riastrad
29 1.1 riastrad #include <sys/cdefs.h>
30 1.25 christos __RCSID("$NetBSD: t_sigstack.c,v 1.25 2025/05/12 14:46:19 christos Exp $");
31 1.1 riastrad
32 1.14 riastrad #include <dlfcn.h>
33 1.1 riastrad #include <setjmp.h>
34 1.1 riastrad #include <signal.h>
35 1.1 riastrad #include <stddef.h>
36 1.1 riastrad #include <stdlib.h>
37 1.1 riastrad #include <ucontext.h>
38 1.1 riastrad
39 1.1 riastrad #include "h_macros.h"
40 1.1 riastrad
41 1.6 riastrad struct sigaltstack ss[3];
42 1.1 riastrad jmp_buf jmp;
43 1.3 riastrad sigjmp_buf sigjmp;
44 1.1 riastrad unsigned nentries;
45 1.3 riastrad const char *bailname;
46 1.3 riastrad void (*bailfn)(void) __dead;
47 1.1 riastrad
48 1.14 riastrad /*
49 1.14 riastrad * Optional compat13 functions from when sigcontext was expanded.
50 1.14 riastrad * Fortunately the only change visible to the caller is that the size
51 1.14 riastrad * of jmp_buf increased, so we can always use the old symbols with new
52 1.14 riastrad * jmp_buf arrays.
53 1.14 riastrad */
54 1.14 riastrad int (*compat13_sigsetjmp)(sigjmp_buf, int);
55 1.25 christos void (*compat13_siglongjmp)(sigjmp_buf, int) __dead;
56 1.14 riastrad int (*compat13_setjmp)(jmp_buf);
57 1.25 christos void (*compat13_longjmp)(jmp_buf, int) __dead;
58 1.14 riastrad
59 1.14 riastrad /*
60 1.14 riastrad * compatsigsys(signo)
61 1.14 riastrad *
62 1.14 riastrad * Signal handler for SIGSYS in case compat_13_sigreturn13 is not
63 1.14 riastrad * implemented by the kernel -- we will just skip the test in that
64 1.14 riastrad * case.
65 1.14 riastrad */
66 1.14 riastrad static void
67 1.14 riastrad compatsigsys(int signo)
68 1.14 riastrad {
69 1.14 riastrad
70 1.14 riastrad atf_tc_skip("no compat syscalls to test");
71 1.14 riastrad }
72 1.14 riastrad
73 1.14 riastrad static void
74 1.14 riastrad compatsetup(void)
75 1.14 riastrad {
76 1.14 riastrad
77 1.14 riastrad /*
78 1.14 riastrad * Grab the libc library symbols if available.
79 1.14 riastrad */
80 1.14 riastrad if ((compat13_sigsetjmp = dlsym(RTLD_SELF, "sigsetjmp")) == NULL ||
81 1.14 riastrad (compat13_siglongjmp = dlsym(RTLD_SELF, "siglongjmp")) == NULL ||
82 1.14 riastrad (compat13_setjmp = dlsym(RTLD_SELF, "setjmp")) == NULL ||
83 1.14 riastrad (compat13_longjmp = dlsym(RTLD_SELF, "longjmp")) == NULL)
84 1.14 riastrad atf_tc_skip("no compat functions to test");
85 1.14 riastrad
86 1.14 riastrad /*
87 1.14 riastrad * Arrange for SIGSYS to skip the test -- this happens if the
88 1.14 riastrad * libc stub has the function, but the kernel isn't built with
89 1.14 riastrad * support for the compat13 sigreturn syscall for longjmp.
90 1.14 riastrad */
91 1.14 riastrad REQUIRE_LIBC(signal(SIGSYS, &compatsigsys), SIG_ERR);
92 1.14 riastrad }
93 1.14 riastrad
94 1.1 riastrad static void
95 1.1 riastrad on_sigusr1(int signo, siginfo_t *si, void *ctx)
96 1.1 riastrad {
97 1.1 riastrad ucontext_t *uc = ctx;
98 1.1 riastrad void *sp = (void *)(uintptr_t)_UC_MACHINE_SP(uc);
99 1.1 riastrad void *fp = __builtin_frame_address(0);
100 1.6 riastrad struct sigaltstack *ssp;
101 1.6 riastrad
102 1.6 riastrad /*
103 1.6 riastrad * Ensure we haven't re-entered the signal handler too many
104 1.6 riastrad * times. We should enter only twice.
105 1.6 riastrad */
106 1.6 riastrad ATF_REQUIRE_MSG(nentries < 2,
107 1.6 riastrad "%u recursive signal handler entries is too many in this test",
108 1.6 riastrad nentries + 1);
109 1.1 riastrad
110 1.1 riastrad /*
111 1.1 riastrad * Ensure that the signal handler was called in the alternate
112 1.1 riastrad * signal stack.
113 1.1 riastrad */
114 1.6 riastrad ssp = &ss[nentries];
115 1.6 riastrad ATF_REQUIRE_MSG((fp >= ssp->ss_sp &&
116 1.6 riastrad fp < (void *)((char *)ssp->ss_sp + ssp->ss_size)),
117 1.4 riastrad "sigaltstack failed to take effect on entry %u --"
118 1.1 riastrad " signal handler's frame pointer %p doesn't lie in sigaltstack"
119 1.1 riastrad " [%p, %p), size 0x%zx",
120 1.4 riastrad nentries,
121 1.6 riastrad fp, ssp->ss_sp, (char *)ssp->ss_sp + ssp->ss_size, ssp->ss_size);
122 1.1 riastrad
123 1.1 riastrad /*
124 1.1 riastrad * Ensure that if we enter the signal handler, we are entering
125 1.6 riastrad * it from the original stack, not from any of the alternate
126 1.6 riastrad * signal stacks.
127 1.1 riastrad */
128 1.6 riastrad for (ssp = &ss[0]; ssp < &ss[__arraycount(ss)]; ssp++) {
129 1.6 riastrad ATF_REQUIRE_MSG((sp < ssp->ss_sp ||
130 1.7 riastrad sp >= (void *)((char *)ssp->ss_sp + ssp->ss_size)),
131 1.6 riastrad "%s failed to restore stack"
132 1.6 riastrad " before allowing signal on entry %u --"
133 1.6 riastrad " interrupted stack pointer %p lies in sigaltstack %zd"
134 1.6 riastrad " [%p, %p), size 0x%zx",
135 1.6 riastrad bailname,
136 1.6 riastrad nentries,
137 1.6 riastrad sp, ssp - ss,
138 1.6 riastrad ssp->ss_sp, (char *)ssp->ss_sp + ssp->ss_size,
139 1.6 riastrad ssp->ss_size);
140 1.6 riastrad }
141 1.1 riastrad
142 1.1 riastrad /*
143 1.1 riastrad * First time through, we want to test whether longjmp restores
144 1.1 riastrad * the signal mask first, or restores the stack pointer first.
145 1.1 riastrad * The signal should be blocked at this point, so we re-raise
146 1.1 riastrad * the signal to queue it up for delivery as soon as it is
147 1.1 riastrad * unmasked -- which should wait until the stack pointer has
148 1.1 riastrad * been restored in longjmp.
149 1.1 riastrad */
150 1.1 riastrad if (nentries++ == 0)
151 1.1 riastrad RL(raise(SIGUSR1));
152 1.1 riastrad
153 1.1 riastrad /*
154 1.6 riastrad * Set up the next sigaltstack. We can't reuse the current one
155 1.6 riastrad * for the next signal handler re-entry until the system clears
156 1.6 riastrad * the SS_ONSTACK process state -- which normal return from
157 1.6 riastrad * signal handler does, but which longjmp does not. So to keep
158 1.6 riastrad * it simple (ha), we just use another sigaltstack.
159 1.6 riastrad */
160 1.6 riastrad RL(sigaltstack(&ss[nentries], NULL));
161 1.6 riastrad
162 1.6 riastrad /*
163 1.1 riastrad * Jump back to the original context.
164 1.1 riastrad */
165 1.3 riastrad (*bailfn)();
166 1.1 riastrad }
167 1.1 riastrad
168 1.3 riastrad static void
169 1.3 riastrad go(const char *name, void (*fn)(void) __dead)
170 1.1 riastrad {
171 1.1 riastrad struct sigaction sa;
172 1.6 riastrad unsigned i;
173 1.1 riastrad
174 1.3 riastrad bailname = name;
175 1.3 riastrad bailfn = fn;
176 1.3 riastrad
177 1.1 riastrad /*
178 1.1 riastrad * Allocate a stack for the signal handler to run in, and
179 1.6 riastrad * configure the system to use the first one.
180 1.1 riastrad *
181 1.1 riastrad * XXX Should maybe use a guard page but this is simpler.
182 1.1 riastrad */
183 1.6 riastrad for (i = 0; i < __arraycount(ss); i++) {
184 1.6 riastrad ss[i].ss_size = SIGSTKSZ;
185 1.6 riastrad REQUIRE_LIBC(ss[i].ss_sp = malloc(ss[i].ss_size), NULL);
186 1.6 riastrad }
187 1.6 riastrad RL(sigaltstack(&ss[0], NULL));
188 1.1 riastrad
189 1.1 riastrad /*
190 1.1 riastrad * Set up a test signal handler for SIGUSR1. Allow all
191 1.1 riastrad * signals, except SIGUSR1 (which is masked by default) -- that
192 1.1 riastrad * way we don't inadvertently obscure weird crashes in the
193 1.1 riastrad * signal handler.
194 1.1 riastrad *
195 1.1 riastrad * Set SA_SIGINFO so the system will pass siginfo -- and, more
196 1.1 riastrad * to the point, ucontext, so the signal handler can determine
197 1.1 riastrad * the stack pointer of the logic it interrupted.
198 1.1 riastrad *
199 1.1 riastrad * Set SA_ONSTACK so the system will use the alternate signal
200 1.1 riastrad * stack to call the signal handler -- that way, it can tell
201 1.1 riastrad * whether the stack was restored before the second time
202 1.1 riastrad * around.
203 1.1 riastrad */
204 1.1 riastrad memset(&sa, 0, sizeof(sa));
205 1.1 riastrad sa.sa_sigaction = &on_sigusr1;
206 1.2 riastrad RL(sigemptyset(&sa.sa_mask));
207 1.1 riastrad sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
208 1.1 riastrad RL(sigaction(SIGUSR1, &sa, NULL));
209 1.1 riastrad
210 1.1 riastrad /*
211 1.3 riastrad * Raise the signal to enter the signal handler the first time.
212 1.3 riastrad */
213 1.3 riastrad RL(raise(SIGUSR1));
214 1.3 riastrad
215 1.3 riastrad /*
216 1.3 riastrad * If we ever reach this point, something went seriously wrong.
217 1.3 riastrad */
218 1.3 riastrad atf_tc_fail("unreachable");
219 1.3 riastrad }
220 1.3 riastrad
221 1.3 riastrad static void __dead
222 1.3 riastrad bail_longjmp(void)
223 1.3 riastrad {
224 1.3 riastrad
225 1.3 riastrad longjmp(jmp, 1);
226 1.3 riastrad }
227 1.3 riastrad
228 1.3 riastrad ATF_TC(setjmp);
229 1.3 riastrad ATF_TC_HEAD(setjmp, tc)
230 1.3 riastrad {
231 1.3 riastrad atf_tc_set_md_var(tc, "descr",
232 1.3 riastrad "Test longjmp restores stack first, then signal mask");
233 1.3 riastrad }
234 1.3 riastrad ATF_TC_BODY(setjmp, tc)
235 1.3 riastrad {
236 1.3 riastrad
237 1.20 riastrad #if defined __ia64__
238 1.20 riastrad atf_tc_expect_fail("PR lib/57946:"
239 1.20 riastrad " longjmp fails to restore stack first before"
240 1.20 riastrad " restoring signal mask on most architectures");
241 1.20 riastrad #endif
242 1.20 riastrad
243 1.3 riastrad /*
244 1.1 riastrad * Set up a return point for the signal handler: when the
245 1.1 riastrad * signal handler does longjmp(jmp, 1), it comes flying out of
246 1.1 riastrad * here.
247 1.1 riastrad */
248 1.1 riastrad if (setjmp(jmp) == 1)
249 1.1 riastrad return;
250 1.1 riastrad
251 1.1 riastrad /*
252 1.3 riastrad * Run the test with longjmp.
253 1.3 riastrad */
254 1.3 riastrad go("longjmp", &bail_longjmp);
255 1.3 riastrad }
256 1.3 riastrad
257 1.3 riastrad static void __dead
258 1.14 riastrad bail_compat13_longjmp(void)
259 1.14 riastrad {
260 1.14 riastrad
261 1.14 riastrad (*compat13_longjmp)(jmp, 1);
262 1.14 riastrad }
263 1.14 riastrad
264 1.14 riastrad ATF_TC(compat13_setjmp);
265 1.14 riastrad ATF_TC_HEAD(compat13_setjmp, tc)
266 1.14 riastrad {
267 1.14 riastrad atf_tc_set_md_var(tc, "descr",
268 1.14 riastrad "Test compat13 longjmp restores stack first, then signal mask");
269 1.14 riastrad }
270 1.14 riastrad ATF_TC_BODY(compat13_setjmp, tc)
271 1.14 riastrad {
272 1.14 riastrad
273 1.14 riastrad compatsetup();
274 1.14 riastrad
275 1.20 riastrad #if defined __arm__ || defined __i386__ || defined __sh3__
276 1.22 riastrad #ifndef __arm__ /* will be exposed once PR 59351 is fixed */
277 1.20 riastrad atf_tc_expect_fail("PR lib/57946:"
278 1.20 riastrad " longjmp fails to restore stack first before"
279 1.20 riastrad " restoring signal mask on most architectures");
280 1.20 riastrad #endif
281 1.22 riastrad #endif
282 1.21 riastrad #ifdef __arm__
283 1.21 riastrad atf_tc_expect_signal(-1, "PR port-arm/59351: compat_setjmp is busted");
284 1.21 riastrad #endif
285 1.20 riastrad
286 1.14 riastrad /*
287 1.14 riastrad * Set up a return point for the signal handler: when the
288 1.14 riastrad * signal handler does (*compat13_longjmp)(jmp, 1), it comes
289 1.14 riastrad * flying out of here.
290 1.14 riastrad */
291 1.14 riastrad if ((*compat13_setjmp)(jmp) == 1)
292 1.14 riastrad return;
293 1.14 riastrad
294 1.14 riastrad /*
295 1.14 riastrad * Run the test with compat13_longjmp.
296 1.14 riastrad */
297 1.14 riastrad go("longjmp", &bail_compat13_longjmp);
298 1.14 riastrad }
299 1.14 riastrad
300 1.14 riastrad static void __dead
301 1.3 riastrad bail_siglongjmp(void)
302 1.3 riastrad {
303 1.3 riastrad
304 1.3 riastrad siglongjmp(sigjmp, 1);
305 1.3 riastrad }
306 1.3 riastrad
307 1.3 riastrad ATF_TC(sigsetjmp);
308 1.3 riastrad ATF_TC_HEAD(sigsetjmp, tc)
309 1.3 riastrad {
310 1.3 riastrad atf_tc_set_md_var(tc, "descr",
311 1.3 riastrad "Test siglongjmp restores stack first, then signal mask");
312 1.3 riastrad }
313 1.3 riastrad ATF_TC_BODY(sigsetjmp, tc)
314 1.3 riastrad {
315 1.3 riastrad
316 1.20 riastrad #if defined __ia64__
317 1.20 riastrad atf_tc_expect_fail("PR lib/57946:"
318 1.20 riastrad " longjmp fails to restore stack first before"
319 1.20 riastrad " restoring signal mask on most architectures");
320 1.20 riastrad #endif
321 1.20 riastrad
322 1.3 riastrad /*
323 1.3 riastrad * Set up a return point for the signal handler: when the
324 1.3 riastrad * signal handler does siglongjmp(sigjmp, 1), it comes flying
325 1.3 riastrad * out of here.
326 1.1 riastrad */
327 1.3 riastrad if (sigsetjmp(sigjmp, /*savesigmask*/1) == 1)
328 1.3 riastrad return;
329 1.1 riastrad
330 1.1 riastrad /*
331 1.3 riastrad * Run the test with siglongjmp.
332 1.1 riastrad */
333 1.3 riastrad go("siglongjmp", &bail_siglongjmp);
334 1.1 riastrad }
335 1.1 riastrad
336 1.14 riastrad static void __dead
337 1.14 riastrad bail_compat13_siglongjmp(void)
338 1.14 riastrad {
339 1.14 riastrad
340 1.14 riastrad (*compat13_siglongjmp)(sigjmp, 1);
341 1.14 riastrad }
342 1.14 riastrad
343 1.14 riastrad ATF_TC(compat13_sigsetjmp);
344 1.14 riastrad ATF_TC_HEAD(compat13_sigsetjmp, tc)
345 1.14 riastrad {
346 1.14 riastrad atf_tc_set_md_var(tc, "descr",
347 1.14 riastrad "Test compat13 siglongjmp restores stack first,"
348 1.14 riastrad " then signal mask");
349 1.14 riastrad }
350 1.14 riastrad ATF_TC_BODY(compat13_sigsetjmp, tc)
351 1.14 riastrad {
352 1.14 riastrad
353 1.20 riastrad compatsetup();
354 1.14 riastrad
355 1.20 riastrad #if defined __arm__ || defined __i386__ || defined __sh3__
356 1.22 riastrad #ifndef __arm__ /* will be exposed once PR 59351 is fixed */
357 1.20 riastrad atf_tc_expect_fail("PR lib/57946:"
358 1.20 riastrad " longjmp fails to restore stack first before"
359 1.20 riastrad " restoring signal mask on most architectures");
360 1.20 riastrad #endif
361 1.22 riastrad #endif
362 1.21 riastrad #ifdef __arm__
363 1.21 riastrad atf_tc_expect_signal(-1, "PR port-arm/59351: compat_setjmp is busted");
364 1.21 riastrad #endif
365 1.14 riastrad
366 1.14 riastrad /*
367 1.14 riastrad * Set up a return point for the signal handler: when the
368 1.14 riastrad * signal handler does (*compat13_siglongjmp)(sigjmp, 1), it
369 1.14 riastrad * comes flying out of here.
370 1.14 riastrad */
371 1.14 riastrad if ((*compat13_sigsetjmp)(sigjmp, /*savesigmask*/1) == 1)
372 1.14 riastrad return;
373 1.14 riastrad
374 1.14 riastrad /*
375 1.14 riastrad * Run the test with compat13_siglongjmp.
376 1.14 riastrad */
377 1.14 riastrad go("siglongjmp", &bail_compat13_siglongjmp);
378 1.14 riastrad }
379 1.14 riastrad
380 1.1 riastrad ATF_TP_ADD_TCS(tp)
381 1.1 riastrad {
382 1.1 riastrad
383 1.14 riastrad ATF_TP_ADD_TC(tp, compat13_setjmp);
384 1.14 riastrad ATF_TP_ADD_TC(tp, compat13_sigsetjmp);
385 1.1 riastrad ATF_TP_ADD_TC(tp, setjmp);
386 1.3 riastrad ATF_TP_ADD_TC(tp, sigsetjmp);
387 1.1 riastrad
388 1.1 riastrad return atf_no_error();
389 1.1 riastrad }
390