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(); }