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