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();
}