Module Name:    src
Committed By:   martin
Date:           Tue Nov 28 12:56:28 UTC 2023

Modified Files:
        src/sys/kern [netbsd-10]: exec_subr.c
        src/tests/lib/libc/sys [netbsd-10]: t_setrlimit.c

Log Message:
Pull up following revision(s) (requested by riastradh in ticket #477):

        tests/lib/libc/sys/t_setrlimit.c: revision 1.8
        tests/lib/libc/sys/t_setrlimit.c: revision 1.9
        sys/kern/exec_subr.c: revision 1.86

t_setrlimit: Verify changing RLIMIT_STACK affects access to stack.
PR kern/57711

exec: Map noaccess part of stack with prot=NONE, maxprot=READ|WRITE.
This way, setrlimit(RLIMT_STACK) can grant READ|WRITE access when
increasing the stack size.
PR kern/57711


To generate a diff of this commit:
cvs rdiff -u -r1.84 -r1.84.20.1 src/sys/kern/exec_subr.c
cvs rdiff -u -r1.7 -r1.7.10.1 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/sys/kern/exec_subr.c
diff -u src/sys/kern/exec_subr.c:1.84 src/sys/kern/exec_subr.c:1.84.20.1
--- src/sys/kern/exec_subr.c:1.84	Mon Apr 13 19:23:18 2020
+++ src/sys/kern/exec_subr.c	Tue Nov 28 12:56:28 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: exec_subr.c,v 1.84 2020/04/13 19:23:18 ad Exp $	*/
+/*	$NetBSD: exec_subr.c,v 1.84.20.1 2023/11/28 12:56:28 martin Exp $	*/
 
 /*
  * Copyright (c) 1993, 1994, 1996 Christopher G. Demetriou
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: exec_subr.c,v 1.84 2020/04/13 19:23:18 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: exec_subr.c,v 1.84.20.1 2023/11/28 12:56:28 martin Exp $");
 
 #include "opt_pax.h"
 
@@ -162,9 +162,10 @@ static int
 vmcmd_get_prot(struct lwp *l, const struct exec_vmcmd *cmd, vm_prot_t *prot,
     vm_prot_t *maxprot)
 {
+	vm_prot_t extraprot = PROT_MPROTECT_EXTRACT(cmd->ev_prot);
 
-	*prot = cmd->ev_prot;
-	*maxprot = PAX_MPROTECT_MAXPROTECT(l, *prot, 0, UVM_PROT_ALL);
+	*prot = cmd->ev_prot & UVM_PROT_ALL;
+	*maxprot = PAX_MPROTECT_MAXPROTECT(l, *prot, extraprot, UVM_PROT_ALL);
 
 	if ((*prot & *maxprot) != *prot)
 		return EACCES;
@@ -458,7 +459,9 @@ exec_setup_stack(struct lwp *l, struct e
 	}
 	if (noaccess_size > 0 && noaccess_size <= MAXSSIZ) {
 		NEW_VMCMD2(&epp->ep_vmcmds, vmcmd_map_zero, noaccess_size,
-		    noaccess_linear_min, NULL, 0, VM_PROT_NONE, VMCMD_STACK);
+		    noaccess_linear_min, NULL, 0,
+		    VM_PROT_NONE | PROT_MPROTECT(VM_PROT_READ | VM_PROT_WRITE),
+		    VMCMD_STACK);
 	}
 	KASSERT(access_size > 0 && access_size <= MAXSSIZ);
 	NEW_VMCMD2(&epp->ep_vmcmds, vmcmd_map_zero, access_size,

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.7.10.1
--- 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	Tue Nov 28 12:56:27 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.7.10.1 2023/11/28 12:56:27 martin 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.7.10.1 2023/11/28 12:56:27 martin 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,131 @@ 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));
+
+	/*
+	 * 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 +665,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();
 }

Reply via email to