Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 24 Sep 2021 00:40:06 GMT
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: d4a478c2703d - stable/13 - procctl(2): Add PROC_WXMAP_CTL/STATUS
Message-ID:  <202109240040.18O0e6Bu051408@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=d4a478c2703d71b7cbbfc4e7d281b96dde2497d6

commit d4a478c2703d71b7cbbfc4e7d281b96dde2497d6
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2021-09-02 00:59:10 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2021-09-24 00:26:59 +0000

    procctl(2): Add PROC_WXMAP_CTL/STATUS
    
    (cherry picked from commit 796a8e1ad1ae3f7b8e4c9f97bebbef5d7d5a2c16)
---
 lib/libc/sys/procctl.2                | 64 ++++++++++++++++++++++++++++-
 sys/compat/freebsd32/freebsd32_misc.c |  3 ++
 sys/kern/imgact_elf.c                 |  7 +++-
 sys/kern/kern_fork.c                  |  3 +-
 sys/kern/kern_procctl.c               | 76 +++++++++++++++++++++++++++++++++++
 sys/sys/proc.h                        |  2 +
 sys/sys/procctl.h                     |  6 +++
 7 files changed, 157 insertions(+), 4 deletions(-)

diff --git a/lib/libc/sys/procctl.2 b/lib/libc/sys/procctl.2
index 432ed5919a81..4308520b8974 100644
--- a/lib/libc/sys/procctl.2
+++ b/lib/libc/sys/procctl.2
@@ -29,7 +29,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd July 1, 2021
+.Dd September 2, 2021
 .Dt PROCCTL 2
 .Os
 .Sh NAME
@@ -589,6 +589,62 @@ following values is written:
 .It Dv PROC_NO_NEW_PRIVS_ENABLE
 .It Dv PROC_NO_NEW_PRIVS_DISABLE
 .El
+.It Dv PROC_WXMAP_CTL
+Controls the 'write exclusive against execution' permissions for the
+mappings in the process address space.
+It overrides the global settings established by the
+.Dv kern.elf{32/64}.allow_wx
+sysctl,
+and the corresponding bit in the ELF control note, see
+.Xr elfctl 1 .
+.Pp
+The
+.Fa data
+parameter must point to the integer variable holding one of the
+following values:
+.Bl -tag -width PROC_WX_MAPPINGS_DISALLOW_EXEC
+.It Dv PROC_WX_MAPPINGS_PERMIT
+Enable creation of mappings that have both write and execute
+protection attributes, in the specified process' address space.
+.It Dv PROC_WX_MAPPINGS_DISALLOW_EXEC
+In the new address space created by
+.Xr execve 2 ,
+disallow creation of mappings that have both write and execute
+permissions.
+.El
+.Pp
+Once creation of writeable and executable mappings is allowed,
+it is impossible (and pointless) to disallow it.
+The only way to ensure the absence of such mappings after they
+were enabled in a given process, is to set the
+.Dv PROC_WX_MAPPINGS_DISALLOW_EXEC
+flag and
+.Xr execve 2
+an image.
+.It Dv PROC_WXMAP_STATUS
+Returns the current status of the 'write exclusive against execution'
+enforcement for the specified process.
+The
+.Dv data
+parameter must point to the integer variable, where one of the
+following values is written:
+.Bl -tag -width PROC_WX_MAPPINGS_DISALLOW_EXEC
+.It Dv PROC_WX_MAPPINGS_PERMIT
+Creation of simultaneously writable and executable mapping is permitted,
+otherwise the process cannot create such mappings.
+.It Dv PROC_WX_MAPPINGS_DISALLOW_EXEC
+After
+.Xr execve 2 ,
+the new address space should disallow creation of simultaneously
+writable and executable mappings.
+.El
+.Pp
+Additionally, if the address space of the process disallows
+creation of simultaneously writable and executable mappings and
+it is guaranteed that no such mapping was created since address space
+creation, the
+.Dv PROC_WXORX_ENFORCE
+flag is set in the returned value.
 .El
 .Sh x86 MACHINE-SPECIFIC REQUESTS
 .Bl -tag -width PROC_KPTI_STATUS
@@ -638,6 +694,12 @@ feature, as it is bypassable both by the kernel and privileged processes,
 and via other system mechanisms.
 As such, it should not be utilized to reliably protect cryptographic
 keying material or other confidential data.
+.Pp
+Note that processes can trivially bypass the 'no simultaneously
+writable and executable mappings' policy by first marking some mapping
+as writeable and write code to it, then removing write and adding
+execute permission.
+This may be legitimately required by some programs, such as JIT compilers.
 .Sh RETURN VALUES
 If an error occurs, a value of -1 is returned and
 .Va errno
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index cefae53b69df..c5bf67a72afd 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -3632,6 +3632,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_TRACE_CTL:
 	case PROC_TRAPCAP_CTL:
 	case PROC_NO_NEW_PRIVS_CTL:
