1 /* $NetBSD: t_stack.c,v 1.6 2023/11/28 02:54:33 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2023 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 #define _KMEMUSER /* __MACHINE_STACK_GROWS_UP */ 30 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: t_stack.c,v 1.6 2023/11/28 02:54:33 riastradh Exp $"); 33 34 #include <sys/mman.h> 35 #include <sys/param.h> 36 #include <sys/sysctl.h> 37 #include <sys/types.h> 38 39 #include <uvm/uvm_param.h> /* VM_THREAD_GUARD_SIZE */ 40 41 #include <atf-c.h> 42 #include <pthread.h> 43 #include <setjmp.h> 44 #include <signal.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 #include "h_macros.h" 49 50 struct jmp_ctx { 51 jmp_buf buf; 52 }; 53 54 /* 55 * State used by various tests. 56 */ 57 struct ctx { 58 size_t size; /* default stack size */ 59 size_t guardsize; /* default guard size */ 60 void *addr; /* user-allocated stack */ 61 pthread_key_t jmp_key; /* jmp_ctx to return from SIGSEGV handler */ 62 } ctx, *C = &ctx; 63 64 /* 65 * getdefaultstacksize() 66 * 67 * Return the default stack size for threads created with 68 * pthread_create. 69 */ 70 static size_t 71 getdefaultstacksize(void) 72 { 73 pthread_attr_t attr; 74 size_t stacksize; 75 76 /* 77 * When called from the main thread, this returns the default 78 * stack size (pthread__stacksize) used for pthreads. 79 */ 80 RZ(pthread_getattr_np(pthread_self(), &attr)); 81 RZ(pthread_attr_getstacksize(&attr, &stacksize)); 82 RZ(pthread_attr_destroy(&attr)); 83 84 /* 85 * Verify that the assumption above holds. 86 */ 87 extern size_t pthread__stacksize; /* pthread_int.h */ 88 ATF_CHECK_EQ_MSG(stacksize, pthread__stacksize, 89 "stacksize=%zu pthread__stacksize=%zu", 90 stacksize, pthread__stacksize); 91 92 return stacksize; 93 } 94 95 /* 96 * getnondefaultstacksize() 97 * 98 * Return a stack size that is not the default stack size for 99 * threads created with pthread_create. 100 */ 101 static size_t 102 getnondefaultstacksize(void) 103 { 104 105 return getdefaultstacksize() + sysconf(_SC_PAGESIZE); 106 } 107 108 /* 109 * getdefaultguardsize() 110 * 111 * Return the default guard size for threads created with 112 * pthread_create. 113 */ 114 static size_t 115 getdefaultguardsize(void) 116 { 117 const int mib[2] = { CTL_VM, VM_THREAD_GUARD_SIZE }; 118 unsigned guardsize; 119 size_t len = sizeof(guardsize); 120 121 RL(sysctl(mib, __arraycount(mib), &guardsize, &len, NULL, 0)); 122 ATF_REQUIRE_EQ_MSG(len, sizeof(guardsize), 123 "len=%zu sizeof(guardsize)=%zu", len, sizeof(guardsize)); 124 125 /* 126 * Verify this matches what libpthread determined. 127 */ 128 extern size_t pthread__guardsize; /* pthread_int.h */ 129 ATF_CHECK_EQ_MSG(guardsize, pthread__guardsize, 130 "guardsize=%u pthread__guardsize=%zu", 131 guardsize, pthread__guardsize); 132 133 return guardsize; 134 } 135 136 /* 137 * alloc(nbytes) 138 * 139 * Allocate an nbytes-long page-aligned read/write region and 140 * return a pointer to it. Abort the test if allocation fails, so 141 * if this function returns it succeeds. 142 */ 143 static void * 144 alloc(size_t nbytes) 145 { 146 void *ptr; 147 148 REQUIRE_LIBC((ptr = mmap(/*hint*/NULL, nbytes, 149 PROT_READ|PROT_WRITE, MAP_ANON, /*fd*/-1, /*offset*/0)), 150 MAP_FAILED); 151 152 return ptr; 153 } 154 155 /* 156 * init(stacksize) 157 * 158 * Initialize state used by various tests with the specified 159 * stacksize. 160 * 161 * Make sure to allocate enough space that even if there shouldn't 162 * be a stack guard (i.e., it should be empty), adjusting the 163 * requested bounds by the default stack guard size will leave us 164 * inside allocated memory. 165 */ 166 static void 167 init(size_t stacksize) 168 { 169 170 C->size = stacksize; 171 C->guardsize = getdefaultguardsize(); 172 C->addr = alloc(C->size + C->guardsize); 173 RZ(pthread_key_create(&C->jmp_key, NULL)); 174 } 175 176 /* 177 * stack_pointer() 178 * 179 * Return the stack pointer. This is used to verify whether the 180 * stack pointer lie within a certain address range. 181 */ 182 static __noinline void * 183 stack_pointer(void) 184 { 185 return __builtin_frame_address(0); 186 } 187 188 /* 189 * sigsegv_ok(signo) 190 * 191 * Signal handler for SIGSEGV to return to the jmp ctx, to verify 192 * that SIGSEGV happened without crashing. 193 */ 194 static void 195 sigsegv_ok(int signo) 196 { 197 struct jmp_ctx *j = pthread_getspecific(C->jmp_key); 198 199 longjmp(j->buf, 1); 200 } 201 202 /* 203 * checksigsegv(p) 204 * 205 * Verify that reading *p triggers SIGSEGV. Fails test nonfatally 206 * if SIGSEGV doesn't happen. 207 */ 208 static void 209 checksigsegv(const char *p) 210 { 211 struct jmp_ctx j; 212 struct sigaction act, oact; 213 volatile struct sigaction oactsave; 214 volatile char v; 215 216 memset(&act, 0, sizeof(act)); 217 act.sa_handler = &sigsegv_ok; 218 219 if (setjmp(j.buf) == 0) { 220 pthread_setspecific(C->jmp_key, &j); 221 RL(sigaction(SIGSEGV, &act, &oact)); 222 oactsave = oact; 223 v = *p; /* trigger SIGSEGV */ 224 atf_tc_fail_nonfatal("failed to trigger SIGSEGV at %p", p); 225 } else { 226 /* return from SIGSEGV handler */ 227 oact = oactsave; 228 } 229 RL(sigaction(SIGSEGV, &oact, NULL)); 230 pthread_setspecific(C->jmp_key, NULL); 231 232 (void)v; /* suppress unused variable warnings */ 233 } 234 235 /* 236 * checknosigsegv(p) 237 * 238 * Verify that reading *p does not trigger SIGSEGV. Fails test 239 * nonfatally if SIGSEGV happens. 240 */ 241 static void 242 checknosigsegv(const char *p) 243 { 244 struct jmp_ctx j; 245 struct sigaction act, oact; 246 volatile struct sigaction oactsave; 247 volatile char v; 248 249 memset(&act, 0, sizeof(act)); 250 act.sa_handler = &sigsegv_ok; 251 252 if (setjmp(j.buf) == 0) { 253 pthread_setspecific(C->jmp_key, &j); 254 RL(sigaction(SIGSEGV, &act, &oact)); 255 oactsave = oact; 256 v = *p; /* better not trigger SIGSEGV */ 257 } else { 258 /* return from SIGSEGV handler */ 259 atf_tc_fail_nonfatal("spuriously triggered SIGSEGV at %p", p); 260 oact = oactsave; 261 } 262 RL(sigaction(SIGSEGV, &oact, NULL)); 263 pthread_setspecific(C->jmp_key, NULL); 264 265 (void)v; /* suppress unused variable warnings */ 266 } 267 268 /* 269 * checkguardaccessthread(cookie) 270 * 271 * Thread start routine that verifies it has access to the start 272 * and end of its stack, according to pthread_attr_getstack, and 273 * _does not_ have access to the start or end of its stack guard, 274 * above the stack (in stack growth direction) by 275 * pthread_attr_getguardsize bytes. 276 */ 277 static void * 278 checkguardaccessthread(void *cookie) 279 { 280 pthread_t t = pthread_self(); 281 pthread_attr_t attr; 282 void *addr, *guard; 283 size_t size, guardsize; 284 285 /* 286 * Get the the stack and stack guard parameters. 287 */ 288 RZ(pthread_getattr_np(t, &attr)); 289 RZ(pthread_attr_getstack(&attr, &addr, &size)); 290 RZ(pthread_attr_getguardsize(&attr, &guardsize)); 291 292 /* 293 * Determine where the guard starts in virtual address space 294 * (not in stack growth direction). 295 */ 296 #ifdef __MACHINE_STACK_GROWS_UP 297 guard = (char *)addr + size; 298 #else 299 guard = (char *)addr - guardsize; 300 #endif 301 302 /* 303 * Verify access to the start and end of the stack itself. 304 */ 305 checknosigsegv(addr); 306 checknosigsegv((char *)addr + size - 1); 307 308 /* 309 * Verify no access to the start or end of the stack guard. 310 */ 311 checksigsegv(guard); 312 checksigsegv((char *)guard + guardsize - 1); 313 314 return NULL; 315 } 316 317 /* 318 * checkaddraccessthread(cookie) 319 * 320 * Thread start routine that verifies its stack is [C->addr, 321 * C->addr + C->size), according to pthread_attr_getstack and 322 * pthread_addr_getstacksize, and verifies it has access to that 323 * whole range. 324 */ 325 static void * 326 checkaddraccessthread(void *cookie) 327 { 328 pthread_t t = pthread_self(); 329 pthread_attr_t attr; 330 void *sp; 331 void *addr; 332 size_t size, size0; 333 334 /* 335 * Verify the stack pointer lies somewhere in the allocated 336 * range. 337 */ 338 sp = stack_pointer(); 339 ATF_CHECK_MSG(C->addr <= sp, "sp=%p not in [%p,%p + 0x%zu) = [%p,%p)", 340 sp, C->addr, C->addr, C->size, C->addr, (char *)C->addr + C->size); 341 ATF_CHECK_MSG(sp <= (void *)((char *)C->addr + C->size), 342 "sp=%p not in [%p,%p + 0x%zu) = [%p,%p)", 343 sp, C->addr, C->addr, C->size, C->addr, (char *)C->addr + C->size); 344 345 /* 346 * Verify, if not that, then the stack pointer at least lies 347 * within the extra buffer we allocated for slop to address a 348 * bug NetBSD libpthread used to have of spuriously adding the 349 * guard size to a user-allocated stack address. This is 350 * ATF_REQUIRE, not ATF_CHECK, because if this doesn't hold, we 351 * might be clobbering some other memory like malloc pages, 352 * causing the whole test to crash with useless diagnostics. 353 */ 354 ATF_REQUIRE_MSG(sp <= (void *)((char *)C->addr + C->size + 355 C->guardsize), 356 "sp=%p not even in buffer [%p,%p + 0x%zu + 0x%zu) = [%p,%p)", 357 sp, C->addr, C->addr, C->size, C->guardsize, 358 C->addr, (char *)C->addr + C->size + C->guardsize); 359 360 /* 361 * Get the stack parameters -- both via pthread_attr_getstack 362 * and via pthread_attr_getstacksize, to make sure they agree 363 * -- and verify that they are what we expect from the caller. 364 */ 365 RZ(pthread_getattr_np(t, &attr)); 366 RZ(pthread_attr_getstack(&attr, &addr, &size)); 367 RZ(pthread_attr_getstacksize(&attr, &size0)); 368 ATF_CHECK_EQ_MSG(C->addr, addr, "expected %p actual %p", 369 C->addr, addr); 370 ATF_CHECK_EQ_MSG(C->size, size, "expected %zu actual %zu", 371 C->size, size); 372 ATF_CHECK_EQ_MSG(C->size, size0, "expected %zu actual %zu", 373 C->size, size0); 374 375 /* 376 * Verify that we have access to what we expect the stack to 377 * be. 378 */ 379 checknosigsegv(C->addr); 380 checknosigsegv((char *)C->addr + C->size - 1); 381 382 return NULL; 383 } 384 385 ATF_TC(stack1); 386 ATF_TC_HEAD(stack1, tc) 387 { 388 atf_tc_set_md_var(tc, "descr", 389 "Test allocating and reallocating a thread with a user stack"); 390 } 391 ATF_TC_BODY(stack1, tc) 392 { 393 pthread_attr_t attr; 394 pthread_t t, t2; 395 396 /* 397 * Allocate a stack with a non-default size to verify 398 * libpthread didn't choose the stack size for us. 399 */ 400 init(getnondefaultstacksize()); 401 402 /* 403 * Create a thread with user-allocated stack of a non-default 404 * size to verify the stack size and access. 405 */ 406 RZ(pthread_attr_init(&attr)); 407 RZ(pthread_attr_setstack(&attr, C->addr, C->size)); 408 RZ(pthread_create(&t, &attr, &checkaddraccessthread, C)); 409 RZ(pthread_join(t, NULL)); 410 411 /* 412 * Create another thread with the same parameters, and verify 413 * that (a) it was recycled, and (b) it works the same way. 414 */ 415 RZ(pthread_create(&t2, &attr, &checkaddraccessthread, C)); 416 ATF_CHECK_EQ_MSG(t, t2, "t=%p t2=%p", t, t2); /* NetBSD recycles */ 417 RZ(pthread_join(t2, NULL)); 418 } 419 420 ATF_TC(stack2); 421 ATF_TC_HEAD(stack2, tc) 422 { 423 atf_tc_set_md_var(tc, "descr", 424 "Test reallocating a thread with a newly self-allocated stack"); 425 } 426 ATF_TC_BODY(stack2, tc) 427 { 428 pthread_attr_t attr, attr2; 429 size_t size, size2; 430 pthread_t t, t2; 431 432 /* 433 * Allocate a stack with the default size so that we verify 434 * when libpthread reuses the thread, it doesn't inadvertently 435 * reuse the libpthread-allocated stack too and instead 436 * correctly uses our user-allocated stack. 437 */ 438 init(getdefaultstacksize()); 439 440 /* 441 * Create a thread with a libpthread-allocated stack that 442 * verifies 443 * (a) access to its own stack, and 444 * (b) no access to its own guard pages; 445 * then get its attributes and wait for it to complete. 446 */ 447 RZ(pthread_create(&t, NULL, &checkguardaccessthread, C)); 448 RZ(pthread_getattr_np(t, &attr)); 449 RZ(pthread_join(t, NULL)); 450 451 /* 452 * Create a thread with a user-allocated stack that verifies 453 * (a) stack addr/size match request, and 454 * (b) access to the requested stack, 455 * and confirm that the first thread was recycled -- not part 456 * of POSIX semantics, but part of NetBSD's implementation; 457 * this way, we verify that, even though the thread is 458 * recycled, the thread's stack is set to the user-allocated 459 * stack and access to it works as expected. Then wait for it 460 * to complete. 461 */ 462 RZ(pthread_attr_init(&attr2)); 463 RZ(pthread_attr_setstack(&attr2, C->addr, C->size)); 464 RZ(pthread_create(&t2, &attr2, &checkaddraccessthread, C)); 465 ATF_CHECK_EQ_MSG(t, t2, "t=%p t2=%p", t, t2); /* NetBSD recycles */ 466 RZ(pthread_join(t2, NULL)); 467 468 /* 469 * Verify that the libpthread-allocated stack and 470 * user-allocated stack had the same size, since we chose the 471 * default size. 472 * 473 * Note: We can't say anything about the guard size, because 474 * with pthread_attr_setstack, the guard size is ignored, and 475 * it's not clear from POSIX whether any meaningful guard size 476 * is stored for retrieval with pthread_attr_getguardsize in 477 * attributes with pthread_attr_setstack. 478 */ 479 RZ(pthread_attr_getstacksize(&attr, &size)); 480 RZ(pthread_attr_getstacksize(&attr2, &size2)); 481 ATF_CHECK_EQ_MSG(size, size2, "size=%zu size2=%zu", size, size2); 482 } 483 484 ATF_TP_ADD_TCS(tp) 485 { 486 487 ATF_TP_ADD_TC(tp, stack1); 488 ATF_TP_ADD_TC(tp, stack2); 489 490 return atf_no_error(); 491 } 492