Page MenuHomeFreeBSD

D31779.id94568.diff
No OneTemporary

D31779.id94568.diff

diff --git a/lib/libc/sys/procctl.2 b/lib/libc/sys/procctl.2
--- 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
@@ -599,6 +599,62 @@
.It Dv PROC_NO_NEW_PRIVS_ENABLE
.It Dv PROC_NO_NEW_PRIVS_DISABLE
.El
+.It Dv PROC_WXORX_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 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_WXORX_ENABLE_ON_EXEC
+.It Dv PROC_WXORX_DISABLE
+Enable creation of mappings that have both write and execute
+protection attributes, in the specified process' address space.
+.It Dv PROC_WXORX_ENABLE_ON_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_WXORX_ENABLE_ON_EXEC
+flag and
+.Xr execve 2
+an image.
+.It Dv PROC_WXORX_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_WXORX_ENABLE_ON_EXEC
+.It Dv PROC_WXORX_DISABLE
+Creation of simultaneously writable and executable mapping is permitted,
+otherwise the process cannot create such mappings.
+.It Dv PROC_WXORX_ENABLE_ON_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
@@ -648,6 +704,12 @@
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
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -3643,6 +3643,7 @@
case PROC_TRACE_CTL:
case PROC_TRAPCAP_CTL:
case PROC_NO_NEW_PRIVS_CTL:
+ case PROC_WXORX_CTL:
error = copyin(PTRIN(uap->data), &flags, sizeof(flags));
if (error != 0)
return (error);
@@ -3677,6 +3678,7 @@
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
case PROC_NO_NEW_PRIVS_STATUS:
+ case PROC_WXORX_STATUS:
data = &flags;
break;
case PROC_PDEATHSIG_CTL:
@@ -3709,6 +3711,7 @@
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
case PROC_NO_NEW_PRIVS_STATUS:
+ case PROC_WXORX_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
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -1216,7 +1216,8 @@
*/
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 ||
@@ -1239,7 +1240,9 @@
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
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -493,7 +493,8 @@
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
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -591,6 +591,71 @@
return (0);
}
+static int
+wxorx_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_WXORX_DISABLE:
+ 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_WXORX_ENABLE_ON_EXEC:
+ p->p_flag2 |= P2_WXORX_ENABLE_EXEC;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+wxorx_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_WXORX_DISABLE;
+ if ((p->p_flag2 & P2_WXORX_ENABLE_EXEC) != 0)
+ d |= PROC_WXORX_ENABLE_ON_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 @@
case PROC_TRACE_CTL:
case PROC_TRAPCAP_CTL:
case PROC_NO_NEW_PRIVS_CTL:
+ case PROC_WXORX_CTL:
error = copyin(uap->data, &flags, sizeof(flags));
if (error != 0)
return (error);
@@ -655,6 +721,7 @@
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
case PROC_NO_NEW_PRIVS_STATUS:
+ case PROC_WXORX_STATUS:
data = &flags;
break;
case PROC_PDEATHSIG_CTL:
@@ -686,6 +753,7 @@
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
case PROC_NO_NEW_PRIVS_STATUS:
+ case PROC_WXORX_STATUS:
if (error == 0)
error = copyout(&flags, uap->data, sizeof(flags));
break;
@@ -739,6 +807,10 @@
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_WXORX_CTL:
+ return (wxorx_ctl(td, p, *(int *)data));
+ case PROC_WXORX_STATUS:
+ return (wxorx_status(td, p, data));
default:
return (EINVAL);
}
@@ -771,6 +843,8 @@
case PROC_PDEATHSIG_STATUS:
case PROC_NO_NEW_PRIVS_CTL:
case PROC_NO_NEW_PRIVS_STATUS:
+ case PROC_WXORX_CTL:
+ case PROC_WXORX_STATUS:
if (idtype != P_PID)
return (EINVAL);
}
@@ -821,6 +895,8 @@
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
case PROC_NO_NEW_PRIVS_STATUS:
+ case PROC_WXORX_CTL:
+ case PROC_WXORX_STATUS:
tree_locked = false;
break;
default:
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -838,6 +838,8 @@
#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
--- 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_WXORX_CTL 21 /* control W^X */
+#define PROC_WXORX_STATUS 22 /* query W^X */
/* Operations for PROC_SPROTECT (passed in integer arg). */
#define PPROT_OP(x) ((x) & 0xf)
@@ -146,6 +148,10 @@
#define PROC_NO_NEW_PRIVS_ENABLE 1
#define PROC_NO_NEW_PRIVS_DISABLE 2
+#define PROC_WXORX_DISABLE 0x0001
+#define PROC_WXORX_ENABLE_ON_EXEC 0x0002
+#define PROC_WXORX_ENFORCE 0x80000000
+
#ifndef _KERNEL
__BEGIN_DECLS
int procctl(idtype_t, id_t, int, void *);
diff --git a/usr.bin/proccontrol/proccontrol.1 b/usr.bin/proccontrol/proccontrol.1
--- a/usr.bin/proccontrol/proccontrol.1
+++ b/usr.bin/proccontrol/proccontrol.1
@@ -28,7 +28,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd July 2, 2021
+.Dd September 2, 2021
.Dt PROCCONTROL 1
.Os
.Sh NAME
@@ -72,6 +72,8 @@
.It Ar nonewprivs
Controls disabling the setuid and sgid bits for
.Xr execve 2 .
+.It Ar wxorx
+Controls the write exclusive execute mode for mappings.
.It Ar kpti
Controls the KPTI enable, AMD64 only.
.It Ar la48
diff --git a/usr.bin/proccontrol/proccontrol.c b/usr.bin/proccontrol/proccontrol.c
--- a/usr.bin/proccontrol/proccontrol.c
+++ b/usr.bin/proccontrol/proccontrol.c
@@ -46,6 +46,7 @@
MODE_PROTMAX,
MODE_STACKGAP,
MODE_NO_NEW_PRIVS,
+ MODE_WXORX,
#ifdef PROC_KPTI_CTL
MODE_KPTI,
#endif
@@ -85,7 +86,7 @@
{
fprintf(stderr, "Usage: proccontrol -m (aslr|protmax|trace|trapcap|"
- "stackgap|nonewprivs"KPTI_USAGE LA_USAGE") [-q] "
+ "stackgap|nonewprivs|wxorx"KPTI_USAGE LA_USAGE") [-q] "
"[-s (enable|disable)] [-p pid | command]\n");
exit(1);
}
@@ -116,6 +117,8 @@
mode = MODE_STACKGAP;
else if (strcmp(optarg, "nonewprivs") == 0)
mode = MODE_NO_NEW_PRIVS;
+ else if (strcmp(optarg, "wxorx") == 0)
+ mode = MODE_WXORX;
#ifdef PROC_KPTI_CTL
else if (strcmp(optarg, "kpti") == 0)
mode = MODE_KPTI;
@@ -178,7 +181,11 @@
error = procctl(P_PID, pid, PROC_STACKGAP_STATUS, &arg);
break;
case MODE_NO_NEW_PRIVS:
- error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_STATUS, &arg);
+ error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_STATUS,
+ &arg);
+ break;
+ case MODE_WXORX:
+ error = procctl(P_PID, pid, PROC_WXORX_STATUS, &arg);
break;
#ifdef PROC_KPTI_CTL
case MODE_KPTI:
@@ -280,6 +287,17 @@
break;
}
break;
+ case MODE_WXORX:
+ if ((arg & PROC_WXORX_DISABLE) != 0)
+ printf("disabled");
+ else
+ printf("enabled");
+ if ((arg & PROC_WXORX_ENABLE_ON_EXEC) != 0)
+ printf(", enabled on exec");
+ if ((arg & PROC_WXORX_ENFORCE) != 0)
+ printf(", enforced");
+ printf("\n");
+ break;
#ifdef PROC_KPTI_CTL
case MODE_KPTI:
switch (arg & ~PROC_KPTI_STATUS_ACTIVE) {
@@ -349,7 +367,13 @@
case MODE_NO_NEW_PRIVS:
arg = enable ? PROC_NO_NEW_PRIVS_ENABLE :
PROC_NO_NEW_PRIVS_DISABLE;
- error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_CTL, &arg);
+ error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_CTL,
+ &arg);
+ break;
+ case MODE_WXORX:
+ arg = enable ? PROC_WXORX_ENABLE_ON_EXEC :
+ PROC_WXORX_DISABLE;
+ error = procctl(P_PID, pid, PROC_WXORX_CTL, &arg);
break;
#ifdef PROC_KPTI_CTL
case MODE_KPTI:

File Metadata

Mime Type
text/plain
Expires
Thu, Apr 2, 3:12 PM (8 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30727127
Default Alt Text
D31779.id94568.diff (11 KB)

Event Timeline