Module Name:    src
Committed By:   joerg
Date:           Sat May  6 21:34:52 UTC 2017

Modified Files:
        src/lib/libc/sys: mmap.2 mprotect.2 mremap.2
        src/share/man/man9: uvm_map.9
        src/sys/compat/linux/common: linux_misc.c
        src/sys/kern: exec_subr.c kern_pax.c
        src/sys/sys: mman.h pax.h
        src/sys/uvm: uvm_extern.h uvm_map.c uvm_mmap.c uvm_mremap.c uvm_unix.c
        src/tests/lib/libc/sys: t_mprotect.c

Log Message:
Extend the mmap(2) interface to allow requesting protections for later
use with mprotect(2), but without enabling them immediately.

Extend the mremap(2) interface to allow duplicating mappings, i.e.
create a second range of virtual addresses references the same physical
pages. Duplicated mappings can have different effective protections.

Adjust PAX mprotect logic to disallow effective protections of W&X, but
allow one mapping W and another X protections. This obsoletes using
temporary files for purposes like JIT.

Adjust PAX logic for mmap(2) and mprotect(2) to fail if W&X is requested
and not silently drop the X protection.

Improve test cases to ensure correct operation of the changed
interfaces.


To generate a diff of this commit:
cvs rdiff -u -r1.48 -r1.49 src/lib/libc/sys/mmap.2
cvs rdiff -u -r1.24 -r1.25 src/lib/libc/sys/mprotect.2
cvs rdiff -u -r1.4 -r1.5 src/lib/libc/sys/mremap.2
cvs rdiff -u -r1.6 -r1.7 src/share/man/man9/uvm_map.9
cvs rdiff -u -r1.237 -r1.238 src/sys/compat/linux/common/linux_misc.c
cvs rdiff -u -r1.76 -r1.77 src/sys/kern/exec_subr.c
cvs rdiff -u -r1.58 -r1.59 src/sys/kern/kern_pax.c
cvs rdiff -u -r1.51 -r1.52 src/sys/sys/mman.h
cvs rdiff -u -r1.25 -r1.26 src/sys/sys/pax.h
cvs rdiff -u -r1.203 -r1.204 src/sys/uvm/uvm_extern.h
cvs rdiff -u -r1.343 -r1.344 src/sys/uvm/uvm_map.c
cvs rdiff -u -r1.163 -r1.164 src/sys/uvm/uvm_mmap.c
cvs rdiff -u -r1.18 -r1.19 src/sys/uvm/uvm_mremap.c
cvs rdiff -u -r1.47 -r1.48 src/sys/uvm/uvm_unix.c
cvs rdiff -u -r1.6 -r1.7 src/tests/lib/libc/sys/t_mprotect.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libc/sys/mmap.2
diff -u src/lib/libc/sys/mmap.2:1.48 src/lib/libc/sys/mmap.2:1.49
--- src/lib/libc/sys/mmap.2:1.48	Fri Feb 27 16:18:00 2015
+++ src/lib/libc/sys/mmap.2	Sat May  6 21:34:51 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: mmap.2,v 1.48 2015/02/27 16:18:00 christos Exp $
+.\"	$NetBSD: mmap.2,v 1.49 2017/05/06 21:34:51 joerg Exp $
 .\"
 .\" Copyright (c) 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -29,7 +29,7 @@
 .\"
 .\"	@(#)mmap.2	8.4 (Berkeley) 5/11/95
 .\"
-.Dd February 27, 2015
+.Dd April 27, 2017
 .Dt MMAP 2
 .Os
 .Sh NAME
@@ -85,8 +85,14 @@ Pages may be read.
 .It Dv PROT_WRITE
 Pages may be written.
 .It Dv PROT_NONE
-Pages may not be accessed.
+Placeholder when requesting no access permission.
 .El
+As a NetBSD extension,
+.Dv PROT_MPROTECT
+can be used to request additional permissions for later use with
+.Fn mprotect 2 .
+This is necessary for switching pages between writeable and executable
+when PAX mprotect restrictions are in place.
 .Pp
 .Bf -symbolic
 Note that, due to hardware limitations, on some platforms
@@ -238,6 +244,7 @@ was specified as part of the
 parameter and
 .Fa fd
 was not open for reading.
+.Pp
 The flags
 .Dv MAP_SHARED
 and
@@ -249,6 +256,8 @@ and
 parameters and
 .Fa fd
 was not open for writing.
+.Pp
+PAX mprotect restrictions prohibit the requested protection.
 .It Bq Er EBADF
 .Fa fd
 is not a valid open file descriptor.
