Module Name: src Committed By: riastradh Date: Mon Nov 20 13:05:17 UTC 2023
Modified Files: src/tests/lib/libc/sys: t_setrlimit.c Log Message: t_setrlimit: Verify changing RLIMIT_STACK affects access to stack. PR kern/57711 XXX pullup-10 XXX pullup-9 XXX pullup-8 To generate a diff of this commit: cvs rdiff -u -r1.7 -r1.8 src/tests/lib/libc/sys/t_setrlimit.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/tests/lib/libc/sys/t_setrlimit.c diff -u src/tests/lib/libc/sys/t_setrlimit.c:1.7 src/tests/lib/libc/sys/t_setrlimit.c:1.8 --- src/tests/lib/libc/sys/t_setrlimit.c:1.7 Tue Oct 13 06:58:57 2020 +++ src/tests/lib/libc/sys/t_setrlimit.c Mon Nov 20 13:05:17 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: t_setrlimit.c,v 1.7 2020/10/13 06:58:57 rin Exp $ */ +/* $NetBSD: t_setrlimit.c,v 1.8 2023/11/20 13:05:17 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include <sys/cdefs.h> -__RCSID("$NetBSD: t_setrlimit.c,v 1.7 2020/10/13 06:58:57 rin Exp $"); +__RCSID("$NetBSD: t_setrlimit.c,v 1.8 2023/11/20 13:05:17 riastradh Exp $"); #include <sys/resource.h> #include <sys/mman.h> @@ -48,6 +48,8 @@ __RCSID("$NetBSD: t_setrlimit.c,v 1.7 20 #include <ucontext.h> #include <unistd.h> +#include "h_macros.h" + static void sighandler(int); static const char path[] = "setrlimit"; @@ -524,6 +526,134 @@ ATF_TC_BODY(setrlimit_stack, tc) } +ATF_TC(setrlimit_stack_growshrink); +ATF_TC_HEAD(setrlimit_stack_growshrink, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test that setrlimit(2), RLIMIT_STACK, grows & shrinks the stack"); +} + +/* + * checkstack(n, ok) + * + * Check whether we can allocate an array of size n on the stack. + * + * - If expectsegv, verify that access fails with SIGSEGV. + * - If not expectsegv, verify that access succeeds. + * + * Do this in a subprocess rather than with a SIGSEGV handler, + * because once we've allocated an array of size n on the stack, + * in the case where the stack is inaccessible, we have just + * trashed the stack pointer so badly we can't make function calls + * like to a SIGSEGV handler. + * + * (We could use an alternate signal stack, but I already wrote it + * this way, and this is a little simpler and more robust than + * juggling signals, setjmp/longjmp, and sigaltstack.) + */ +static void +checkstack(size_t n, int expectsegv) +{ + pid_t forked, waited; + size_t i; + int status; + + RL(forked = fork()); + if (forked == 0) { /* child */ + volatile char *const x = alloca(n); + for (i = 0; i < n; i++) + x[i] = 0x1a; + _exit(expectsegv); + } + + /* parent */ + RL(waited = waitpid(forked, &status, 0)); + ATF_REQUIRE_EQ_MSG(waited, forked, "waited=%jd forked=%jd", + (intmax_t)waited, (intmax_t)forked); + if (expectsegv) { + ATF_REQUIRE_MSG(!WIFEXITED(status), + "expected signal but exited normally with status %d", + WEXITSTATUS(status)); + ATF_REQUIRE_MSG(WIFSIGNALED(status), "status=0x%x", status); + ATF_REQUIRE_EQ_MSG(WTERMSIG(status), SIGSEGV, "termsig=%d", + WTERMSIG(status)); + } else { + ATF_REQUIRE_MSG(!WIFSIGNALED(status), + "expected normal exit but termintaed on signal %d", + WTERMSIG(status)); + ATF_REQUIRE_MSG(WIFEXITED(status), "status=0x%x", status); + ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), 0, "exitstatus=%d", + WEXITSTATUS(status)); + } +} + +ATF_TC_BODY(setrlimit_stack_growshrink, tc) +{ + struct rlimit res; + size_t n; + + /* + * Disable core dumps -- we're going to deliberately cause + * SIGSEGV to test stack accessibility (which breaks even + * calling a function so we can't just use a SIGSEGV handler), + * so let's not waste time dumping core. + */ + res = (struct rlimit){ .rlim_cur = 0, .rlim_max = 0 }; + RL(setrlimit(RLIMIT_CORE, &res)); + + /* + * Get the current stack size and hard limit. + */ + RL(getrlimit(RLIMIT_STACK, &res)); + n = res.rlim_cur; + + /* + * Verify that we can't get at pages past the end of the stack + * right now. + */ + checkstack(n, /*expectsegv*/1); + + /* + * Stop if the hard limit is too small to test. Not sure + * exactly how much more space we need to verify that setrlimit + * actually expands the stack without examining the current + * stack pointer relative to the process's stack base, so we'll + * just double the stack size -- definitely enough to test + * stack growth -- and hope the hard rlimit is big enough to + * let us double it. + */ + if (n > res.rlim_max/2) + atf_tc_skip("hard stack rlimit is too small"); + + /* + * Double the stack size. This way we can allocate an array of + * length equal to the current stack size and be guaranteed + * that (a) it can be allocated, and (b) access to it requires + * the stack to have grown. + */ + res.rlim_cur = 2*n; + RL(setrlimit(RLIMIT_STACK, &res)); + + atf_tc_expect_fail("PR kern/57711:" + " setrlimit(RLIMIT_STACK) fails to increase usable stack size"); + + /* + * Verify that we can now get at pages past the end of the new + * stack but not beyond that. + */ + checkstack(n, /*expectsegv*/0); + if (n < SIZE_MAX/2) + checkstack(2*n, /*expectsegv*/1); + + /* + * Restore the stack size and verify that we can no longer + * access an array of length equal to the whole stack size. + */ + res.rlim_cur = n; + RL(setrlimit(RLIMIT_STACK, &res)); + checkstack(n, /*expectsegv*/1); +} + ATF_TP_ADD_TCS(tp) { @@ -538,6 +668,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, setrlimit_perm); ATF_TP_ADD_TC(tp, setrlimit_nthr); ATF_TP_ADD_TC(tp, setrlimit_stack); + ATF_TP_ADD_TC(tp, setrlimit_stack_growshrink); return atf_no_error(); }