tech-kern archive

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

Re: PAX mprotect and JIT



On Sat, Feb 25, 2017 at 10:35:27PM +0100, Joerg Sonnenberger wrote:
> (2) A hack for allow mprotect to switch between W and X, but still
> honoring W^X. This is a hack and needs to be carefully rethought,
> since I believe the way pax is currently implemented is wrong. Consider
> it a PoC.

Attached is a new version of the patch. This introduces the new concept
of protection limits. It is the initial permissions of a mapping as
requested by the user (or the driver in case of in-kernel mappings).
mprotect is allowed if the new permissions are allowed for all parts of
the mapping, independent of any restrictions PAX might have added. As
such, RWX mapping requests will result in an effective permission of RW,
but mprotect can be used to turn them into RX later. The test case
depends on the earlier mremap patch and verifies basic operation.

Patch is tested on amd64 so far, other architectures might need a few
more changes for UVM_MAPFLAG.

Joerg
Index: sys/arch/xen/xen/privcmd.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/arch/xen/xen/privcmd.c,v
retrieving revision 1.49
diff -u -p -r1.49 privcmd.c
--- sys/arch/xen/xen/privcmd.c	17 Oct 2014 16:37:02 -0000	1.49
+++ sys/arch/xen/xen/privcmd.c	24 Mar 2017 15:07:34 -0000
@@ -566,7 +566,7 @@ privcmd_map_obj(struct vm_map *map, vadd
 	obj->npages = npages;
 	obj->domid = domid;
 	mutex_exit(obj->uobj.vmobjlock);
-	uvmflag = UVM_MAPFLAG(prot, prot, UVM_INH_NONE, UVM_ADV_NORMAL,
+	uvmflag = UVM_MAPFLAG(prot, prot, prot, UVM_INH_NONE, UVM_ADV_NORMAL,
 	    UVM_FLAG_FIXED | UVM_FLAG_NOMERGE);
 	error = uvm_map(map, &newstart, size, &obj->uobj, 0, 0, uvmflag);
 
Index: sys/external/bsd/drm2/dist/drm/i915/i915_gem.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/external/bsd/drm2/dist/drm/i915/i915_gem.c,v
retrieving revision 1.33
diff -u -p -r1.33 i915_gem.c
--- sys/external/bsd/drm2/dist/drm/i915/i915_gem.c	26 Nov 2015 13:15:34 -0000	1.33
+++ sys/external/bsd/drm2/dist/drm/i915/i915_gem.c	24 Mar 2017 14:47:25 -0000
@@ -1777,9 +1777,8 @@ i915_gem_mmap_ioctl(struct drm_device *d
 	/* XXX errno NetBSD->Linux */
 	ret = -uvm_map(&curproc->p_vmspace->vm_map, &addr, args->size,
 	    obj->gemo_shm_uao, args->offset, 0,
-	    UVM_MAPFLAG((VM_PROT_READ | VM_PROT_WRITE),
-		(VM_PROT_READ | VM_PROT_WRITE), UVM_INH_COPY, UVM_ADV_NORMAL,
-		0));
+	    UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_PROT_RW, UVM_INH_COPY,
+	        UVM_ADV_NORMAL, 0));
 	if (ret) {
 		drm_gem_object_unreference_unlocked(obj);
 		return ret;
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	24 Mar 2017 14:48:05 -0000
@@ -162,7 +162,7 @@ vmcmd_map_pagedvn(struct lwp *l, struct 
 	struct vnode *vp = cmd->ev_vp;
 	struct proc *p = l->l_proc;
 	int error;
-	vm_prot_t prot, maxprot;
+	vm_prot_t prot, maxprot, limprot;
 
 	KASSERT(vp->v_iflag & VI_TEXT);
 
@@ -180,7 +180,7 @@ vmcmd_map_pagedvn(struct lwp *l, struct 
 		return EINVAL;
 
 	prot = cmd->ev_prot;
-	maxprot = UVM_PROT_ALL;
+	limprot = maxprot = UVM_PROT_ALL;
 	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
 
 	/*
@@ -203,9 +203,13 @@ vmcmd_map_pagedvn(struct lwp *l, struct 
 	uobj = &vp->v_uobj;
 	vref(vp);
 
+	prot = cmd->ev_prot;
+	limprot = maxprot = UVM_PROT_ALL;
+	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
+
 	error = uvm_map(&p->p_vmspace->vm_map, &cmd->ev_addr, cmd->ev_len,
 		uobj, cmd->ev_offset, 0,
-		UVM_MAPFLAG(prot, maxprot, UVM_INH_COPY,
+		UVM_MAPFLAG(prot, maxprot, limprot, UVM_INH_COPY,
 			UVM_ADV_NORMAL, UVM_FLAG_COPYONW|UVM_FLAG_FIXED));
 	if (error) {
 		uobj->pgops->pgo_detach(uobj);
@@ -225,6 +229,7 @@ vmcmd_map_readvn(struct lwp *l, struct e
 	struct proc *p = l->l_proc;
 	int error;
 	long diff;
+	vm_prot_t prot, maxprot, limprot;
 
 	if (cmd->ev_len == 0)
 		return 0;
@@ -234,9 +239,13 @@ vmcmd_map_readvn(struct lwp *l, struct e
 	cmd->ev_offset -= diff;
 	cmd->ev_len += diff;
 
+	prot = VM_PROT_ALL;
+	limprot = maxprot = VM_PROT_ALL;
+	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
+
 	error = uvm_map(&p->p_vmspace->vm_map, &cmd->ev_addr,
 			round_page(cmd->ev_len), NULL, UVM_UNKNOWN_OFFSET, 0,
-			UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_COPY,
+			UVM_MAPFLAG(prot, maxprot, limprot, UVM_INH_COPY,
 			UVM_ADV_NORMAL,
 			UVM_FLAG_FIXED|UVM_FLAG_OVERLAY|UVM_FLAG_COPYONW));
 
@@ -311,19 +320,19 @@ vmcmd_map_zero(struct lwp *l, struct exe
 	struct proc *p = l->l_proc;
 	int error;
 	long diff;
-	vm_prot_t prot, maxprot;
+	vm_prot_t prot, maxprot, limprot;
 
 	diff = cmd->ev_addr - trunc_page(cmd->ev_addr);
 	cmd->ev_addr -= diff;			/* required by uvm_map */
 	cmd->ev_len += diff;
 
 	prot = cmd->ev_prot;
-	maxprot = UVM_PROT_ALL;
+	limprot = maxprot = UVM_PROT_ALL;
 	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
 
 	error = uvm_map(&p->p_vmspace->vm_map, &cmd->ev_addr,
 			round_page(cmd->ev_len), NULL, UVM_UNKNOWN_OFFSET, 0,
-			UVM_MAPFLAG(prot, maxprot, UVM_INH_COPY,
+			UVM_MAPFLAG(prot, maxprot, limprot, UVM_INH_COPY,
 			UVM_ADV_NORMAL,
 			UVM_FLAG_FIXED|UVM_FLAG_COPYONW));
 	if (cmd->ev_flags & VMCMD_STACK)
Index: sys/kern/init_main.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/kern/init_main.c,v
retrieving revision 1.490
diff -u -p -r1.490 init_main.c
--- sys/kern/init_main.c	16 Jan 2017 09:28:40 -0000	1.490
+++ sys/kern/init_main.c	24 Mar 2017 14:41:25 -0000
@@ -943,7 +943,7 @@ start_init(void *arg)
 	addr = (vaddr_t)STACK_ALLOC(USRSTACK, PAGE_SIZE);
 	if (uvm_map(&p->p_vmspace->vm_map, &addr, PAGE_SIZE,
 	    NULL, UVM_UNKNOWN_OFFSET, 0,
-	    UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_INH_COPY,
+	    UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_PROT_RW, UVM_INH_COPY,
 	    UVM_ADV_NORMAL,
 	    UVM_FLAG_FIXED|UVM_FLAG_OVERLAY|UVM_FLAG_COPYONW)) != 0)
 		panic("init: couldn't allocate argument space");
Index: sys/kern/kern_exec.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/kern/kern_exec.c,v
retrieving revision 1.441
diff -u -p -r1.441 kern_exec.c
--- sys/kern/kern_exec.c	25 Jan 2017 17:57:14 -0000	1.441
+++ sys/kern/kern_exec.c	24 Mar 2017 14:41:18 -0000
@@ -1927,7 +1927,7 @@ exec_sigcode_map(struct proc *p, const s
 			va = vm_map_min(kernel_map);
 			if ((error = uvm_map(kernel_map, &va, round_page(sz),
 			    uobj, 0, 0,
-			    UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW,
+			    UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_PROT_RW,
 			    UVM_INH_SHARE, UVM_ADV_RANDOM, 0)))) {
 				printf("kernel mapping failed %d\n", error);
 				(*uobj->pgops->pgo_detach)(uobj);
@@ -1962,8 +1962,8 @@ exec_sigcode_map(struct proc *p, const s
 	(*uobj->pgops->pgo_reference)(uobj);
 	error = uvm_map(&p->p_vmspace->vm_map, &va, round_page(sz),
 			uobj, 0, 0,
-			UVM_MAPFLAG(UVM_PROT_RX, UVM_PROT_RX, UVM_INH_SHARE,
-				    UVM_ADV_RANDOM, 0));
+			UVM_MAPFLAG(UVM_PROT_RX, UVM_PROT_RX, UVM_PROT_RX,
+			            UVM_INH_SHARE, UVM_ADV_RANDOM, 0));
 	if (error) {
 		DPRINTF(("%s, %d: map %p "
 		    "uvm_map %#"PRIxVSIZE"@%#"PRIxVADDR" failed %d\n",
Index: sys/kern/kern_lwp.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/kern/kern_lwp.c,v
retrieving revision 1.187
diff -u -p -r1.187 kern_lwp.c
--- sys/kern/kern_lwp.c	14 Jan 2017 19:32:10 -0000	1.187
+++ sys/kern/kern_lwp.c	24 Mar 2017 14:45:51 -0000
@@ -1785,7 +1785,7 @@ lwp_ctl_alloc(vaddr_t *uaddr)
 		     p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN);
 		error = uvm_map(&p->p_vmspace->vm_map, &lp->lp_uva,
 		    LWPCTL_UAREA_SZ, lp->lp_uao, 0, 0, UVM_MAPFLAG(UVM_PROT_RW,
-		    UVM_PROT_RW, UVM_INH_NONE, UVM_ADV_NORMAL, 0));
+		    UVM_PROT_RW, UVM_PROT_RW, UVM_INH_NONE, UVM_ADV_NORMAL, 0));
 		if (error != 0) {
 			uao_detach(lp->lp_uao);
 			lp->lp_uao = NULL;
@@ -1819,7 +1819,7 @@ lwp_ctl_alloc(vaddr_t *uaddr)
 		lcp->lcp_kaddr = vm_map_min(kernel_map);
 		error = uvm_map(kernel_map, &lcp->lcp_kaddr, PAGE_SIZE,
 		    uao, lp->lp_cur, PAGE_SIZE,
-		    UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW,
+		    UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_PROT_RW,
 		    UVM_INH_NONE, UVM_ADV_RANDOM, 0));
 		if (error != 0) {
 			mutex_exit(&lp->lp_lock);
Index: sys/kern/sysv_shm.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/kern/sysv_shm.c,v
retrieving revision 1.131
diff -u -p -r1.131 sysv_shm.c
--- sys/kern/sysv_shm.c	26 Nov 2015 13:15:34 -0000	1.131
+++ sys/kern/sysv_shm.c	24 Mar 2017 14:45:30 -0000
@@ -459,7 +459,7 @@ sys_shmat(struct lwp *l, const struct sy
 	uobj = shmseg->_shm_internal;
 	uao_reference(uobj);
 	error = uvm_map(&vm->vm_map, &attach_va, size, uobj, 0, 0,
-	    UVM_MAPFLAG(prot, prot, UVM_INH_SHARE, UVM_ADV_RANDOM, flags));
+	    UVM_MAPFLAG(prot, prot, prot, UVM_INH_SHARE, UVM_ADV_RANDOM, flags));
 	if (error)
 		goto err_detach;
 	if (shm_use_phys || (shmseg->shm_perm.mode & SHMSEG_WIRED)) {
Index: sys/uvm/uvm_bio.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/uvm/uvm_bio.c,v
retrieving revision 1.89
diff -u -p -r1.89 uvm_bio.c
--- sys/uvm/uvm_bio.c	21 Mar 2017 02:24:35 -0000	1.89
+++ sys/uvm/uvm_bio.c	24 Mar 2017 14:06:07 -0000
@@ -207,8 +207,9 @@ ubc_init(void)
 
 	if (uvm_map(kernel_map, (vaddr_t *)&ubc_object.kva,
 		    ubc_nwins << ubc_winshift, &ubc_object.uobj, 0, (vsize_t)va,
-		    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_NONE,
-				UVM_ADV_RANDOM, UVM_FLAG_NOMERGE)) != 0) {
+		    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_PROT_ALL,
+		                UVM_INH_NONE, UVM_ADV_RANDOM,
+		                UVM_FLAG_NOMERGE)) != 0) {
 		panic("ubc_init: failed to map ubc_object");
 	}
 }
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	24 Mar 2017 15:30:56 -0000
@@ -120,7 +120,7 @@
 #define UVM_ADV_NOREUSE	0x5	/* pages will be used only once */
 #define UVM_ADV_MASK	0x7	/* mask */
 
-/* bits 0xffff0000: mapping flags */
+/* bits 0x0fff0000: mapping flags */
 #define UVM_FLAG_FIXED   0x010000 /* find space */
 #define UVM_FLAG_OVERLAY 0x020000 /* establish overlay */
 #define UVM_FLAG_NOMERGE 0x040000 /* don't merge map entries */
@@ -131,15 +131,22 @@
 #define UVM_FLAG_WAITVA  0x800000 /* wait for va */
 #define UVM_FLAG_VAONLY  0x2000000 /* unmap: no pages are mapped */
 #define UVM_FLAG_COLORMATCH 0x4000000 /* match color given in off */
+/* 0x08000000: unused */
+
+/* bits 0x70000000: limits for max protection */
+
+/* 0x80000000: unused */
 
 /* macros to extract info */
 #define UVM_PROTECTION(X)	((X) & UVM_PROT_MASK)
 #define UVM_INHERIT(X)		(((X) & UVM_INH_MASK) >> 4)
 #define UVM_MAXPROTECTION(X)	(((X) >> 8) & UVM_PROT_MASK)
+#define UVM_LIMITPROTECTION(X)	(((X) >> 28) & UVM_PROT_MASK)
 #define UVM_ADVICE(X)		(((X) >> 12) & UVM_ADV_MASK)
 
-#define UVM_MAPFLAG(PROT,MAXPROT,INH,ADVICE,FLAGS) \
-	(((MAXPROT) << 8)|(PROT)|(INH)|((ADVICE) << 12)|(FLAGS))
+#define UVM_MAPFLAG(PROT,MAXPROT,LIMPROT,INH,ADVICE,FLAGS) \
+	(((MAXPROT) << 8) | (PROT) | (INH) | ((ADVICE) << 12) | \
+	 (FLAGS)| ((LIMPROT) << 28))
 
 /* magic offset value: offset not known(obj) or don't care(!obj) */
 #define UVM_UNKNOWN_OFFSET ((voff_t) -1)
Index: sys/uvm/uvm_km.c
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/uvm/uvm_km.c,v
retrieving revision 1.142
diff -u -p -r1.142 uvm_km.c
--- sys/uvm/uvm_km.c	19 Mar 2017 23:44:34 -0000	1.142
+++ sys/uvm/uvm_km.c	24 Mar 2017 14:05:37 -0000
@@ -287,8 +287,9 @@ uvm_km_bootstrap(vaddr_t start, vaddr_t 
 		error = uvm_map_prepare(&kernel_map_store,
 		    base, start - base,
 		    NULL, UVM_UNKNOWN_OFFSET, 0,
-		    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_NONE,
-		    		UVM_ADV_RANDOM, UVM_FLAG_FIXED), &args);
+		    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_PROT_ALL,
+		                UVM_INH_NONE, UVM_ADV_RANDOM, UVM_FLAG_FIXED),
+		    &args);
 		if (!error) {
 			kernel_image_mapent_store.flags =
 			    UVM_MAP_KERNEL | UVM_MAP_STATIC | UVM_MAP_NOMERGE;
@@ -308,7 +309,7 @@ uvm_km_bootstrap(vaddr_t start, vaddr_t 
 	error = uvm_map_prepare(&kernel_map_store,
 	    kmembase, kmemsize,
 	    NULL, UVM_UNKNOWN_OFFSET, 0,
-	    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_NONE,
+	    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_NONE,
 	    		UVM_ADV_RANDOM, UVM_FLAG_FIXED), &args);
 	if (!error) {
 		kernel_kmem_mapent_store.flags =
@@ -396,7 +397,7 @@ uvm_km_suballoc(struct vm_map *map, vadd
 	 */
 
 	if (uvm_map(map, vmin, size, NULL, UVM_UNKNOWN_OFFSET, 0,
-	    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_NONE,
+	    UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_NONE,
 	    UVM_ADV_RANDOM, mapflags)) != 0) {
 		panic("%s: unable to allocate space in parent map", __func__);
 	}
@@ -619,8 +620,8 @@ uvm_km_alloc(struct vm_map *map, vsize_t
 
 	vaprot = (flags & UVM_KMF_EXEC) ? UVM_PROT_ALL : UVM_PROT_RW;
 	if (__predict_false(uvm_map(map, &kva, size, obj, UVM_UNKNOWN_OFFSET,
-	    align, UVM_MAPFLAG(vaprot, UVM_PROT_ALL, UVM_INH_NONE,
-	    UVM_ADV_RANDOM,
+	    align, UVM_MAPFLAG(vaprot, UVM_PROT_ALL, UVM_PROT_ALL,
+	    UVM_INH_NONE, UVM_ADV_RANDOM,
 	    (flags & (UVM_KMF_TRYLOCK | UVM_KMF_NOWAIT | UVM_KMF_WAITVA
 	     | UVM_KMF_COLORMATCH)))) != 0)) {
 		UVMHIST_LOG(maphist, "<- done (no VM)",0,0,0,0);
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	24 Mar 2017 14:52:19 -0000
@@ -190,12 +190,13 @@ int user_va0_disable = __USER_VA0_DISABL
 extern struct vm_map *pager_map;
 
 #define	UVM_ET_ISCOMPATIBLE(ent, type, uobj, meflags, \
-    prot, maxprot, inh, adv, wire) \
+    prot, maxprot, limprot, inh, adv, wire) \
 	((ent)->etype == (type) && \
 	(((ent)->flags ^ (meflags)) & (UVM_MAP_NOMERGE)) == 0 && \
 	(ent)->object.uvm_obj == (uobj) && \
 	(ent)->protection == (prot) && \
 	(ent)->max_protection == (maxprot) && \
+	(ent)->lim_protection == (limprot) && \
 	(ent)->inheritance == (inh) && \
 	(ent)->advice == (adv) && \
 	(ent)->wired_count == (wire))
@@ -1255,6 +1256,7 @@ uvm_map_enter(struct vm_map *map, const 
 	const uvm_flag_t flags = args->uma_flags;
 	const vm_prot_t prot = UVM_PROTECTION(flags);
 	const vm_prot_t maxprot = UVM_MAXPROTECTION(flags);
+	const vm_prot_t limprot = UVM_LIMITPROTECTION(flags);
 	const vm_inherit_t inherit = UVM_INHERIT(flags);
 	const int amapwaitflag = (flags & UVM_FLAG_NOWAIT) ?
 	    AMAP_EXTEND_NOWAIT : 0;
@@ -1303,7 +1305,7 @@ uvm_map_enter(struct vm_map *map, const 
 	if (prev_entry->end == start &&
 	    prev_entry != &map->header &&
 	    UVM_ET_ISCOMPATIBLE(prev_entry, newetype, uobj, 0,
-	    prot, maxprot, inherit, advice, 0)) {
+	    prot, maxprot, limprot, inherit, advice, 0)) {
 
 		if (uobj && prev_entry->offset +
 		    (prev_entry->end - prev_entry->start) != uoffset)
@@ -1360,7 +1362,7 @@ forwardmerge:
 	if (prev_entry->next->start == (start + size) &&
 	    prev_entry->next != &map->header &&
 	    UVM_ET_ISCOMPATIBLE(prev_entry->next, newetype, uobj, 0,
-	    prot, maxprot, inherit, advice, 0)) {
+	    prot, maxprot, limprot, inherit, advice, 0)) {
 
 		if (uobj && prev_entry->next->offset != uoffset + size)
 			goto nomerge;
@@ -1522,6 +1524,7 @@ nomerge:
 
 		new_entry->protection = prot;
 		new_entry->max_protection = maxprot;
+		new_entry->lim_protection = limprot;
 		new_entry->inheritance = inherit;
 		new_entry->wired_count = 0;
 		new_entry->advice = advice;
@@ -2399,8 +2402,8 @@ uvm_map_reserve(struct vm_map *map, vsiz
 	 */
 
 	if (uvm_map(map, raddr, size, NULL, offset, align,
-	    UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_INH_NONE,
-	    UVM_ADV_RANDOM, UVM_FLAG_NOMERGE|flags)) != 0) {
+	    UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_PROT_NONE,
+	    UVM_INH_NONE, UVM_ADV_RANDOM, UVM_FLAG_NOMERGE|flags)) != 0) {
 	    UVMHIST_LOG(maphist, "<- done (no VM)", 0,0,0,0);
 		return (false);
 	}
@@ -2988,7 +2991,11 @@ uvm_map_protect(struct vm_map *map, vadd
 			error = EINVAL;
 			goto out;
 		}
-		if ((new_prot & current->max_protection) != new_prot) {
+		if ((new_prot & current->lim_protection) != new_prot) {
+			error = EACCES;
+			goto out;
+		}
+		if (!set_max && (new_prot & current->max_protection) != new_prot) {
 			error = EACCES;
 			goto out;
 		}
@@ -3021,10 +3028,8 @@ uvm_map_protect(struct vm_map *map, vadd
 		UVM_MAP_CLIP_END(map, current, end);
 		old_prot = current->protection;
 		if (set_max)
-			current->protection =
-			    (current->max_protection = new_prot) & old_prot;
-		else
-			current->protection = new_prot;
+			current->max_protection = new_prot;
+		current->protection = new_prot;
 
 		/*
 		 * update physical map if necessary.  worry about copy-on-write
@@ -4523,7 +4528,8 @@ uvm_mapent_trymerge(struct vm_map *map, 
 	    (!copying && next->aref.ar_amap == NULL)) &&
 	    UVM_ET_ISCOMPATIBLE(next, newetype,
 	    uobj, entry->flags, entry->protection,
-	    entry->max_protection, entry->inheritance, entry->advice,
+	    entry->max_protection, entry->lim_protection,
+	    entry->inheritance, entry->advice,
 	    entry->wired_count) &&
 	    (uobj == NULL || entry->offset + size == next->offset)) {
 		int error;
@@ -4562,7 +4568,8 @@ uvm_mapent_trymerge(struct vm_map *map, 
 	    (!copying && prev->aref.ar_amap == NULL)) &&
 	    UVM_ET_ISCOMPATIBLE(prev, newetype,
 	    uobj, entry->flags, entry->protection,
-	    entry->max_protection, entry->inheritance, entry->advice,
+	    entry->max_protection, entry->lim_protection,
+	    entry->inheritance, entry->advice,
 	    entry->wired_count) &&
 	    (uobj == NULL ||
 	    prev->offset + prev->end - prev->start == entry->offset)) {
@@ -4744,13 +4751,14 @@ uvm_map_printit(struct vm_map *map, bool
 		    (long long)entry->offset, entry->aref.ar_amap,
 		    entry->aref.ar_pageoff);
 		(*pr)(
-		    "\tsubmap=%c, cow=%c, nc=%c, prot(max)=%d/%d, inh=%d, "
+		    "\tsubmap=%c, cow=%c, nc=%c, prot(max,lim)=%d/%d/%d, inh=%d, "
 		    "wc=%d, adv=%d\n",
 		    (entry->etype & UVM_ET_SUBMAP) ? 'T' : 'F',
 		    (entry->etype & UVM_ET_COPYONWRITE) ? 'T' : 'F',
 		    (entry->etype & UVM_ET_NEEDSCOPY) ? 'T' : 'F',
 		    entry->protection, entry->max_protection,
-		    entry->inheritance, entry->wired_count, entry->advice);
+		    entry->lim_protection, entry->inheritance,
+		    entry->wired_count, entry->advice);
 	}
 }
 
Index: sys/uvm/uvm_map.h
===================================================================
RCS file: /home/joerg/repo/netbsd/src/sys/uvm/uvm_map.h,v
retrieving revision 1.73
diff -u -p -r1.73 uvm_map.h
--- sys/uvm/uvm_map.h	25 May 2016 17:43:58 -0000	1.73
+++ sys/uvm/uvm_map.h	24 Mar 2017 14:07:05 -0000
@@ -142,6 +142,7 @@ struct vm_map_entry {
 	int			etype;		/* entry type */
 	vm_prot_t		protection;	/* protection code */
 	vm_prot_t		max_protection;	/* maximum protection */
+	vm_prot_t		lim_protection;	/* limit for max_protection */
 	vm_inherit_t		inheritance;	/* inheritance */
 	int			wired_count;	/* can be paged if == 0 */
 	struct vm_aref		aref;		/* anonymous overlay */
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	24 Mar 2017 15:55:11 -0000
@@ -64,7 +64,7 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_mmap.c,v
 #include <uvm/uvm_device.h>
 
 static int uvm_mmap(struct vm_map *, vaddr_t *, vsize_t, vm_prot_t, vm_prot_t,
-    int, int, struct uvm_object *, voff_t, vsize_t);
+    vm_prot_t, int, int, struct uvm_object *, voff_t, vsize_t);
 
 static int
 range_test(struct vm_map *map, vaddr_t addr, vsize_t size, bool ismmap)
@@ -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, limprot;
 	int flags, fd, advice;
 	vaddr_t defaddr;
 	struct file *fp = NULL;
@@ -411,6 +411,8 @@ sys_mmap(struct lwp *l, const struct sys
 		pos = 0;
 	}
 
+	KASSERT((prot & maxprot) == prot);
+	limprot = prot = maxprot = prot & maxprot;
 	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
 
 	pax_aslr_mmap(l, &addr, orig_addr, flags);
@@ -420,7 +422,8 @@ sys_mmap(struct lwp *l, const struct sys
 	 */
 
 	error = uvm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, maxprot,
-	    flags, advice, uobj, pos, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
+	    limprot, flags, advice, uobj, pos,
+	    p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
 
 	/* remember to add offset */
 	*retval = (register_t)(addr + pageoff);
@@ -603,7 +606,7 @@ sys_mprotect(struct lwp *l, const struct
 	struct proc *p = l->l_proc;
 	vaddr_t addr;
 	vsize_t size, pageoff;
-	vm_prot_t prot;
+	vm_prot_t oprot, prot, maxprot;
 	int error;
 
 	/*
@@ -612,7 +615,10 @@ sys_mprotect(struct lwp *l, const struct
 
 	addr = (vaddr_t)SCARG(uap, addr);
 	size = (vsize_t)SCARG(uap, len);
-	prot = SCARG(uap, prot) & VM_PROT_ALL;
+	maxprot = prot = oprot = SCARG(uap, prot) & VM_PROT_ALL;
+	PAX_MPROTECT_ADJUST(l, &prot, &maxprot);
+	if (prot != oprot)
+		return EPERM;
 
 	/*
 	 * align the address to a page boundary and adjust the size accordingly.
@@ -628,7 +634,7 @@ sys_mprotect(struct lwp *l, const struct
 		return EINVAL;
 
 	error = uvm_map_protect(&p->p_vmspace->vm_map, addr, addr + size, prot,
-				false);
+				true);
 	return error;
 }
 
@@ -915,8 +921,8 @@ sys_munlockall(struct lwp *l, const void
 
 int
 uvm_mmap(struct vm_map *map, vaddr_t *addr, vsize_t size, vm_prot_t prot,
-    vm_prot_t maxprot, int flags, int advice, struct uvm_object *uobj,
-    voff_t foff, vsize_t locklimit)
+    vm_prot_t maxprot, vm_prot_t limprot, int flags, int advice,
+    struct uvm_object *uobj, voff_t foff, vsize_t locklimit)
 {
 	vaddr_t align = 0;
 	int error;
@@ -1002,7 +1008,7 @@ uvm_mmap(struct vm_map *map, vaddr_t *ad
 		}
 	}
 
-	uvmflag = UVM_MAPFLAG(prot, maxprot,
+	uvmflag = UVM_MAPFLAG(prot, maxprot, limprot,
 	    (flags & MAP_SHARED) ? UVM_INH_SHARE : UVM_INH_COPY, advice,
 	    uvmflag);
 	error = uvm_map(map, addr, size, uobj, foff, align, uvmflag);
@@ -1084,7 +1090,7 @@ uvm_mmap_dev(struct proc *p, void **addr
 		return EINVAL;
 
 	error = uvm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp,
-	    (vsize_t)len, prot, prot, flags, UVM_ADV_RANDOM, uobj, off,
+	    (vsize_t)len, prot, prot, prot, flags, UVM_ADV_RANDOM, uobj, off,
 	    p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
 	return error;
 }
@@ -1104,7 +1110,7 @@ uvm_mmap_anon(struct proc *p, void **add
 		    p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN);
 
 	error = uvm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp,
-	    (vsize_t)len, prot, prot, flags, UVM_ADV_NORMAL, NULL, 0,
+	    (vsize_t)len, prot, prot, prot, flags, UVM_ADV_NORMAL, NULL, 0,
 	    p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
 	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	24 Mar 2017 14:10:45 -0000
@@ -94,6 +94,7 @@ uvm_mapent_extend(struct vm_map *map, va
 	reserved_entry->flags &= ~UVM_MAP_NOMERGE;
 	reserved_entry->protection = entry->protection;
 	reserved_entry->max_protection = entry->max_protection;
+	reserved_entry->lim_protection = entry->lim_protection;
 	reserved_entry->inheritance = entry->inheritance;
 	reserved_entry->advice = entry->advice;
 	reserved_entry->wired_count = 0; /* XXX should inherit? */
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	24 Mar 2017 14:33:02 -0000
@@ -104,7 +104,7 @@ sys_obreak(struct lwp *l, const struct s
 
 		error = uvm_map(&vm->vm_map, &obreak, nbreak - obreak, NULL,
 		    UVM_UNKNOWN_OFFSET, 0,
-		    UVM_MAPFLAG(prot, maxprot,
+		    UVM_MAPFLAG(prot, maxprot, maxprot,
 				UVM_INH_COPY,
 				UVM_ADV_NORMAL, UVM_FLAG_AMAPPAD|UVM_FLAG_FIXED|
 				UVM_FLAG_OVERLAY|UVM_FLAG_COPYONW));
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	31 Mar 2017 12:59:25 -0000
@@ -171,7 +171,7 @@ 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_EXEC|PROT_WRITE|PROT_READ, MAP_ANON, -1, 0);
 	ATF_REQUIRE(map != MAP_FAILED);
 
 	memcpy(map, (void *)return_one,
@@ -312,6 +312,76 @@ 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_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 +392,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