@@ -265,6 +274,7 @@ was specified and the
 .Fa addr
 parameter was not page aligned or was outside of the
 valid address range for a process.
+.Pp
 .Dv MAP_ANON was specified and
 .Fa fd
 was not \-1.
@@ -276,6 +286,7 @@ did not reference a regular or character
 was specified and the
 .Fa addr
 parameter wasn't available.
+.Pp
 .Dv MAP_ANON
 was specified and insufficient memory was available.
 .It Bq Er EOVERFLOW

Index: src/lib/libc/sys/mprotect.2
diff -u src/lib/libc/sys/mprotect.2:1.24 src/lib/libc/sys/mprotect.2:1.25
--- src/lib/libc/sys/mprotect.2:1.24	Sun Apr  3 06:54:30 2011
+++ src/lib/libc/sys/mprotect.2	Sat May  6 21:34:51 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: mprotect.2,v 1.24 2011/04/03 06:54:30 jruoho Exp $
+.\"	$NetBSD: mprotect.2,v 1.25 2017/05/06 21:34:51 joerg Exp $
 .\"
 .\" Copyright (c) 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -29,7 +29,7 @@
 .\"
 .\"	@(#)mprotect.2	8.1 (Berkeley) 6/9/93
 .\"
-.Dd April 3, 2011
+.Dd April 27, 2017
 .Dt MPROTECT 2
 .Os
 .Sh NAME
@@ -64,7 +64,7 @@ Pages may be read.
 .It Dv PROT_WRITE
 Pages may be written.
 .It Dv PROT_NONE
-No permissions.
+Placeholder when requesting no access permission.
 .El
 .Sh RETURN VALUES
 Upon successful completion,
@@ -75,11 +75,19 @@ is set to indicate the error.
 .Sh ERRORS
 .Bl -tag -width Er
 .It Bq Er EACCES
-A memory protection violation occurred, or the
+A memory protection violation occurred.
+.Pp
+The
 .Dv PROT_EXEC
 flag was attempted on pages which belong to a filesystem mounted with the
 .Dv NOEXEC
 flag.
+.Pp
+The new protection is less restrictive than the protection originally
+set with
+.Xr mmap 2 .
+.Pp
+PAX mprotect restrictions prohibit the requested protection.
 .It Bq Er EINVAL
 An invalid memory range, or invalid parameters were provided.
 .It Bq Er ENOMEM

Index: src/lib/libc/sys/mremap.2
diff -u src/lib/libc/sys/mremap.2:1.4 src/lib/libc/sys/mremap.2:1.5
--- src/lib/libc/sys/mremap.2:1.4	Thu Apr 28 12:00:55 2011
+++ src/lib/libc/sys/mremap.2	Sat May  6 21:34:51 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: mremap.2,v 1.4 2011/04/28 12:00:55 wiz Exp $
+.\"	$NetBSD: mremap.2,v 1.5 2017/05/06 21:34:51 joerg Exp $
 .\"
 .\" Copyright (c) 2007 Thomas Klausner and Joerg Sonnenberger
 .\" All rights reserved.
@@ -25,7 +25,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\" ------------------------------------------------------------
-.Dd February 14, 2008
+.Dd April 28, 2017
 .Dt MREMAP 2
 .Os
 .Sh NAME
@@ -82,6 +82,9 @@ Otherwise,
 and
 .Ar newp
 are used as hints for the position, factoring in the given alignment.
+.It Dv MAP_REMAPDUP
+Duplicate the mapping.
+Both address ranges reference the same pages, but can have different protection flags.
 .El
 .Sh RETURN VALUES
 .Fn mremap

Index: src/share/man/man9/uvm_map.9
diff -u src/share/man/man9/uvm_map.9:1.6 src/share/man/man9/uvm_map.9:1.7
--- src/share/man/man9/uvm_map.9:1.6	Fri Sep 12 21:06:25 2014
+++ src/share/man/man9/uvm_map.9	Sat May  6 21:34:51 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: uvm_map.9,v 1.6 2014/09/12 21:06:25 riastradh Exp $
+.\"	$NetBSD: uvm_map.9,v 1.7 2017/05/06 21:34:51 joerg Exp $
 .\"
 .\" Copyright (c) 1998 Matthew R. Green
 .\" All rights reserved.
@@ -49,6 +49,9 @@ virtual address space management interfa
 .Fn uvm_map_protect "struct vm_map *map" "vaddr_t start" "vaddr_t end" \
 "vm_prot_t new_prot" "bool set_max"
 .Ft int
+.Fn uvm_map_protect_user "struct lwp *l" "vaddr_t start" "vaddr_t end" \
+"vm_prot_t new_prot"
+.Ft int
 .Fn uvm_deallocate "struct vm_map *map" "vaddr_t start" "vsize_t size"
 .Ft struct vmspace *
 .Fn uvmspace_alloc "vaddr_t min" "vaddr_t max"
@@ -308,6 +311,12 @@ if
 is true.
 This function returns a standard UVM return value.
 .Pp
+.Fn uvm_map_protect_user
+verifies that the new permissions honor PAX restrictions if applicable
+and forwards to
+.Fn uvm_map_protect
+on passing.
+.Pp
 .Fn uvm_deallocate
 deallocates kernel memory in map
 .Fa map

Index: src/sys/compat/linux/common/linux_misc.c
diff -u src/sys/compat/linux/common/linux_misc.c:1.237 src/sys/compat/linux/common/linux_misc.c:1.238
--- src/sys/compat/linux/common/linux_misc.c:1.237	Sat Jan 28 15:01:01 2017
+++ src/sys/compat/linux/common/linux_misc.c	Sat May  6 21:34:51 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_misc.c,v 1.237 2017/01/28 15:01:01 christos Exp $	*/
+/*	$NetBSD: linux_misc.c,v 1.238 2017/05/06 21:34:51 joerg Exp $	*/
 
 /*-
  * Copyright (c) 1995, 1998, 1999, 2008 The NetBSD Foundation, Inc.
@@ -57,7 +57,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_misc.c,v 1.237 2017/01/28 15:01:01 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_misc.c,v 1.238 2017/05/06 21:34:51 joerg Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -603,7 +603,7 @@ linux_sys_mprotect(struct lwp *l, const 
 		}
 	}
 	vm_map_unlock(map);
-	return uvm_map_protect(map, start, end, prot, FALSE);
+	return uvm_map_protect_user(l, start, end, prot);
 }
 #endif /* USRSTACK */
 

