Pull up following revision(s) (requested by riastradh in ticket #1920):
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
This commit is contained in:
parent
981abbb066
commit
a2469ca7a0
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: exec_subr.c,v 1.78.2.3 2017/08/31 08:32:39 bouyer Exp $ */
|
||||
/* $NetBSD: exec_subr.c,v 1.78.2.4 2023/11/28 13:00:52 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.78.2.3 2017/08/31 08:32:39 bouyer Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: exec_subr.c,v 1.78.2.4 2023/11/28 13:00:52 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;
|
||||
|
@ -456,7 +457,9 @@ exec_setup_stack(struct lwp *l, struct exec_package *epp)
|
|||
}
|
||||
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,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: t_setrlimit.c,v 1.6 2017/01/13 21:16:38 christos Exp $ */
|
||||
/* $NetBSD: t_setrlimit.c,v 1.6.6.1 2023/11/28 13:00:52 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.6 2017/01/13 21:16:38 christos Exp $");
|
||||
__RCSID("$NetBSD: t_setrlimit.c,v 1.6.6.1 2023/11/28 13:00:52 martin Exp $");
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -48,6 +48,8 @@ __RCSID("$NetBSD: t_setrlimit.c,v 1.6 2017/01/13 21:16:38 christos Exp $");
|
|||
#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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue