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 50struct jmp_ctx { 51 jmp_buf buf; 52}; 53 54/* 55 * State used by various tests. 56 */ 57struct 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 */ 70static size_t 71getdefaultstacksize(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 */ 101static size_t 102getnondefaultstacksize(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 */ 114static size_t 115getdefaultguardsize(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 */ 143static void * 144alloc(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 */ 166static void 167init(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 */ 182static __noinline void * 183stack_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 */ 194static void 195sigsegv_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 */ 208static void 209checksigsegv(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 */ 241static void 242checknosigsegv(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 */ 277static void * 278checkguardaccessthread(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 */ 325static void * 326checkaddraccessthread(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 385ATF_TC(stack1); 386ATF_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} 391ATF_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 420ATF_TC(stack2); 421ATF_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} 426ATF_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 484ATF_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