Index: src/sys/kern/exec_subr.c
diff -u src/sys/kern/exec_subr.c:1.76 src/sys/kern/exec_subr.c:1.77
--- src/sys/kern/exec_subr.c:1.76	Sun May 22 14:26:09 2016
+++ src/sys/kern/exec_subr.c	Sat May  6 21:34:51 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: exec_subr.c,v 1.76 2016/05/22 14:26:09 christos Exp $	*/
+/*	$NetBSD: exec_subr.c,v 1.77 2017/05/06 21:34:51 joerg 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.76 2016/05/22 14:26:09 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: exec_subr.c,v 1.77 2017/05/06 21:34:51 joerg Exp $");
 
 #include "opt_pax.h"
 
@@ -180,8 +180,11 @@ vmcmd_map_pagedvn(struct lwp *l, struct 
 		return EINVAL;
 
 	prot = cmd->ev_prot;
-	maxprot = UVM_PROT_ALL;
-	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
+	maxprot = PAX_MPROTECT_MAXPROTECT(l, prot, 0, UVM_PROT_ALL);
+	if ((prot & maxprot) != prot)
+		return EACCES;
+	if ((error = PAX_MPROTECT_VALIDATE(l, prot)))
+		return error;
 
 	/*
 	 * check the file system's opinion about mmapping the file
@@ -260,8 +263,11 @@ vmcmd_readvn(struct lwp *l, struct exec_
 		return error;
 
 	prot = cmd->ev_prot;
-	maxprot = VM_PROT_ALL;
-	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
+	maxprot = PAX_MPROTECT_MAXPROTECT(l, prot, 0, UVM_PROT_ALL);
+	if ((prot & maxprot) != prot)
+		return EACCES;
+	if ((error = PAX_MPROTECT_VALIDATE(l, prot)))
+		return error;
 
 #ifdef PMAP_NEED_PROCWR
 	/*
@@ -318,8 +324,11 @@ vmcmd_map_zero(struct lwp *l, struct exe
 	cmd->ev_len += diff;
 
 	prot = cmd->ev_prot;
-	maxprot = UVM_PROT_ALL;
-	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
+	maxprot = PAX_MPROTECT_MAXPROTECT(l, prot, 0, UVM_PROT_ALL);
+	if ((prot & maxprot) != prot)
+		return EACCES;
+	if ((error = PAX_MPROTECT_VALIDATE(l, prot)))
+		return error;
 
 	error = uvm_map(&p->p_vmspace->vm_map, &cmd->ev_addr,
 			round_page(cmd->ev_len), NULL, UVM_UNKNOWN_OFFSET, 0,

Index: src/sys/kern/kern_pax.c
diff -u src/sys/kern/kern_pax.c:1.58 src/sys/kern/kern_pax.c:1.59
--- src/sys/kern/kern_pax.c:1.58	Sat Feb 18 01:29:09 2017
+++ src/sys/kern/kern_pax.c	Sat May  6 21:34:51 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_pax.c,v 1.58 2017/02/18 01:29:09 chs Exp $	*/
+/*	$NetBSD: kern_pax.c,v 1.59 2017/05/06 21:34:51 joerg Exp $	*/
 
 /*
  * Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -57,7 +57,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_pax.c,v 1.58 2017/02/18 01:29:09 chs Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_pax.c,v 1.59 2017/05/06 21:34:51 joerg Exp $");
 
 #include "opt_pax.h"
 
@@ -423,42 +423,48 @@ pax_mprotect_elf_flags_active(uint32_t f
 	return true;
 }
 
-void
-pax_mprotect_adjust(
+vm_prot_t
+pax_mprotect_maxprotect(
 #ifdef PAX_MPROTECT_DEBUG
     const char *file, size_t line,
 #endif
-    struct lwp *l, vm_prot_t *prot, vm_prot_t *maxprot)
+    struct lwp *l, vm_prot_t active, vm_prot_t extra, vm_prot_t maxprot)
 {
 	uint32_t flags;
 
 	flags = l->l_proc->p_pax;
 	if (!pax_flags_active(flags, P_PAX_MPROTECT))
-		return;
+		return maxprot;
+
+	return (active|extra) & maxprot;
+}
 
-	if ((*prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) != VM_PROT_EXECUTE) {
+int
+pax_mprotect_validate(
 #ifdef PAX_MPROTECT_DEBUG
-		struct proc *p = l->l_proc;
-		if ((*prot & VM_PROT_EXECUTE) && pax_mprotect_debug) {
-			printf("%s: %s,%zu: %d.%d (%s): -x\n",
-			    __func__, file, line,
-			    p->p_pid, l->l_lid, p->p_comm);
-		}
+    const char *file, size_t line,
 #endif
-		*prot &= ~VM_PROT_EXECUTE;
-		*maxprot &= ~VM_PROT_EXECUTE;
-	} else {
+    struct lwp *l, vm_prot_t prot)
+{
+	uint32_t flags;
+
+	flags = l->l_proc->p_pax;
+	if (!pax_flags_active(flags, P_PAX_MPROTECT))
+		return 0;
+
+	if ((prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) ==
+	    (VM_PROT_WRITE|VM_PROT_EXECUTE)) {
 #ifdef PAX_MPROTECT_DEBUG
 		struct proc *p = l->l_proc;
-		if ((*prot & VM_PROT_WRITE) && pax_mprotect_debug) {
-			printf("%s: %s,%zu: %d.%d (%s): -w\n",
+
+		if (pax_mprotect_debug)
+			printf("%s: %s,%zu: %d.%d (%s): WX rejected\n",
 			    __func__, file, line,
 			    p->p_pid, l->l_lid, p->p_comm);
-		}
 #endif
-		*prot &= ~VM_PROT_WRITE;
-		*maxprot &= ~VM_PROT_WRITE;
+		return EACCES;
 	}
+	return 0;
 }
 
 /*

Index: src/sys/sys/mman.h
diff -u src/sys/sys/mman.h:1.51 src/sys/sys/mman.h:1.52
--- src/sys/sys/mman.h:1.51	Sat Apr 29 01:17:47 2017
+++ src/sys/sys/mman.h	Sat May  6 21:34:52 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: mman.h,v 1.51 2017/04/29 01:17:47 christos Exp $	*/
+/*	$NetBSD: mman.h,v 1.52 2017/05/06 21:34:52 joerg Exp $	*/
 
 /*-
  * Copyright (c) 1982, 1986, 1993
@@ -64,6 +64,16 @@ typedef	__off_t		off_t;		/* file offset 
 #define	PROT_WRITE	0x02	/* pages can be written */
 #define	PROT_EXEC	0x04	/* pages can be executed */
 
+#ifdef _NETBSD_SOURCE
+/*
+ * PAX mprotect prohibits setting protection bits
+ * missing from the original mmap call unless explicitly
+ * requested with PROT_MPROTECT.
+ */
+#define        PROT_MPROTECT(x)                ((x) << 3)
+#define        PROT_MPROTECT_EXTRACT(x)        (((x) >> 3) & 0x7)
+#endif
+
 /*
  * Flags contain sharing type and options.
  * Sharing types; choose one.
@@ -75,6 +85,7 @@ typedef	__off_t		off_t;		/* file offset 
 /*
  * Other flags
  */