+	case PROC_WXMAP_CTL:
 		error = copyin(PTRIN(uap->data), &flags, sizeof(flags));
 		if (error != 0)
 			return (error);
@@ -3666,6 +3667,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
 	case PROC_NO_NEW_PRIVS_STATUS:
+	case PROC_WXMAP_STATUS:
 		data = &flags;
 		break;
 	case PROC_PDEATHSIG_CTL:
@@ -3698,6 +3700,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
 	case PROC_NO_NEW_PRIVS_STATUS:
+	case PROC_WXMAP_STATUS:
 		if (error == 0)
 			error = copyout(&flags, uap->data, sizeof(flags));
 		break;
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index 9d039b914fb5..057c583324bb 100644
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -1219,7 +1219,8 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
 	 */
 	if (imgp->credential_setid) {
 		PROC_LOCK(imgp->proc);
-		imgp->proc->p_flag2 &= ~(P2_ASLR_ENABLE | P2_ASLR_DISABLE);
+		imgp->proc->p_flag2 &= ~(P2_ASLR_ENABLE | P2_ASLR_DISABLE |
+		    P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC);
 		PROC_UNLOCK(imgp->proc);
 	}
 	if ((sv->sv_flags & SV_ASLR) == 0 ||
@@ -1242,7 +1243,9 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
 			imgp->map_flags |= MAP_ASLR_IGNSTART;
 	}
 
-	if (!__elfN(allow_wx) && (fctl0 & NT_FREEBSD_FCTL_WXNEEDED) == 0)
+	if ((!__elfN(allow_wx) && (fctl0 & NT_FREEBSD_FCTL_WXNEEDED) == 0 &&
+	    (imgp->proc->p_flag2 & P2_WXORX_DISABLE) == 0) ||
+	    (imgp->proc->p_flag2 & P2_WXORX_ENABLE_EXEC) != 0)
 		imgp->map_flags |= MAP_WXORX;
 
 	error = exec_new_vmspace(imgp, sv);
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 7a80f7de85d8..b2d58e804123 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -492,7 +492,8 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
 	p2->p_flag2 = p1->p_flag2 & (P2_ASLR_DISABLE | P2_ASLR_ENABLE |
 	    P2_ASLR_IGNSTART | P2_NOTRACE | P2_NOTRACE_EXEC |
 	    P2_PROTMAX_ENABLE | P2_PROTMAX_DISABLE | P2_TRAPCAP |
-	    P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC | P2_NO_NEW_PRIVS);
+	    P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC | P2_NO_NEW_PRIVS |
+	    P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC);
 	p2->p_swtick = ticks;
 	if (p1->p_flag & P_PROFIL)
 		startprofclock(p2);
diff --git a/sys/kern/kern_procctl.c b/sys/kern/kern_procctl.c
index 68fa4bc0c3ac..53626caa0fd9 100644
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -591,6 +591,71 @@ stackgap_status(struct thread *td, struct proc *p, int *data)
 	return (0);
 }
 
