summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authormaxv <maxv@NetBSD.org>2016-08-06 15:13:13 +0000
committermaxv <maxv@NetBSD.org>2016-08-06 15:13:13 +0000
commite9652cc8f54db6dcd4c193f4e40483cb6584d55a (patch)
treefb8e61eadaef9418db6607ac35bf74a3fef7bb7a /sys
parent0a5021b0b53c08657631480fee108c5c833635f9 (diff)
The way the kernel tries to prevent a userland process from allocating page
zero is hugely flawed. It is easy to demonstrate that one can trick UVM into chosing a NULL hint after the user_va0_disable check from uvm_map. Such a bypass allows kernel NULL pointer dereferences to be exploitable on architectures with a shared userland<->kernel VA, like amd64. Fix this by increasing the limit of the vm space made available for userland processes. This way, UVM will never chose a NULL hint, since it would be outside of the vm space. The user_va0_disable sysctl still controls this feature.
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/linux32/common/linux32_exec_elf32.c6
-rw-r--r--sys/compat/netbsd32/netbsd32_exec_aout.c14
-rw-r--r--sys/compat/netbsd32/netbsd32_exec_elf32.c6
-rw-r--r--sys/kern/kern_exec.c24
-rw-r--r--sys/sys/exec.h3
-rw-r--r--sys/uvm/uvm_map.c6
6 files changed, 37 insertions, 22 deletions
diff --git a/sys/compat/linux32/common/linux32_exec_elf32.c b/sys/compat/linux32/common/linux32_exec_elf32.c
index 24399aa6ae6..19ed2980f41 100644
--- a/sys/compat/linux32/common/linux32_exec_elf32.c
+++ b/sys/compat/linux32/common/linux32_exec_elf32.c
@@ -1,4 +1,4 @@
-/* $NetBSD: linux32_exec_elf32.c,v 1.18 2015/03/20 20:36:27 maxv Exp $ */
+/* $NetBSD: linux32_exec_elf32.c,v 1.19 2016/08/06 15:13:13 maxv Exp $ */
/*-
* Copyright (c) 1995, 1998, 2000, 2001,2006 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux32_exec_elf32.c,v 1.18 2015/03/20 20:36:27 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux32_exec_elf32.c,v 1.19 2016/08/06 15:13:13 maxv Exp $");
#define ELFSIZE 32
@@ -93,7 +93,7 @@ ELFNAME2(linux32,probe)(struct lwp *l, struct exec_package *epp,
#endif
epp->ep_flags |= EXEC_32 | EXEC_FORCEAUX;
- epp->ep_vm_minaddr = VM_MIN_ADDRESS;
+ epp->ep_vm_minaddr = exec_vm_minaddr(VM_MIN_ADDRESS);
epp->ep_vm_maxaddr = USRSTACK32;
return 0;
diff --git a/sys/compat/netbsd32/netbsd32_exec_aout.c b/sys/compat/netbsd32/netbsd32_exec_aout.c
index 0553f96aa96..e748c8e0db2 100644
--- a/sys/compat/netbsd32/netbsd32_exec_aout.c
+++ b/sys/compat/netbsd32/netbsd32_exec_aout.c
@@ -1,4 +1,4 @@
-/* $NetBSD: netbsd32_exec_aout.c,v 1.29 2014/12/05 22:21:47 christos Exp $ */
+/* $NetBSD: netbsd32_exec_aout.c,v 1.30 2016/08/06 15:13:13 maxv Exp $ */
/* from: NetBSD: exec_aout.c,v 1.15 1996/09/26 23:34:46 cgd Exp */
/*
@@ -57,7 +57,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: netbsd32_exec_aout.c,v 1.29 2014/12/05 22:21:47 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: netbsd32_exec_aout.c,v 1.30 2016/08/06 15:13:13 maxv Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -164,7 +164,7 @@ netbsd32_exec_aout_prep_zmagic(struct lwp *l, struct exec_package *epp)
epp->ep_daddr = epp->ep_taddr + execp->a_text;
epp->ep_dsize = execp->a_data + execp->a_bss;
epp->ep_entry = execp->a_entry;
- epp->ep_vm_minaddr = VM_MIN_ADDRESS;
+ epp->ep_vm_minaddr = exec_vm_minaddr(VM_MIN_ADDRESS);
epp->ep_vm_maxaddr = VM_MAXUSER_ADDRESS32;
error = vn_marktext(epp->ep_vp);
@@ -205,7 +205,7 @@ netbsd32_exec_aout_prep_nmagic(struct lwp *l, struct exec_package *epp)
epp->ep_daddr = roundup(epp->ep_taddr + execp->a_text, AOUT_LDPGSZ);
epp->ep_dsize = execp->a_data + execp->a_bss;
epp->ep_entry = execp->a_entry;
- epp->ep_vm_minaddr = VM_MIN_ADDRESS;
+ epp->ep_vm_minaddr = exec_vm_minaddr(VM_MIN_ADDRESS);
epp->ep_vm_maxaddr = VM_MAXUSER_ADDRESS32;
/* set up command for text segment */
@@ -244,7 +244,7 @@ netbsd32_exec_aout_prep_omagic(struct lwp *l, struct exec_package *epp)
epp->ep_daddr = epp->ep_taddr + execp->a_text;
epp->ep_dsize = execp->a_data + execp->a_bss;
epp->ep_entry = execp->a_entry;
- epp->ep_vm_minaddr = VM_MIN_ADDRESS;
+ epp->ep_vm_minaddr = exec_vm_minaddr(VM_MIN_ADDRESS);
epp->ep_vm_maxaddr = VM_MAXUSER_ADDRESS32;
/* set up command for text and data segments */
@@ -294,7 +294,7 @@ netbsd32_exec_aout_prep_oldzmagic(struct lwp *l, struct exec_package *epp)
epp->ep_daddr = epp->ep_taddr + execp->a_text;
epp->ep_dsize = execp->a_data + execp->a_bss;
epp->ep_entry = execp->a_entry;
- epp->ep_vm_minaddr = VM_MIN_ADDRESS;
+ epp->ep_vm_minaddr = exec_vm_minaddr(VM_MIN_ADDRESS);
epp->ep_vm_maxaddr = VM_MAXUSER_ADDRESS32;
error = vn_marktext(epp->ep_vp);
@@ -342,7 +342,7 @@ netbsd32_exec_aout_prep_oldnmagic(struct lwp *l, struct exec_package *epp)
epp->ep_daddr = roundup(epp->ep_taddr + execp->a_text, AOUT_LDPGSZ);
epp->ep_dsize = execp->a_data + execp->a_bss;
epp->ep_entry = execp->a_entry;
- epp->ep_vm_minaddr = VM_MIN_ADDRESS;
+ epp->ep_vm_minaddr = exec_vm_minaddr(VM_MIN_ADDRESS);
epp->ep_vm_maxaddr = VM_MAXUSER_ADDRESS32;
/* set up command for text segment */
diff --git a/sys/compat/netbsd32/netbsd32_exec_elf32.c b/sys/compat/netbsd32/netbsd32_exec_elf32.c
index a25d26b5fd8..0d58d9e3ec6 100644
--- a/sys/compat/netbsd32/netbsd32_exec_elf32.c
+++ b/sys/compat/netbsd32/netbsd32_exec_elf32.c
@@ -1,4 +1,4 @@
-/* $NetBSD: netbsd32_exec_elf32.c,v 1.39 2015/03/20 20:36:27 maxv Exp $ */
+/* $NetBSD: netbsd32_exec_elf32.c,v 1.40 2016/08/06 15:13:13 maxv Exp $ */
/* from: NetBSD: exec_aout.c,v 1.15 1996/09/26 23:34:46 cgd Exp */
/*
@@ -57,7 +57,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: netbsd32_exec_elf32.c,v 1.39 2015/03/20 20:36:27 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: netbsd32_exec_elf32.c,v 1.40 2016/08/06 15:13:13 maxv Exp $");
#define ELFSIZE 32
@@ -115,7 +115,7 @@ ELFNAME2(netbsd32,probe_noteless)(struct lwp *l, struct exec_package *epp,
#ifdef _LP64
epp->ep_flags |= EXEC_32 | EXEC_FORCEAUX;
#endif
- epp->ep_vm_minaddr = VM_MIN_ADDRESS;
+ epp->ep_vm_minaddr = exec_vm_minaddr(VM_MIN_ADDRESS);
epp->ep_vm_maxaddr = USRSTACK32;
#ifdef ELF_INTERP_NON_RELOCATABLE
*pos = ELF_LINK_ADDR;
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 6f1d5e622d3..ecc28f0e9f9 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_exec.c,v 1.435 2016/07/07 06:55:43 msaitoh Exp $ */
+/* $NetBSD: kern_exec.c,v 1.436 2016/08/06 15:13:13 maxv Exp $ */
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -59,7 +59,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.435 2016/07/07 06:55:43 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.436 2016/08/06 15:13:13 maxv Exp $");
#include "opt_exec.h"
#include "opt_execfmt.h"
@@ -122,6 +122,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.435 2016/07/07 06:55:43 msaitoh Exp
struct execve_data;
+extern int user_va0_disable;
+
static size_t calcargs(struct execve_data * restrict, const size_t);
static size_t calcstack(struct execve_data * restrict, const size_t);
static int copyoutargs(struct execve_data * restrict, struct lwp *,
@@ -401,11 +403,10 @@ check_exec(struct lwp *l, struct exec_package *epp, struct pathbuf *pb)
/*
* Set up default address space limits. Can be overridden
* by individual exec packages.
- *
- * XXX probably should be all done in the exec packages.
*/
- epp->ep_vm_minaddr = VM_MIN_ADDRESS;
+ epp->ep_vm_minaddr = exec_vm_minaddr(VM_MIN_ADDRESS);
epp->ep_vm_maxaddr = VM_MAXUSER_ADDRESS;
+
/*
* set up the vmcmds for creation of the process
* address space
@@ -652,6 +653,19 @@ out:
return 0;
}
+vaddr_t
+exec_vm_minaddr(vaddr_t va_min)
+{
+ /*
+ * Increase va_min if we don't want NULL to be mappable by the
+ * process.
+ */
+#define VM_MIN_GUARD (2 * PAGE_SIZE)
+ if (user_va0_disable && (va_min < VM_MIN_GUARD))
+ return VM_MIN_GUARD;
+ return va_min;
+}
+
static int
execve_loadvm(struct lwp *l, const char *path, char * const *args,
char * const *envs, execve_fetch_element_t fetch_element,
diff --git a/sys/sys/exec.h b/sys/sys/exec.h
index 5c5b2a303a2..b85226c636a 100644
--- a/sys/sys/exec.h
+++ b/sys/sys/exec.h
@@ -1,4 +1,4 @@
-/* $NetBSD: exec.h,v 1.150 2016/01/23 14:03:48 christos Exp $ */
+/* $NetBSD: exec.h,v 1.151 2016/08/06 15:13:14 maxv Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -250,6 +250,7 @@ struct exec_vmcmd {
* funtions used either by execve() or the various CPU-dependent execve()
* hooks.
*/
+vaddr_t exec_vm_minaddr (vaddr_t);
void kill_vmcmd (struct exec_vmcmd **);
int exec_makecmds (struct lwp *, struct exec_package *);
int exec_runcmds (struct lwp *, struct exec_package *);
diff --git a/sys/uvm/uvm_map.c b/sys/uvm/uvm_map.c
index 66a4f0866ab..0068fbfecd8 100644
--- a/sys/uvm/uvm_map.c
+++ b/sys/uvm/uvm_map.c
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_map.c,v 1.340 2016/07/07 06:55:44 msaitoh Exp $ */
+/* $NetBSD: uvm_map.c,v 1.341 2016/08/06 15:13:14 maxv Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.340 2016/07/07 06:55:44 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.341 2016/08/06 15:13:14 maxv Exp $");
#include "opt_ddb.h"
#include "opt_uvmhist.h"
@@ -171,7 +171,7 @@ vaddr_t uvm_maxkaddr;
#undef __USER_VA0_DISABLE_DEFAULT
#define __USER_VA0_DISABLE_DEFAULT USER_VA0_DISABLE_DEFAULT
#endif
-static int user_va0_disable = __USER_VA0_DISABLE_DEFAULT;
+int user_va0_disable = __USER_VA0_DISABLE_DEFAULT;
#endif
/*