aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2019-07-02 19:07:17 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2019-07-02 19:07:17 +0000
commit5dc7e31a098741399f171c1bd7be51124e75b12f (patch)
tree96703a93b51f5d48d8b713c1307d2ef5c6f4da62
parent3730695151008c2f83ee606d87f6561a597fd463 (diff)
downloadsrc-5dc7e31a098741399f171c1bd7be51124e75b12f.tar.gz
src-5dc7e31a098741399f171c1bd7be51124e75b12f.zip
Control implicit PROT_MAX() using procctl(2) and the FreeBSD note
feature bit. In particular, allocate the bit to opt-out the image from implicit PROTMAX enablement. Provide procctl(2) verbs to set and query implicit PROTMAX handling. The knobs mimic the same per-image flag and per-process controls for ASLR. Reviewed by: emaste, markj (previous version) Discussed with: brooks Sponsored by: The FreeBSD Foundation Differential revision: https://reviews.freebsd.org/D20795
Notes
Notes: svn path=/head/; revision=349609
-rw-r--r--lib/libc/sys/procctl.245
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c3
-rw-r--r--sys/kern/kern_procctl.c57
-rw-r--r--sys/sys/elf_common.h1
-rw-r--r--sys/sys/proc.h2
-rw-r--r--sys/sys/procctl.h7
-rw-r--r--sys/sys/syscallsubr.h1
-rw-r--r--sys/vm/vm_mmap.c19
8 files changed, 130 insertions, 5 deletions
diff --git a/lib/libc/sys/procctl.2 b/lib/libc/sys/procctl.2
index 598db5343b10..c0fce21b0b3d 100644
--- a/lib/libc/sys/procctl.2
+++ b/lib/libc/sys/procctl.2
@@ -94,7 +94,7 @@ Same notes as for
.Dv PROC_ASLR_FORCE_ENABLE
apply.
.It Dv PROC_ASLR_NOFORCE
-Use system-wide configured policy for ASLR.
+Use the system-wide configured policy for ASLR.
.El
.It Dv PROC_ASLR_STATUS
Returns the current status of ASLR enablement for the target process.
@@ -112,6 +112,47 @@ If the currently executed image in the process itself has ASLR enabled,
the
.Dv PROC_ASLR_ACTIVE
flag is or-ed with the value listed above.
+.It Dv PROC_PROTMAX_CTL
+Controls implicit application of PROT_MAX protection equal to the
+.Fa prot
+argument of the
+.Xr mmap 2
+syscall, in the target process.
+The
+.Va arg
+parameter must point to the integer variable holding one of the following
+values:
+.Bl -tag -width PROC_PROTMAX_FORCE_DISABLE
+.It Dv PROC_PROTMAX_FORCE_ENABLE
+Enables implicit PROT_MAX application,
+even if it is disabled system-wide by the sysctl
+.Va vm.imply_prot_max .
+The image flag might still prevent the enablement.
+.It Dv PROC_ASLR_FORCE_DISABLE
+Request that implicit application of PROT_MAX be disabled.
+Same notes as for
+.Dv PROC_PROTMAX_FORCE_ENABLE
+apply.
+.It Dv PROC_PROTMAX_NOFORCE
+Use the system-wide configured policy for PROT_MAX.
+.El
+.It Dv PROC_PROTMAX_STATUS
+Returns the current status of implicit PROT_MAX enablement for the
+target process.
+The
+.Va arg
+parameter must point to the integer variable, where one of the
+following values is written:
+.Bl -tag -width PROC_PROTMAX_FORCE_DISABLE
+.It Dv PROC_PROTMAX_FORCE_ENABLE
+.It Dv PROC_PROTMAX_FORCE_DISABLE
+.It Dv PROC_PROTMAX_NOFORCE
+.El
+.Pp
+If the currently executed image in the process itself has implicit PROT_MAX
+application enabled, the
+.Dv PROC_PROTMAX_ACTIVE
+flag is or-ed with the value listed above.
.It Dv PROC_SPROTECT
Set process protection state.
This is used to mark a process as protected from being killed if the system
@@ -575,6 +616,8 @@ or invalid signal number.
.Xr cap_enter 2,
.Xr kill 2 ,
.Xr ktrace 2 ,
+.Xr mmap 2 ,
+.Xr mprotect 2 ,
.Xr ptrace 2 ,
.Xr wait 2 ,
.Xr capsicum 4 ,
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 1e43d25d26c9..ce73635a94d5 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -3333,6 +3333,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
switch (uap->com) {
case PROC_ASLR_CTL:
+ case PROC_PROTMAX_CTL:
case PROC_SPROTECT:
case PROC_TRACE_CTL:
case PROC_TRAPCAP_CTL:
@@ -3365,6 +3366,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
data = &x.rk;
break;
case PROC_ASLR_STATUS:
+ case PROC_PROTMAX_STATUS:
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
data = &flags;
@@ -3394,6 +3396,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
error = error1;
break;
case PROC_ASLR_STATUS:
+ case PROC_PROTMAX_STATUS:
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
if (error == 0)
diff --git a/sys/kern/kern_procctl.c b/sys/kern/kern_procctl.c
index 4e9f200aea79..54dbeafb0f60 100644
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/capsicum.h>
#include <sys/lock.h>
+#include <sys/mman.h>
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
@@ -419,6 +420,51 @@ trapcap_status(struct thread *td, struct proc *p, int *data)
}
static int
+protmax_ctl(struct thread *td, struct proc *p, int state)
+{
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+
+ switch (state) {
+ case PROC_PROTMAX_FORCE_ENABLE:
+ p->p_flag2 &= ~P2_PROTMAX_DISABLE;
+ p->p_flag2 |= P2_PROTMAX_ENABLE;
+ break;
+ case PROC_PROTMAX_FORCE_DISABLE:
+ p->p_flag2 |= P2_PROTMAX_DISABLE;
+ p->p_flag2 &= ~P2_PROTMAX_ENABLE;
+ break;
+ case PROC_PROTMAX_NOFORCE:
+ p->p_flag2 &= ~(P2_PROTMAX_ENABLE | P2_PROTMAX_DISABLE);
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+protmax_status(struct thread *td, struct proc *p, int *data)
+{
+ int d;
+
+ switch (p->p_flag2 & (P2_PROTMAX_ENABLE | P2_PROTMAX_DISABLE)) {
+ case 0:
+ d = PROC_ASLR_NOFORCE;
+ break;
+ case P2_PROTMAX_ENABLE:
+ d = PROC_PROTMAX_FORCE_ENABLE;
+ break;
+ case P2_PROTMAX_DISABLE:
+ d = PROC_PROTMAX_FORCE_DISABLE;
+ break;
+ }
+ if (kern_mmap_maxprot(p, PROT_READ) == PROT_READ)
+ d |= PROC_PROTMAX_ACTIVE;
+ *data = d;
+ return (0);
+}
+
+static int
aslr_ctl(struct thread *td, struct proc *p, int state)
{
@@ -500,6 +546,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
switch (uap->com) {
case PROC_ASLR_CTL:
+ case PROC_PROTMAX_CTL:
case PROC_SPROTECT:
case PROC_TRACE_CTL:
case PROC_TRAPCAP_CTL:
@@ -530,6 +577,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
data = &x.rk;
break;
case PROC_ASLR_STATUS:
+ case PROC_PROTMAX_STATUS:
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
data = &flags;
@@ -558,6 +606,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
error = error1;
break;
case PROC_ASLR_STATUS:
+ case PROC_PROTMAX_STATUS:
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
if (error == 0)
@@ -583,6 +632,10 @@ kern_procctl_single(struct thread *td, struct proc *p, int com, void *data)
return (aslr_status(td, p, data));
case PROC_SPROTECT:
return (protect_set(td, p, *(int *)data));
+ case PROC_PROTMAX_CTL:
+ return (protmax_ctl(td, p, *(int *)data));
+ case PROC_PROTMAX_STATUS:
+ return (protmax_status(td, p, data));
case PROC_REAP_ACQUIRE:
return (reap_acquire(td, p));
case PROC_REAP_RELEASE:
@@ -618,6 +671,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
switch (com) {
case PROC_ASLR_CTL:
case PROC_ASLR_STATUS:
+ case PROC_PROTMAX_CTL:
+ case PROC_PROTMAX_STATUS:
case PROC_REAP_ACQUIRE:
case PROC_REAP_RELEASE:
case PROC_REAP_STATUS:
@@ -669,6 +724,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
break;
case PROC_ASLR_CTL:
case PROC_ASLR_STATUS:
+ case PROC_PROTMAX_CTL:
+ case PROC_PROTMAX_STATUS:
case PROC_TRACE_STATUS:
case PROC_TRAPCAP_STATUS:
tree_locked = false;
diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h
index 538ae4eb2db3..f7484bd41c1a 100644
--- a/sys/sys/elf_common.h
+++ b/sys/sys/elf_common.h
@@ -777,6 +777,7 @@ typedef struct {
/* NT_FREEBSD_FEATURE_CTL desc[0] bits */
#define NT_FREEBSD_FCTL_ASLR_DISABLE 0x00000001
+#define NT_FREEBSD_FCTL_PROTMAX_DISABLE 0x00000002
/* Values for n_type. Used in core files. */
#define NT_PRSTATUS 1 /* Process status. */
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index a257de430ba7..857bb5234d12 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -761,6 +761,8 @@ struct proc {
#define P2_ASLR_ENABLE 0x00000040 /* Force enable ASLR. */
#define P2_ASLR_DISABLE 0x00000080 /* Force disable ASLR. */
#define P2_ASLR_IGNSTART 0x00000100 /* Enable ASLR to consume sbrk area. */
+#define P2_PROTMAX_ENABLE 0x00000200 /* Force enable implied PROT_MAX. */
+#define P2_PROTMAX_DISABLE 0x00000400 /* Force disable implied PROT_MAX. */
/* 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 80679f5eb972..f80dc023af19 100644
--- a/sys/sys/procctl.h
+++ b/sys/sys/procctl.h
@@ -59,6 +59,8 @@
#define PROC_PDEATHSIG_STATUS 12 /* get parent death signal */
#define PROC_ASLR_CTL 13 /* en/dis ASLR */
#define PROC_ASLR_STATUS 14 /* query ASLR status */
+#define PROC_PROTMAX_CTL 15 /* en/dis implicit PROT_MAX */
+#define PROC_PROTMAX_STATUS 16 /* query implicit PROT_MAX status */
/* Operations for PROC_SPROTECT (passed in integer arg). */
#define PPROT_OP(x) ((x) & 0xf)
@@ -127,6 +129,11 @@ struct procctl_reaper_kill {
#define PROC_ASLR_NOFORCE 3
#define PROC_ASLR_ACTIVE 0x80000000
+#define PROC_PROTMAX_FORCE_ENABLE 1
+#define PROC_PROTMAX_FORCE_DISABLE 2
+#define PROC_PROTMAX_NOFORCE 3
+#define PROC_PROTMAX_ACTIVE 0x80000000
+
#ifndef _KERNEL
__BEGIN_DECLS
int procctl(idtype_t, id_t, int, void *);
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index 29bd01de841e..0c3ec79071d8 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -175,6 +175,7 @@ int kern_mlock(struct proc *proc, struct ucred *cred, uintptr_t addr,
size_t len);
int kern_mmap(struct thread *td, uintptr_t addr, size_t len, int prot,
int flags, int fd, off_t pos);
+int kern_mmap_maxprot(struct proc *p, int prot);
int kern_mprotect(struct thread *td, uintptr_t addr, size_t size, int prot);
int kern_msgctl(struct thread *, int, int, struct msqid_ds *);
int kern_msgrcv(struct thread *, int, void *, size_t, long, int, long *);
diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c
index be06a5cb5699..31941b09e86c 100644
--- a/sys/vm/vm_mmap.c
+++ b/sys/vm/vm_mmap.c
@@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysproto.h>
+#include <sys/elf.h>
#include <sys/filedesc.h>
#include <sys/priv.h>
#include <sys/proc.h>
@@ -182,6 +183,19 @@ sys_mmap(struct thread *td, struct mmap_args *uap)
}
int
+kern_mmap_maxprot(struct proc *p, int prot)
+{
+
+ if ((p->p_flag2 & P2_PROTMAX_DISABLE) != 0 ||
+ (p->p_fctl0 & NT_FREEBSD_FCTL_PROTMAX_DISABLE) != 0)
+ return (_PROT_ALL);
+ if (((p->p_flag2 & P2_PROTMAX_ENABLE) != 0 || imply_prot_max) &&
+ prot != PROT_NONE)
+ return (prot);
+ return (_PROT_ALL);
+}
+
+int
kern_mmap(struct thread *td, uintptr_t addr0, size_t len, int prot, int flags,
int fd, off_t pos)
{
@@ -206,12 +220,9 @@ kern_mmap(struct thread *td, uintptr_t addr0, size_t len, int prot, int flags,
/*
* Always honor PROT_MAX if set. If not, default to all
* permissions unless we're implying maximum permissions.
- *
- * XXX: should be tunable per process and ABI.
*/
if (max_prot == 0)
- max_prot = (imply_prot_max && prot != PROT_NONE) ?
- prot : _PROT_ALL;
+ max_prot = kern_mmap_maxprot(p, prot);
vms = p->p_vmspace;
fp = NULL;