+#define	MAP_REMAPDUP	 0x0004	/* mremap only: duplicate the mapping */
 #define	MAP_FIXED	 0x0010	/* map addr must be exactly as requested */
 #define	MAP_RENAME	 0x0020	/* Sun: rename private pages to file */
 #define	MAP_NORESERVE	 0x0040	/* Sun: don't reserve needed swap area */

Index: src/sys/sys/pax.h
diff -u src/sys/sys/pax.h:1.25 src/sys/sys/pax.h:1.26
--- src/sys/sys/pax.h:1.25	Sat Sep  3 12:20:58 2016
+++ src/sys/sys/pax.h	Sat May  6 21:34:52 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: pax.h,v 1.25 2016/09/03 12:20:58 christos Exp $ */
+/* $NetBSD: pax.h,v 1.26 2017/05/06 21:34:52 joerg Exp $ */
 
 /*-
  * Copyright (c) 2006 Elad Efrat <e...@netbsd.org>
@@ -63,23 +63,34 @@ void pax_setup_elf_flags(struct exec_pac
 # define pax_setup_elf_flags(e, flags) __USE(flags)
 #endif
 
-void pax_mprotect_adjust(
+vm_prot_t pax_mprotect_maxprotect(
 #ifdef PAX_MPROTECT_DEBUG
     const char *, size_t,
 #endif
-    struct lwp *, vm_prot_t *, vm_prot_t *);
+    struct lwp *, vm_prot_t, vm_prot_t, vm_prot_t);
+int pax_mprotect_validate(
+#ifdef PAX_MPROTECT_DEBUG
+    const char *, size_t,
+#endif
+    struct lwp *, vm_prot_t);
+
 #ifndef PAX_MPROTECT
-# define PAX_MPROTECT_ADJUST(a, b, c)
+# define PAX_MPROTECT_MAXPROTECT(l, active, extra, max) (max)
+# define PAX_MPROTECT_VALIDATE(l, prot) (0)
 # define pax_mprotect_prot(l)	0
 #else
 # ifdef PAX_MPROTECT_DEBUG
-#  define PAX_MPROTECT_ADJUST(a, b, c) \
-    pax_mprotect_adjust(__FILE__, __LINE__, (a), (b), (c))
+#  define PAX_MPROTECT_MAXPROTECT(l, active, extra, max) \
+    pax_mprotect_maxprotect(__FILE__, __LINE__, (l), (active), (extra), (max))
+#  define PAX_MPROTECT_VALIDATE(l, prot) \
+    pax_mprotect_validate(__FILE__, __LINE__, (l), (prot))
 # else
-#  define PAX_MPROTECT_ADJUST(a, b, c) \
-    pax_mprotect_adjust((a), (b), (c))
+#  define PAX_MPROTECT_MAXPROTECT(l, active, extra, max) \
+    pax_mprotect_maxprotect((l), (active), (extra), (max))
+#  define PAX_MPROTECT_VALIDATE(l, prot) \
+    pax_mprotect_validate((l), (prot))
 # endif
-extern int pax_mprotect_prot(struct lwp *);
+int pax_mprotect_prot(struct lwp *);
 #endif
 int pax_segvguard(struct lwp *, struct vnode *, const char *, bool);
 

Index: src/sys/uvm/uvm_extern.h
diff -u src/sys/uvm/uvm_extern.h:1.203 src/sys/uvm/uvm_extern.h:1.204
--- src/sys/uvm/uvm_extern.h:1.203	Wed Jan  4 23:59:49 2017
+++ src/sys/uvm/uvm_extern.h	Sat May  6 21:34:52 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_extern.h,v 1.203 2017/01/04 23:59:49 christos Exp $	*/
+/*	$NetBSD: uvm_extern.h,v 1.204 2017/05/06 21:34:52 joerg Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -662,6 +662,8 @@ bool			uvm_map_checkprot(struct vm_map *
 			    vaddr_t, vm_prot_t);
 int			uvm_map_protect(struct vm_map *, vaddr_t,
 			    vaddr_t, vm_prot_t, bool);
+int			uvm_map_protect_user(struct lwp *, vaddr_t, vaddr_t,
+			    vm_prot_t);
 struct vmspace		*uvmspace_alloc(vaddr_t, vaddr_t, bool);
 void			uvmspace_init(struct vmspace *, struct pmap *,
 			    vaddr_t, vaddr_t, bool);

Index: src/sys/uvm/uvm_map.c
diff -u src/sys/uvm/uvm_map.c:1.343 src/sys/uvm/uvm_map.c:1.344
--- src/sys/uvm/uvm_map.c:1.343	Wed Mar 15 20:25:41 2017
+++ src/sys/uvm/uvm_map.c	Sat May  6 21:34:52 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_map.c,v 1.343 2017/03/15 20:25:41 christos Exp $	*/
+/*	$NetBSD: uvm_map.c,v 1.344 2017/05/06 21:34:52 joerg Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,9 +66,10 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.343 2017/03/15 20:25:41 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.344 2017/05/06 21:34:52 joerg Exp $");
 
 #include "opt_ddb.h"
+#include "opt_pax.h"
 #include "opt_uvmhist.h"
 #include "opt_uvm.h"
 #include "opt_sysv.h"
@@ -80,6 +81,7 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 
 #include <sys/pool.h>
 #include <sys/kernel.h>
 #include <sys/mount.h>
+#include <sys/pax.h>
 #include <sys/vnode.h>
 #include <sys/filedesc.h>
 #include <sys/lockdebug.h>
@@ -2951,6 +2953,24 @@ uvm_map_submap(struct vm_map *map, vaddr
 }
 
 /*
+ * uvm_map_protect_user: change map protection on behalf of the user.
+ * Enforces PAX settings as necessary.
+ */
+int
+uvm_map_protect_user(struct lwp *l, vaddr_t start, vaddr_t end,
+    vm_prot_t new_prot)
+{
+	int error;
+
+	if ((error = PAX_MPROTECT_VALIDATE(l, new_prot)))
+		return error;
+
+	return uvm_map_protect(&l->l_proc->p_vmspace->vm_map, start, end,
+	    new_prot, false);
+}
+
+
+/*
  * uvm_map_protect: change map protection
  *
  * => set_max means set max_protection.

Index: src/sys/uvm/uvm_mmap.c
diff -u src/sys/uvm/uvm_mmap.c:1.163 src/sys/uvm/uvm_mmap.c:1.164
--- src/sys/uvm/uvm_mmap.c:1.163	Sat Apr 29 01:18:35 2017
+++ src/sys/uvm/uvm_mmap.c	Sat May  6 21:34:52 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_mmap.c,v 1.163 2017/04/29 01:18:35 christos Exp $	*/
+/*	$NetBSD: uvm_mmap.c,v 1.164 2017/05/06 21:34:52 joerg Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -46,7 +46,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_mmap.c,v 1.163 2017/04/29 01:18:35 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_mmap.c,v 1.164 2017/05/06 21:34:52 joerg Exp $");
 
 #include "opt_compat_netbsd.h"
 #include "opt_pax.h"
@@ -287,7 +287,7 @@ sys_mmap(struct lwp *l, const struct sys
 	vaddr_t addr;
 	off_t pos;
 	vsize_t size, pageoff, newsize;
-	vm_prot_t prot, maxprot;
+	vm_prot_t prot, maxprot, extraprot;
 	int flags, fd, advice;
 	vaddr_t defaddr;
 	struct file *fp = NULL;
@@ -304,6 +304,7 @@ sys_mmap(struct lwp *l, const struct sys
 	addr = (vaddr_t)SCARG(uap, addr);
 	size = (vsize_t)SCARG(uap, len);
 	prot = SCARG(uap, prot) & VM_PROT_ALL;
+	extraprot = PROT_MPROTECT_EXTRACT(SCARG(uap, prot));
 	flags = SCARG(uap, flags);
 	fd = SCARG(uap, fd);
 	pos = SCARG(uap, pos);
@@ -396,7 +397,11 @@ sys_mmap(struct lwp *l, const struct sys
 		pos = 0;
 	}
 
-	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
+	maxprot = PAX_MPROTECT_MAXPROTECT(l, prot, extraprot, maxprot);
+	if (((prot | extraprot) & maxprot) != (prot | extraprot))
+		return EACCES;
+	if ((error = PAX_MPROTECT_VALIDATE(l, prot)))
+		return error;
 
 	pax_aslr_mmap(l, &addr, orig_addr, flags);
 
@@ -612,8 +617,7 @@ sys_mprotect(struct lwp *l, const struct
 	if (error)
 		return EINVAL;
 
-	error = uvm_map_protect(&p->p_vmspace->vm_map, addr, addr + size, prot,
-				false);
+	error = uvm_map_protect_user(l, addr, addr + size, prot);
 	return error;
 }
 

Index: src/sys/uvm/uvm_mremap.c
diff -u src/sys/uvm/uvm_mremap.c:1.18 src/sys/uvm/uvm_mremap.c:1.19
--- src/sys/uvm/uvm_mremap.c:1.18	Thu Nov 26 13:15:34 2015
+++ src/sys/uvm/uvm_mremap.c	Sat May  6 21:34:52 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_mremap.c,v 1.18 2015/11/26 13:15:34 martin Exp $	*/
+/*	$NetBSD: uvm_mremap.c,v 1.19 2017/05/06 21:34:52 joerg Exp $	*/
 
 /*-
  * Copyright (c)2006,2007,2009 YAMAMOTO Takashi,
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_mremap.c,v 1.18 2015/11/26 13:15:34 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_mremap.c,v 1.19 2017/05/06 21:34:52 joerg Exp $");
 
 #include <sys/param.h>
 #include <sys/mman.h>
@@ -120,6 +120,7 @@ uvm_mremap(struct vm_map *oldmap, vaddr_
 	vaddr_t align = 0;
 	int error = 0;
 	const bool fixed = (flags & MAP_FIXED) != 0;
+	const bool duplicate = (flags & MAP_REMAPDUP) != 0;
 
 	if (fixed) {
 		newva = *newvap;
@@ -165,7 +166,8 @@ uvm_mremap(struct vm_map *oldmap, vaddr_
 	 * check the easy cases first.
 	 */
 
