aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libc/sys/procctl.279
-rw-r--r--sys/compat/freebsd32/freebsd32_misc.c8
-rw-r--r--sys/kern/kern_exec.c2
-rw-r--r--sys/kern/kern_fork.c2
-rw-r--r--sys/kern/kern_procctl.c79
-rw-r--r--sys/kern/kern_prot.c7
-rw-r--r--sys/kern/kern_sig.c3
-rw-r--r--sys/sys/priv.h1
-rw-r--r--sys/sys/proc.h2
-rw-r--r--sys/sys/procctl.h6
10 files changed, 185 insertions, 4 deletions
diff --git a/lib/libc/sys/procctl.2 b/lib/libc/sys/procctl.2
index 649e0ad8a0d7..2c77901f1863 100644
--- a/lib/libc/sys/procctl.2
+++ b/lib/libc/sys/procctl.2
@@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd December 16, 2014
+.Dd December 29, 2014
.Dt PROCCTL 2
.Os
.Sh NAME
@@ -275,7 +275,61 @@ delivery failed, e.g. due to the permission problems.
If no such process exist, the
.Fa rk_fpid
field is set to -1.
+.It Dv PROC_TRACE_CTL
+Enable or disable tracing of the specified process(es), according to the
+value of the integer argument.
+Tracing includes attachment to the process using
+.Xr ptrace 2
+and
+.Xr ktrace 2 ,
+debugging sysctls,
+.Xr hwpmc 4 ,
+.Xr dtrace 1
+and core dumping.
+Possible values for the
+.Fa data
+argument are:
+.Bl -tag -width "Dv PROC_TRACE_CTL_DISABLE_EXEC"
+.It Dv PROC_TRACE_CTL_ENABLE
+Enable tracing, after it was disabled by
+.Dv PROC_TRACE_CTL_DISABLE .
+Only allowed for self.
+.It Dv PROC_TRACE_CTL_DISABLE
+Disable tracing for the specified process.
+Tracing is re-enabled when the process changes the executing
+program with
+.Xr execve 2
+syscall.
+A child inherits the trace settings from the parent on
+.Xr fork 2 .
+.It Dv PROC_TRACE_CTL_DISABLE_EXEC
+Same as
+.Dv PROC_TRACE_CTL_DISABLE ,
+but the setting persist for the process even after
+.Xr execve 2 .
+.El
+.It Dv PROC_TRACE_STATUS
+Returns the current tracing status for the specified process in
+the integer variable pointed to by
+.Fa data .
+If tracing is disabled,
+.Fa data
+is set to -1.
+If tracing is enabled, but no debugger is attached by
+.Xr ptrace 2
+syscall,
+.Fa data
+is set to 0.
+If a debugger is attached,
+.Fa data
+is set to the pid of the debugger process.
.El
+.Sh NOTES
+Disabling tracing on a process should not be considered a security
+feature, as it is bypassable both by the kernel and privileged processes,
+and via other system mechanisms.
+As such, it should not be relied on to reliably protect cryptographic
+keying material or other confidential data.
.Sh RETURN VALUES
If an error occurs, a value of -1 is returned and
.Va errno
@@ -343,11 +397,34 @@ The
.Dv PROC_REAP_ACQUIRE
request was issued by a process that had already acquired reaper status
and has not yet released it.
+.It Bq Er EBUSY
+The
+.Dv PROC_TRACE_CTL
+request was issued for a process already being traced.
+.It Bq Er EPERM
+The
+.Dv PROC_TRACE_CTL
+request to re-enable tracing of the process (
+.Dv PROC_TRACE_CTL_ENABLE ) ,
+or to disable persistence of the
+.Dv PROC_TRACE_CTL_DISABLE
+on
+.Xr execve 2
+was issued for a non-current process.
+.It Bq Er EINVAL
+The value of the integer
+.Fa data
+parameter for the
+.Dv PROC_TRACE_CTL
+request is invalid.
.El
.Sh SEE ALSO
+.Xr dtrace 1 ,
.Xr kill 2 ,
+.Xr ktrace 2 ,
.Xr ptrace 2 ,
.Xr wait 2 ,
+.Xr hwpmc 4 ,
.Xr init 8
.Sh HISTORY
The
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index ba8c2ff7ec5b..ffea28371043 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -2969,6 +2969,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
switch (uap->com) {
case PROC_SPROTECT:
+ case PROC_TRACE_CTL:
error = copyin(PTRIN(uap->data), &flags, sizeof(flags));
if (error != 0)
return (error);
@@ -2997,6 +2998,9 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
return (error);
data = &x.rk;
break;
+ case PROC_TRACE_STATUS:
+ data = &flags;
+ break;
default:
return (EINVAL);
}
@@ -3012,6 +3016,10 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
if (error == 0)
error = error1;
break;
+ case PROC_TRACE_STATUS:
+ if (error == 0)
+ error = copyout(&flags, uap->data, sizeof(flags));
+ break;
}
return (error);
}
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 19c33b62acc4..5b80f5c6a147 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -634,6 +634,8 @@ interpret:
* it that it now has its own resources back
*/
p->p_flag |= P_EXEC;
+ if ((p->p_flag2 & P2_NOTRACE_EXEC) == 0)
+ p->p_flag2 &= ~P2_NOTRACE;
if (p->p_flag & P_PPWAIT) {
p->p_flag &= ~(P_PPWAIT | P_PPTRACE);
cv_broadcast(&p->p_pwait);
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index f469db634bca..2c83422008a8 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -494,7 +494,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
* Increase reference counts on shared objects.
*/
p2->p_flag = P_INMEM;
- p2->p_flag2 = 0;
+ p2->p_flag2 = p1->p_flag2 & (P2_NOTRACE | P2_NOTRACE_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 9b0d14aea354..d58302a3df56 100644
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -280,6 +280,62 @@ reap_kill(struct thread *td, struct proc *p, struct procctl_reaper_kill *rk)
return (error);
}
+static int
+trace_ctl(struct thread *td, struct proc *p, int state)
+{
+
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+
+ /*
+ * Ktrace changes p_traceflag from or to zero under the
+ * process lock, so the test does not need to acquire ktrace
+ * mutex.
+ */
+ if ((p->p_flag & P_TRACED) != 0 || p->p_traceflag != 0)
+ return (EBUSY);
+
+ switch (state) {
+ case PROC_TRACE_CTL_ENABLE:
+ if (td->td_proc != p)
+ return (EPERM);
+ p->p_flag2 &= ~(P2_NOTRACE | P2_NOTRACE_EXEC);
+ break;
+ case PROC_TRACE_CTL_DISABLE_EXEC:
+ p->p_flag2 |= P2_NOTRACE_EXEC | P2_NOTRACE;
+ break;
+ case PROC_TRACE_CTL_DISABLE:
+ if ((p->p_flag2 & P2_NOTRACE_EXEC) != 0) {
+ KASSERT((p->p_flag2 & P2_NOTRACE) != 0,
+ ("dandling P2_NOTRACE_EXEC"));
+ if (td->td_proc != p)
+ return (EPERM);
+ p->p_flag2 &= ~P2_NOTRACE_EXEC;
+ } else {
+ p->p_flag2 |= P2_NOTRACE;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+trace_status(struct thread *td, struct proc *p, int *data)
+{
+
+ if ((p->p_flag2 & P2_NOTRACE) != 0) {
+ KASSERT((p->p_flag & P_TRACED) == 0,
+ ("%d traced but tracing disabled", p->p_pid));
+ *data = -1;
+ } else if ((p->p_flag & P_TRACED) != 0) {
+ *data = p->p_pptr->p_pid;
+ } else {
+ *data = 0;
+ }
+ return (0);
+}
+
#ifndef _SYS_SYSPROTO_H_
struct procctl_args {
idtype_t idtype;
@@ -302,6 +358,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
switch (uap->com) {
case PROC_SPROTECT:
+ case PROC_TRACE_CTL:
error = copyin(uap->data, &flags, sizeof(flags));
if (error != 0)
return (error);
@@ -328,6 +385,9 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
return (error);
data = &x.rk;
break;
+ case PROC_TRACE_STATUS:
+ data = &flags;
+ break;
default:
return (EINVAL);
}
@@ -342,6 +402,10 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
if (error == 0)
error = error1;
break;
+ case PROC_TRACE_STATUS:
+ if (error == 0)
+ error = copyout(&flags, uap->data, sizeof(flags));
+ break;
}
return (error);
}
@@ -364,6 +428,10 @@ kern_procctl_single(struct thread *td, struct proc *p, int com, void *data)
return (reap_getpids(td, p, data));
case PROC_REAP_KILL:
return (reap_kill(td, p, data));
+ case PROC_TRACE_CTL:
+ return (trace_ctl(td, p, *(int *)data));
+ case PROC_TRACE_STATUS:
+ return (trace_status(td, p, data));
default:
return (EINVAL);
}
@@ -375,6 +443,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
struct pgrp *pg;
struct proc *p;
int error, first_error, ok;
+ bool tree_locked;
switch (com) {
case PROC_REAP_ACQUIRE:
@@ -382,6 +451,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
case PROC_REAP_STATUS:
case PROC_REAP_GETPIDS:
case PROC_REAP_KILL:
+ case PROC_TRACE_STATUS:
if (idtype != P_PID)
return (EINVAL);
}
@@ -391,11 +461,17 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
case PROC_REAP_STATUS:
case PROC_REAP_GETPIDS:
case PROC_REAP_KILL:
+ case PROC_TRACE_CTL:
sx_slock(&proctree_lock);
+ tree_locked = true;
break;
case PROC_REAP_ACQUIRE:
case PROC_REAP_RELEASE:
sx_xlock(&proctree_lock);
+ tree_locked = true;
+ break;
+ case PROC_TRACE_STATUS:
+ tree_locked = false;
break;
default:
return (EINVAL);
@@ -456,6 +532,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
error = EINVAL;
break;
}
- sx_unlock(&proctree_lock);
+ if (tree_locked)
+ sx_unlock(&proctree_lock);
return (error);
}
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
index 2a667b598642..1043915658be 100644
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -1714,6 +1714,13 @@ p_candebug(struct thread *td, struct proc *p)
if ((p->p_flag & P_INEXEC) != 0)
return (EBUSY);
+ /* Denied explicitely */
+ if ((p->p_flag2 & P2_NOTRACE) != 0) {
+ error = priv_check(td, PRIV_DEBUG_DENIED);
+ if (error != 0)
+ return (error);
+ }
+
return (0);
}
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index e60e09db8a0d..45cb4b95630c 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -3247,7 +3247,8 @@ coredump(struct thread *td)
MPASS((p->p_flag & P_HADTHREADS) == 0 || p->p_singlethread == td);
_STOPEVENT(p, S_CORE, 0);
- if (!do_coredump || (!sugid_coredump && (p->p_flag & P_SUGID) != 0)) {
+ if (!do_coredump || (!sugid_coredump && (p->p_flag & P_SUGID) != 0) ||
+ (p->p_flag2 & P2_NOTRACE) != 0) {
PROC_UNLOCK(p);
return (EFAULT);
}
diff --git a/sys/sys/priv.h b/sys/sys/priv.h
index 94af58b4cf35..cf34762fdd89 100644
--- a/sys/sys/priv.h
+++ b/sys/sys/priv.h
@@ -111,6 +111,7 @@
#define PRIV_DEBUG_DIFFCRED 80 /* Exempt debugging other users. */
#define PRIV_DEBUG_SUGID 81 /* Exempt debugging setuid proc. */
#define PRIV_DEBUG_UNPRIV 82 /* Exempt unprivileged debug limit. */
+#define PRIV_DEBUG_DENIED 83 /* Exempt P2_NOTRACE. */
/*
* Dtrace privileges.
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 033b3dd776ca..e694d918c45e 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -675,6 +675,8 @@ struct proc {
/* These flags are kept in p_flag2. */
#define P2_INHERIT_PROTECTED 0x00000001 /* New children get P_PROTECTED. */
+#define P2_NOTRACE 0x00000002 /* No ptrace(2) attach or coredumps. */
+#define P2_NOTRACE_EXEC 0x00000004 /* Keep P2_NOPTRACE on exec(2). */
/* 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 d11b2b29600a..c57eee63d51a 100644
--- a/sys/sys/procctl.h
+++ b/sys/sys/procctl.h
@@ -41,6 +41,8 @@
#define PROC_REAP_STATUS 4 /* reaping status */
#define PROC_REAP_GETPIDS 5 /* get descendants */
#define PROC_REAP_KILL 6 /* kill descendants */
+#define PROC_TRACE_CTL 7 /* en/dis ptrace and coredumps */
+#define PROC_TRACE_STATUS 8 /* query tracing status */
/* Operations for PROC_SPROTECT (passed in integer arg). */
#define PPROT_OP(x) ((x) & 0xf)
@@ -96,6 +98,10 @@ struct procctl_reaper_kill {
#define REAPER_KILL_CHILDREN 0x00000001
#define REAPER_KILL_SUBTREE 0x00000002
+#define PROC_TRACE_CTL_ENABLE 1
+#define PROC_TRACE_CTL_DISABLE 2
+#define PROC_TRACE_CTL_DISABLE_EXEC 3
+
#ifndef _KERNEL
__BEGIN_DECLS
int procctl(idtype_t, id_t, int, void *);