tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

PAX mprotect and JIT v2



Hi all,
this is the second version of changes to improve the JIT support in
current. This version extends mmap(2)'s protection field to allow
specifying additional protections that can be enabled by mprotect later.
With the changes mprotect allows setting any permissions originally
requested and not more. I.e. if a page was originally mapped with
PROT_READ, it will never be writeable or executable. The PAX integration
is now split into two separate parts:
(1) Compute the maximum protection level for a mapping.
(2) Verify that a requested protection level is valid.

mmap and mprotect will now explicitly return an error if the requested
protection is not invalid, they won't silently truncate protections.
Stricter PAX mprotect changes would be implementable without further
interface changes, i.e. like the current "never make a writeable page
executable", but I'm not including anything like that as I find it of
questionable usefulness.

The patch also includes the mremap part as well as test cases.

Joerg
Index: lib/libc/sys/mmap.2
===================================================================
RCS file: /home/joerg/repo/netbsd/src/lib/libc/sys/mmap.2,v
retrieving revision 1.48
diff -u -p -r1.48 mmap.2
--- lib/libc/sys/mmap.2	27 Feb 2015 16:18:00 -0000	1.48
+++ lib/libc/sys/mmap.2	27 Apr 2017 15:07:48 -0000
@@ -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 requesting any access permissions.
 .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: lib/libc/sys/mprotect.2
===================================================================
RCS file: /home/joerg/repo/netbsd/src/lib/libc/sys/mprotect.2,v
retrieving revision 1.24
diff -u -p -r1.24 mprotect.2
--- lib/libc/sys/mprotect.2	3 Apr 2011 06:54:30 -0000	1.24
+++ lib/libc/sys/mprotect.2	27 Apr 2017 15:08:05 -0000
@@ -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 requesting any access permissions.
 .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: share/man/man9/uvm_map.9
===================================================================
RCS file: /home/joerg/repo/netbsd/src/share/man/man9/uvm_map.9,v
retrieving revision 1.6
diff -u -p -r1.6 uvm_map.9
--- share/man/man9/uvm_map.9	12 Sep 2014 21:06:25 -0000	1.6
+++ share/man/man9/uvm_map.9	27 Apr 2017 14:26:37 -0000
@@ -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: sys/compat/linux/common/linux_misc.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/compat/linux/common/linux_misc.c,v
retrieving revision 1.237
diff -u -p -r1.237 linux_misc.c
--- sys/compat/linux/common/linux_misc.c	28 Jan 2017 15:01:01 -0000	1.237
+++ sys/compat/linux/common/linux_misc.c	27 Apr 2017 14:00:19 -0000
@@ -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: sys/kern/exec_subr.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/kern/exec_subr.c,v
retrieving revision 1.76
diff -u -p -r1.76 exec_subr.c
--- sys/kern/exec_subr.c	22 May 2016 14:26:09 -0000	1.76
+++ sys/kern/exec_subr.c	21 Apr 2017 12:14:00 -0000
@@ -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 EPERM;
+	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 EPERM;
+	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 EPERM;
+	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: sys/kern/kern_pax.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/kern/kern_pax.c,v
retrieving revision 1.58
diff -u -p -r1.58 kern_pax.c
--- sys/kern/kern_pax.c	18 Feb 2017 01:29:09 -0000	1.58
+++ sys/kern/kern_pax.c	27 Apr 2017 14:57:41 -0000
@@ -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: sys/sys/mman.h
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/sys/mman.h,v
retrieving revision 1.50
diff -u -p -r1.50 mman.h
--- sys/sys/mman.h	1 Jun 2016 00:46:44 -0000	1.50
+++ sys/sys/mman.h	21 Apr 2017 21:16:55 -0000
@@ -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.
@@ -82,6 +92,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: sys/sys/pax.h
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/sys/pax.h,v
retrieving revision 1.25
diff -u -p -r1.25 pax.h
--- sys/sys/pax.h	3 Sep 2016 12:20:58 -0000	1.25
+++ sys/sys/pax.h	21 Apr 2017 12:38:29 -0000
@@ -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: sys/uvm/uvm_extern.h
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/uvm/uvm_extern.h,v
retrieving revision 1.203
diff -u -p -r1.203 uvm_extern.h
--- sys/uvm/uvm_extern.h	4 Jan 2017 23:59:49 -0000	1.203
+++ sys/uvm/uvm_extern.h	27 Apr 2017 13:59:58 -0000
@@ -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: sys/uvm/uvm_map.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/uvm/uvm_map.c,v
retrieving revision 1.343
diff -u -p -r1.343 uvm_map.c
--- sys/uvm/uvm_map.c	15 Mar 2017 20:25:41 -0000	1.343
+++ sys/uvm/uvm_map.c	27 Apr 2017 14:23:19 -0000
@@ -69,6 +69,7 @@
 __KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.343 2017/03/15 20:25:41 christos 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: sys/uvm/uvm_mmap.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/uvm/uvm_mmap.c,v
retrieving revision 1.162
diff -u -p -r1.162 uvm_mmap.c
--- sys/uvm/uvm_mmap.c	9 Aug 2016 12:17:04 -0000	1.162
+++ sys/uvm/uvm_mmap.c	27 Apr 2017 14:57:22 -0000
@@ -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);
@@ -411,7 +412,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);
 
@@ -627,8 +632,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: sys/uvm/uvm_mremap.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/uvm/uvm_mremap.c,v
retrieving revision 1.18
diff -u -p -r1.18 uvm_mremap.c
--- sys/uvm/uvm_mremap.c	26 Nov 2015 13:15:34 -0000	1.18
+++ sys/uvm/uvm_mremap.c	21 Apr 2017 11:38:09 -0000
@@ -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: sys/uvm/uvm_unix.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/uvm/uvm_unix.c,v
retrieving revision 1.47
diff -u -p -r1.47 uvm_unix.c
--- sys/uvm/uvm_unix.c	7 Apr 2016 12:07:36 -0000	1.47
+++ sys/uvm/uvm_unix.c	21 Apr 2017 12:04:38 -0000
@@ -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: tests/lib/libc/sys/t_mprotect.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/tests/lib/libc/sys/t_mprotect.c,v
retrieving revision 1.6
diff -u -p -r1.6 t_mprotect.c
--- tests/lib/libc/sys/t_mprotect.c	25 Mar 2017 01:39:20 -0000	1.6
+++ tests/lib/libc/sys/t_mprotect.c	21 Apr 2017 20:58:45 -0000
@@ -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();
 }


Home | Main Index | Thread Index | Old Index