-	if ((!fixed || newva == oldva) && newmap == oldmap &&
+	if (!duplicate &&
+	    (!fixed || newva == oldva) && newmap == oldmap &&
 	    (align == 0 || (oldva & (align - 1)) == 0)) {
 		vaddr_t va;
 
@@ -240,7 +242,7 @@ extend:
 	 * remove original entries unless we did in-place extend.
 	 */
 
-	if (oldva != newva || oldmap != newmap) {
+	if (!duplicate && (oldva != newva || oldmap != newmap)) {
 		uvm_unmap(oldmap, oldva, oldva + oldsize);
 	}
 done:
@@ -278,7 +280,7 @@ sys_mremap(struct lwp *l, const struct s
 	newva = (vaddr_t)SCARG(uap, new_address);
 	newsize = (vsize_t)(SCARG(uap, new_size));
 
-	if ((flags & ~(MAP_FIXED | MAP_ALIGNMENT_MASK)) != 0) {
+	if ((flags & ~(MAP_FIXED | MAP_REMAPDUP | MAP_ALIGNMENT_MASK)) != 0) {
 		error = EINVAL;
 		goto done;
 	}

Index: src/sys/uvm/uvm_unix.c
diff -u src/sys/uvm/uvm_unix.c:1.47 src/sys/uvm/uvm_unix.c:1.48
--- src/sys/uvm/uvm_unix.c:1.47	Thu Apr  7 12:07:36 2016
+++ src/sys/uvm/uvm_unix.c	Sat May  6 21:34:52 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_unix.c,v 1.47 2016/04/07 12:07:36 christos Exp $	*/
+/*	$NetBSD: uvm_unix.c,v 1.48 2017/05/06 21:34:52 joerg Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -45,7 +45,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_unix.c,v 1.47 2016/04/07 12:07:36 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_unix.c,v 1.48 2017/05/06 21:34:52 joerg Exp $");
 
 #include "opt_pax.h"
 
@@ -97,10 +97,10 @@ sys_obreak(struct lwp *l, const struct s
 	 */
 
 	if (nbreak > obreak) {
-		vm_prot_t prot = UVM_PROT_READ | UVM_PROT_WRITE;
-		vm_prot_t maxprot = UVM_PROT_ALL;
-
-		PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
+		vm_prot_t prot = UVM_PROT_RW;
+		vm_prot_t maxprot;
+		
+		maxprot = PAX_MPROTECT_MAXPROTECT(l, prot, 0, UVM_PROT_ALL);
 
 		error = uvm_map(&vm->vm_map, &obreak, nbreak - obreak, NULL,
 		    UVM_UNKNOWN_OFFSET, 0,

Index: src/tests/lib/libc/sys/t_mprotect.c
diff -u src/tests/lib/libc/sys/t_mprotect.c:1.6 src/tests/lib/libc/sys/t_mprotect.c:1.7
--- src/tests/lib/libc/sys/t_mprotect.c:1.6	Sat Mar 25 01:39:20 2017
+++ src/tests/lib/libc/sys/t_mprotect.c	Sat May  6 21:34:52 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: t_mprotect.c,v 1.6 2017/03/25 01:39:20 pgoyette Exp $ */
+/* $NetBSD: t_mprotect.c,v 1.7 2017/05/06 21:34:52 joerg Exp $ */
 
 /*-
  * Copyright (c) 2011 The NetBSD Foundation, Inc.
@@ -29,7 +29,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: t_mprotect.c,v 1.6 2017/03/25 01:39:20 pgoyette Exp $");
+__RCSID("$NetBSD: t_mprotect.c,v 1.7 2017/05/06 21:34:52 joerg Exp $");
 
 #include <sys/param.h>
 #include <sys/mman.h>
@@ -161,7 +161,6 @@ ATF_TC_BODY(mprotect_exec, tc)
 		break;
 	}
 
-
 	/*
 	 * Map a page read/write and copy a trivial assembly function inside.
 	 * We will then change the mapping rights:
@@ -171,7 +170,8 @@ ATF_TC_BODY(mprotect_exec, tc)
 	 *   a SIGSEGV on architectures that can enforce --x permissions.
 	 */
 
-	map = mmap(NULL, page, PROT_WRITE|PROT_READ, MAP_ANON, -1, 0);
+	map = mmap(NULL, page, PROT_MPROTECT(PROT_EXEC)|PROT_WRITE|PROT_READ,
+	    MAP_ANON, -1, 0);
 	ATF_REQUIRE(map != MAP_FAILED);
 
 	memcpy(map, (void *)return_one,
@@ -312,6 +312,77 @@ ATF_TC_BODY(mprotect_write, tc)
 	ATF_REQUIRE(munmap(map, page) == 0);
 }
 
+ATF_TC(mprotect_mremap_exec);
+ATF_TC_HEAD(mprotect_mremap_exec, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Test mremap(2)+mprotect(2) executable space protections");
+}
+
+/*
+ * Trivial function -- should fit into a page
+ */
+ATF_TC_BODY(mprotect_mremap_exec, tc)
+{
+	void *map, *map2;
+	pid_t pid;
+	int sta;
+
+	/*
+	 * Map a page read/write/exec and duplicate it.
+	 * Map the copy executable.
+	 * Copy a trivial assembly function to the writeable mapping.
+	 * Try to execute it. This should never create a SIGSEGV.
+	 */
+
+	map = mmap(NULL, page, PROT_MPROTECT(PROT_EXEC|PROT_WRITE|PROT_READ),
+	    MAP_ANON, -1, 0);
+	ATF_REQUIRE(map != MAP_FAILED);
+	map2 = mremap(map, page, NULL, page, MAP_REMAPDUP);
+	ATF_REQUIRE(map2 != MAP_FAILED);
+	ATF_REQUIRE(mprotect(map, page, PROT_WRITE|PROT_READ) == 0);
+	ATF_REQUIRE(mprotect(map2, page, PROT_EXEC|PROT_READ) == 0);
+
+	memcpy(map, (void *)return_one,
+	    (uintptr_t)return_one_end - (uintptr_t)return_one);
+	__builtin___clear_cache(map, (void *)((uintptr_t)map + page));
+
+	ATF_REQUIRE(((int (*)(void))map2)() == 1);
+
+	/* Double check that the executable mapping is not writeable. */
+	pid = fork();
+	ATF_REQUIRE(pid >= 0);
+
+	if (pid == 0) {
+		ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
+		ATF_REQUIRE(strlcpy(map2, "XXX", 3) == 0);
+	}
+
+	(void)wait(&sta);
+
+	ATF_REQUIRE(WIFEXITED(sta) != 0);
+	ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
+
+	if (exec_prot_support() == PERPAGE_XP) {
+		/* Double check that the writeable mapping is not executable. */
+		pid = fork();
+		ATF_REQUIRE(pid >= 0);
+
+		if (pid == 0) {
+			ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
+			ATF_REQUIRE(((int (*)(void))map)() == 1);
+		}
+
+		(void)wait(&sta);
+
+		ATF_REQUIRE(WIFEXITED(sta) != 0);
+		ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
+	}
+
+	ATF_REQUIRE(munmap(map, page) == 0);
+	ATF_REQUIRE(munmap(map2, page) == 0);
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 	page = sysconf(_SC_PAGESIZE);
@@ -322,6 +393,7 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, mprotect_exec);
 	ATF_TP_ADD_TC(tp, mprotect_pax);
 	ATF_TP_ADD_TC(tp, mprotect_write);
+	ATF_TP_ADD_TC(tp, mprotect_mremap_exec);
 
 	return atf_no_error();
 }

Reply via email to