+static int
+wxmap_ctl(struct thread *td, struct proc *p, int state)
+{
+	struct vmspace *vm;
+	vm_map_t map;
+
+	PROC_LOCK_ASSERT(p, MA_OWNED);
+	if ((p->p_flag & P_WEXIT) != 0)
+		return (ESRCH);
+
+	switch (state) {
+	case PROC_WX_MAPPINGS_PERMIT:
+		p->p_flag2 |= P2_WXORX_DISABLE;
+		_PHOLD(p);
+		PROC_UNLOCK(p);
+		vm = vmspace_acquire_ref(p);
+		if (vm != NULL) {
+			map = &vm->vm_map;
+			vm_map_lock(map);
+			map->flags &= ~MAP_WXORX;
+			vm_map_unlock(map);
+			vmspace_free(vm);
+		}
+		PROC_LOCK(p);
+		_PRELE(p);
+		break;
+	case PROC_WX_MAPPINGS_DISALLOW_EXEC:
+		p->p_flag2 |= P2_WXORX_ENABLE_EXEC;
+		break;
+	default:
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+static int
+wxmap_status(struct thread *td, struct proc *p, int *data)
+{
+	struct vmspace *vm;
+	int d;
+
+	PROC_LOCK_ASSERT(p, MA_OWNED);
+	if ((p->p_flag & P_WEXIT) != 0)
+		return (ESRCH);
+
+	d = 0;
+	if ((p->p_flag2 & P2_WXORX_DISABLE) != 0)
+		d |= PROC_WX_MAPPINGS_PERMIT;
+	if ((p->p_flag2 & P2_WXORX_ENABLE_EXEC) != 0)
+		d |= PROC_WX_MAPPINGS_DISALLOW_EXEC;
+	_PHOLD(p);
+	PROC_UNLOCK(p);
+	vm = vmspace_acquire_ref(p);
+	if (vm != NULL) {
+		if ((vm->vm_map.flags & MAP_WXORX) != 0)
+			d |= PROC_WXORX_ENFORCE;
+		vmspace_free(vm);
+	}
+	PROC_LOCK(p);
+	_PRELE(p);
+	*data = d;
+	return (0);
+}
+
 #ifndef _SYS_SYSPROTO_H_
 struct procctl_args {
 	idtype_t idtype;
@@ -623,6 +688,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
 	case PROC_TRACE_CTL:
 	case PROC_TRAPCAP_CTL:
 	case PROC_NO_NEW_PRIVS_CTL:
+	case PROC_WXMAP_CTL:
 		error = copyin(uap->data, &flags, sizeof(flags));
 		if (error != 0)
 			return (error);
@@ -655,6 +721,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
 	case PROC_NO_NEW_PRIVS_STATUS:
+	case PROC_WXMAP_STATUS:
 		data = &flags;
 		break;
 	case PROC_PDEATHSIG_CTL:
@@ -686,6 +753,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
 	case PROC_NO_NEW_PRIVS_STATUS:
+	case PROC_WXMAP_STATUS:
 		if (error == 0)
 			error = copyout(&flags, uap->data, sizeof(flags));
 		break;
@@ -739,6 +807,10 @@ kern_procctl_single(struct thread *td, struct proc *p, int com, void *data)
 		return (no_new_privs_ctl(td, p, *(int *)data));
 	case PROC_NO_NEW_PRIVS_STATUS:
 		return (no_new_privs_status(td, p, data));
+	case PROC_WXMAP_CTL:
+		return (wxmap_ctl(td, p, *(int *)data));
+	case PROC_WXMAP_STATUS:
+		return (wxmap_status(td, p, data));
 	default:
 		return (EINVAL);
 	}
@@ -771,6 +843,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
 	case PROC_PDEATHSIG_STATUS:
 	case PROC_NO_NEW_PRIVS_CTL:
 	case PROC_NO_NEW_PRIVS_STATUS:
+	case PROC_WXMAP_CTL:
+	case PROC_WXMAP_STATUS:
 		if (idtype != P_PID)
 			return (EINVAL);
 	}
@@ -821,6 +895,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
 	case PROC_NO_NEW_PRIVS_STATUS:
+	case PROC_WXMAP_CTL:
+	case PROC_WXMAP_STATUS:
 		tree_locked = false;
 		break;
 	default:
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 15e175bf5aad..ffb723576e2e 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -825,6 +825,8 @@ struct proc {
 #define	P2_ITSTOPPED		0x00002000
 #define	P2_PTRACEREQ		0x00004000	/* Active ptrace req */
 #define	P2_NO_NEW_PRIVS		0x00008000	/* Ignore setuid */
+#define	P2_WXORX_DISABLE	0x00010000	/* WX mappings enabled */
+#define	P2_WXORX_ENABLE_EXEC	0x00020000	/* WXORX enabled after exec */
 
 /* Flags protected by proctree_lock, kept in p_treeflags. */
 #define	P_TREE_ORPHANED		0x00000001	/* Reparented, on orphan list */
diff --git a/sys/sys/procctl.h b/sys/sys/procctl.h
index cc0279fb0d08..0fcb62e94bb9 100644
--- a/sys/sys/procctl.h
+++ b/sys/sys/procctl.h
@@ -65,6 +65,8 @@
 #define	PROC_STACKGAP_STATUS	18	/* query stack gap */
 #define	PROC_NO_NEW_PRIVS_CTL	19	/* disable setuid/setgid */
 #define	PROC_NO_NEW_PRIVS_STATUS 20	/* query suid/sgid disabled status */
+#define	PROC_WXMAP_CTL		21	/* control W^X */
+#define	PROC_WXMAP_STATUS	22	/* query W^X */
 
 /* Operations for PROC_SPROTECT (passed in integer arg). */
 #define	PPROT_OP(x)	((x) & 0xf)
@@ -146,6 +148,10 @@ struct procctl_reaper_kill {
 #define	PROC_NO_NEW_PRIVS_ENABLE	1
 #define	PROC_NO_NEW_PRIVS_DISABLE	2
 
+#define	PROC_WX_MAPPINGS_PERMIT		0x0001
+#define	PROC_WX_MAPPINGS_DISALLOW_EXEC	0x0002
+#define	PROC_WXORX_ENFORCE		0x80000000
+
 #ifndef _KERNEL
 __BEGIN_DECLS
 int	procctl(idtype_t, id_t, int, void *);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202109240040.18O0e6Bu051408>