Page MenuHomeFreeBSD

D58094.id181777.diff
No OneTemporary

D58094.id181777.diff

diff --git a/lib/libsys/kqueue.2 b/lib/libsys/kqueue.2
--- a/lib/libsys/kqueue.2
+++ b/lib/libsys/kqueue.2
@@ -587,11 +587,19 @@
and returns when the associated process performs one or more of the
requested events.
The events to monitor are:
-.Bl -tag -width "Dv NOTE_EXIT"
+.Bl -tag -width "Dv NOTE_PDSIGCHLD"
.It Dv NOTE_EXIT
The process has exited.
The exit status will be stored in
.Va data .
+.It Dv NOTE_PDSIGCHLD
+Activates on events that are reported through
+.Xr pdwait 2
+on the process descriptor.
+After the event is reported, the
+.Xr pdwait 2
+can be called to obtain the information about the process
+status change.
.El
.Pp
On return,
diff --git a/lib/libsys/pdfork.2 b/lib/libsys/pdfork.2
--- a/lib/libsys/pdfork.2
+++ b/lib/libsys/pdfork.2
@@ -83,7 +83,7 @@
.Fn pdfork
can accept the
.Fa pdflags:
-.Bl -tag -width PD_CLOEXEC
+.Bl -tag -width PD_ATTACH_CAP
.It Dv PD_DAEMON
Instead of the default terminate-on-close behaviour, allow the process to
live until it is explicitly killed with
@@ -103,6 +103,12 @@
flag, closing that descriptor kills the process.
.It Dv PD_CLOEXEC
Set close-on-exec on process descriptor.
+.It Dv PD_ATTACH_CAP
+Allows the
+.Xr ptrace 2
+request
+.Dv PT_ATTACH_PD
+on the resulting file descriptor.
.El
.Pp
The
diff --git a/lib/libsys/ptrace.2 b/lib/libsys/ptrace.2
--- a/lib/libsys/ptrace.2
+++ b/lib/libsys/ptrace.2
@@ -180,6 +180,12 @@
process'.
These requests will fail with error
.Er ESRCH .
+.It Dv security.bsd.ptrace_in_cap_mode
+Setting this sysctl to true allows the
+.Fn ptrace
+requests, except
+.Dv PT_ATTACH,
+issued by the processes in capability mode.
.It Dv securelevel and init
The
.Xr init 1
@@ -468,6 +474,29 @@
apply.)
The tracing process will see the newly-traced process stop and may
then control it as if it had been traced all along.
+.It Dv PT_ATTACH_PD
+The request attaches to the process specified by the process
+descriptor passed in the
+.Fa data
+argument.
+Otherwise, it is identical to
+.Dv PT_ATTACH .
+.Pp
+See
+.Xr pdfork 2
+for description of the process descriptors and the required flag
+.Dv PT_ATTACH_CAP .
+The
+.Fa pid
+argument must be set to zero.
+.Pp
+If the tunable
+.Dv security.bsd.ptrace_in_cap_mode
+is set to
+.Dv true ,
+.Dv PT_ATTACH_PD
+works even if the process issuing the request is in capability mode,
+and the process descriptor has the ptrace capability enabled.
.It Dv PT_DETACH
This request is like PT_CONTINUE, except that it does not allow
specifying an alternate place to continue execution, and after it
@@ -1436,6 +1465,17 @@
.Fa pve_pathlen
holds the minimum buffer size required on return.
.El
+.It Bq ECAPMODE
+The process issuing the
+.Fn ptrace
+call is in capability mode, and either the
+.Dv security.bsd.ptrace_in_cap_mode
+tunable is set to
+.Dv false,
+or, when the tunable is set to
+.Dv true ,
+the request is
+.Dv PT_ATTACH .
.El
.Sh SEE ALSO
.Xr execve 2 ,
diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c
--- a/sys/compat/freebsd32/freebsd32_sysent.c
+++ b/sys/compat/freebsd32/freebsd32_sysent.c
@@ -93,7 +93,7 @@
{ .sy_narg = AS(setuid_args), .sy_call = (sy_call_t *)sys_setuid, .sy_auevent = AUE_SETUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 23 = setuid */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_getuid, .sy_auevent = AUE_GETUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 24 = getuid */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_geteuid, .sy_auevent = AUE_GETEUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 25 = geteuid */
- { .sy_narg = AS(freebsd32_ptrace_args), .sy_call = (sy_call_t *)freebsd32_ptrace, .sy_auevent = AUE_PTRACE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 26 = freebsd32_ptrace */
+ { .sy_narg = AS(freebsd32_ptrace_args), .sy_call = (sy_call_t *)freebsd32_ptrace, .sy_auevent = AUE_PTRACE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 26 = freebsd32_ptrace */
{ .sy_narg = AS(freebsd32_recvmsg_args), .sy_call = (sy_call_t *)freebsd32_recvmsg, .sy_auevent = AUE_RECVMSG, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 27 = freebsd32_recvmsg */
{ .sy_narg = AS(freebsd32_sendmsg_args), .sy_call = (sy_call_t *)freebsd32_sendmsg, .sy_auevent = AUE_SENDMSG, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 28 = freebsd32_sendmsg */
{ .sy_narg = AS(recvfrom_args), .sy_call = (sy_call_t *)sys_recvfrom, .sy_auevent = AUE_RECVFROM, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 29 = recvfrom */
diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c
--- a/sys/kern/init_sysent.c
+++ b/sys/kern/init_sysent.c
@@ -92,7 +92,7 @@
{ .sy_narg = AS(setuid_args), .sy_call = (sy_call_t *)sys_setuid, .sy_auevent = AUE_SETUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 23 = setuid */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_getuid, .sy_auevent = AUE_GETUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 24 = getuid */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_geteuid, .sy_auevent = AUE_GETEUID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 25 = geteuid */
- { .sy_narg = AS(ptrace_args), .sy_call = (sy_call_t *)sys_ptrace, .sy_auevent = AUE_PTRACE, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 26 = ptrace */
+ { .sy_narg = AS(ptrace_args), .sy_call = (sy_call_t *)sys_ptrace, .sy_auevent = AUE_PTRACE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 26 = ptrace */
{ .sy_narg = AS(recvmsg_args), .sy_call = (sy_call_t *)sys_recvmsg, .sy_auevent = AUE_RECVMSG, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 27 = recvmsg */
{ .sy_narg = AS(sendmsg_args), .sy_call = (sy_call_t *)sys_sendmsg, .sy_auevent = AUE_SENDMSG, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 28 = sendmsg */
{ .sy_narg = AS(recvfrom_args), .sy_call = (sy_call_t *)sys_recvfrom, .sy_auevent = AUE_RECVFROM, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 29 = recvfrom */
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -1906,7 +1906,7 @@
/*
* Fill the given filecaps structure with full rights.
*/
-static void
+void
filecaps_fill(struct filecaps *fcaps)
{
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -1562,14 +1562,9 @@
if (error != 0)
return (error);
- error = fget(td, fd, &cap_pdwait_rights, &fp);
+ error = fget_procdesc(td, fd, &cap_pdwait_rights, &fp, &pd, NULL);
if (error != 0)
- return (error);
- if (fp->f_type != DTYPE_PROCDESC) {
- error = EINVAL;
goto exit_unlocked;
- }
- pd = fp->f_data;
for (;;) {
/* We own a reference on the procdesc file. */
@@ -1620,7 +1615,8 @@
exit_tree_locked:
sx_xunlock(&proctree_lock);
exit_unlocked:
- fdrop(fp, td);
+ if (fp != NULL)
+ fdrop(fp, td);
return (error);
}
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
@@ -41,6 +41,7 @@
#include <sys/systm.h>
#include <sys/acct.h>
#include <sys/bitstring.h>
+#include <sys/capsicum.h>
#include <sys/eventhandler.h>
#include <sys/exterrvar.h>
#include <sys/fcntl.h>
@@ -119,6 +120,7 @@
sys_pdfork(struct thread *td, struct pdfork_args *uap)
{
struct fork_req fr;
+ struct filecaps fcaps;
int error, fd, pid;
bzero(&fr, sizeof(fr));
@@ -126,6 +128,10 @@
fr.fr_pidp = &pid;
fr.fr_pd_fd = &fd;
fr.fr_pd_flags = uap->flags;
+ filecaps_fill(&fcaps);
+ if ((uap->flags & PD_ATTACH_CAP) != 0)
+ cap_rights_set(&fcaps.fc_rights, CAP_PTRACE_ATTACH);
+ fr.fr_pd_fcaps = &fcaps;
AUDIT_ARG_FFLAGS(uap->flags);
/*
* It is necessary to return fd by reference because 0 is a valid file
@@ -194,6 +200,7 @@
sys_pdrfork(struct thread *td, struct pdrfork_args *uap)
{
struct fork_req fr;
+ struct filecaps fcaps;
int error, fd, pid;
bzero(&fr, sizeof(fr));
@@ -226,6 +233,10 @@
fr.fr_pidp = &pid;
fr.fr_pd_fd = &fd;
fr.fr_pd_flags = uap->pdflags;
+ filecaps_fill(&fcaps);
+ if ((uap->pdflags & PD_ATTACH_CAP) != 0)
+ cap_rights_set(&fcaps.fc_rights, CAP_PTRACE_ATTACH);
+ fr.fr_pd_fcaps = &fcaps;
error = fork1(td, &fr);
if (error == 0) {
td->td_retval[0] = pid;
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -38,6 +38,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bitstring.h>
+#include <sys/capsicum.h>
#include <sys/conf.h>
#include <sys/elf.h>
#include <sys/eventhandler.h>
@@ -2389,6 +2390,7 @@
sysctl_kern_proc_sv_name(SYSCTL_HANDLER_ARGS)
{
struct proc *p;
+ struct thread *td;
char *sv_name;
int *name;
int namelen;
@@ -2402,9 +2404,16 @@
error = pget((pid_t)name[0], PGET_CANSEE, &p);
if (error != 0)
return (error);
- sv_name = p->p_sysent->sv_name;
+ td = curthread;
+ if (!IN_CAPABILITY_MODE(td) || (allow_ptrace_in_cap_mode &&
+ (td->td_proc == p->p_pptr || td->td_proc->p_pid == p->p_oppid)))
+ sv_name = p->p_sysent->sv_name;
+ else
+ error = ECAPMODE;
PROC_UNLOCK(p);
- return (sysctl_handle_string(oidp, sv_name, 0, req));
+ if (error == 0)
+ error = sysctl_handle_string(oidp, sv_name, 0, req);
+ return (error);
}
#ifdef KINFO_OVMENTRY_SIZE
@@ -3430,7 +3439,7 @@
CTLFLAG_MPSAFE, sysctl_kern_proc_pathname, "Process executable path");
static SYSCTL_NODE(_kern_proc, KERN_PROC_SV_NAME, sv_name, CTLFLAG_RD |
- CTLFLAG_MPSAFE, sysctl_kern_proc_sv_name,
+ CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_kern_proc_sv_name,
"Process syscall vector name (ABI type)");
static SYSCTL_NODE(_kern_proc, (KERN_PROC_GID | KERN_PROC_INC_THREAD), gid_td,
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -1956,6 +1956,7 @@
sys_pdkill(struct thread *td, struct pdkill_args *uap)
{
struct proc *p;
+ struct file *fp;
int error;
AUDIT_ARG_SIGNUM(uap->signum);
@@ -1963,14 +1964,19 @@
if ((u_int)uap->signum > _SIG_MAXSIG)
return (EINVAL);
- error = procdesc_find(td, uap->fd, &cap_pdkill_rights, &p);
- if (error)
- return (error);
+ sx_slock(&proctree_lock);
+ error = fget_procdesc(td, uap->fd, &cap_pdkill_rights, &fp, NULL, &p);
+ sx_sunlock(&proctree_lock);
+ if (error != 0)
+ goto out;
AUDIT_ARG_PROCESS(p);
error = p_cansignal(td, p, uap->signum);
- if (error == 0 && uap->signum)
+ if (error == 0 && uap->signum != 0)
kern_psignal(p, uap->signum);
PROC_UNLOCK(p);
+out:
+ if (fp != NULL)
+ fdrop(fp, td);
return (error);
}
@@ -2315,6 +2321,7 @@
ps = p->p_sigacts;
KNOTE_LOCKED(p->p_klist, NOTE_SIGNAL | sig);
+ procdesc_jobstate(p);
prop = sigprop(sig);
if (td == NULL) {
@@ -3682,6 +3689,7 @@
*/
p->p_pptr->p_flag |= P_STATCHILD;
wakeup(p->p_pptr);
+ procdesc_jobstate(p);
ps = p->p_pptr->p_sigacts;
mtx_lock(&ps->ps_mtx);
diff --git a/sys/kern/subr_capability.c b/sys/kern/subr_capability.c
--- a/sys/kern/subr_capability.c
+++ b/sys/kern/subr_capability.c
@@ -93,6 +93,8 @@
const cap_rights_t cap_pdkill_rights = CAP_RIGHTS_INITIALIZER(CAP_PDKILL);
const cap_rights_t cap_pdwait_rights = CAP_RIGHTS_INITIALIZER(CAP_PDWAIT);
const cap_rights_t cap_pread_rights = CAP_RIGHTS_INITIALIZER(CAP_PREAD);
+const cap_rights_t cap_ptrace_attach_rights =
+ CAP_RIGHTS_INITIALIZER(CAP_PTRACE_ATTACH);
const cap_rights_t cap_pwrite_rights = CAP_RIGHTS_INITIALIZER(CAP_PWRITE);
const cap_rights_t cap_read_rights = CAP_RIGHTS_INITIALIZER(CAP_READ);
const cap_rights_t cap_recv_rights = CAP_RIGHTS_INITIALIZER(CAP_RECV);
diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c
--- a/sys/kern/sys_procdesc.c
+++ b/sys/kern/sys_procdesc.c
@@ -64,6 +64,7 @@
#include <sys/param.h>
#include <sys/capsicum.h>
+#include <sys/event.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
@@ -115,38 +116,6 @@
.fo_flags = DFLAG_PASSABLE,
};
-/*
- * Return a locked process given a process descriptor, or ESRCH if it has
- * died.
- */
-int
-procdesc_find(struct thread *td, int fd, const cap_rights_t *rightsp,
- struct proc **p)
-{
- struct procdesc *pd;
- struct file *fp;
- int error;
-
- error = fget(td, fd, rightsp, &fp);
- if (error)
- return (error);
- if (fp->f_type != DTYPE_PROCDESC) {
- error = EINVAL;
- goto out;
- }
- pd = fp->f_data;
- sx_slock(&proctree_lock);
- if (pd->pd_proc != NULL) {
- *p = pd->pd_proc;
- PROC_LOCK(*p);
- } else
- error = ESRCH;
- sx_sunlock(&proctree_lock);
-out:
- fdrop(fp, td);
- return (error);
-}
-
/*
* Function to be used by procstat(1) sysctls when returning procdesc
* information.
@@ -173,16 +142,11 @@
struct file *fp;
int error;
- error = fget(td, fd, rightsp, &fp);
- if (error)
- return (error);
- if (fp->f_type != DTYPE_PROCDESC) {
- error = EBADF;
- goto out;
- }
- *pidp = procdesc_pid(fp);
-out:
- fdrop(fp, td);
+ error = fget_procdesc(td, fd, rightsp, &fp, NULL, NULL);
+ if (error == 0)
+ *pidp = procdesc_pid(fp);
+ if (fp != NULL)
+ fdrop(fp, td);
return (error);
}
@@ -347,7 +311,7 @@
return (1);
}
selwakeup(&pd->pd_selinfo);
- KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_EXIT);
+ KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_EXIT | NOTE_PDSIGCHLD);
PROCDESC_UNLOCK(pd);
/* Wakeup all waiters for this procdesc' process exit. */
@@ -355,6 +319,22 @@
return (0);
}
+void
+procdesc_jobstate(struct proc *p)
+{
+ struct procdesc *pd;
+
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ pd = p->p_procdesc;
+ if (pd == NULL)
+ return;
+
+ PROCDESC_LOCK(pd);
+ KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_PDSIGCHLD);
+ PROCDESC_UNLOCK(pd);
+ wakeup(&p->p_procdesc);
+}
+
/*
* When a process descriptor is reaped, perhaps as a result of close(), release
* the process's reference on the process descriptor.
@@ -516,7 +496,7 @@
kn->kn_fflags |= event;
/* Process is gone, so flag the event as finished. */
- if (event == NOTE_EXIT) {
+ if ((event & NOTE_EXIT) != 0) {
kn->kn_flags |= EV_EOF | EV_ONESHOT;
if (kn->kn_fflags & NOTE_EXIT)
kn->kn_data = pd->pd_xstat;
@@ -616,7 +596,8 @@
}
static int
-pdopenpid1(struct thread *td, pid_t pid, struct procdesc **pdf, struct file *fp)
+pdopenpid1(struct thread *td, pid_t pid, struct procdesc **pdf,
+ struct file *fp, int flags, cap_rights_t *fcaps )
{
struct proc *p;
struct procdesc *pd;
@@ -631,6 +612,19 @@
PROC_UNLOCK(p);
return (EBUSY);
}
+
+ /*
+ * Allow implicit parent in cap mode: either real parent or
+ * debugger can open pid.
+ */
+ if (td->td_proc == p->p_pptr || p->p_oppid != td->td_proc->p_pid) {
+ if ((flags & PD_ATTACH_CAP) != 0)
+ cap_rights_set(fcaps, CAP_PTRACE_ATTACH);
+ } else if (!IN_CAPABILITY_MODE(td)) {
+ PROC_UNLOCK(p);
+ return (ECHILD);
+ }
+
pd = p->p_procdesc;
if (pd != NULL) {
refcount_acquire(&pd->pd_refcount);
@@ -655,6 +649,7 @@
{
struct file *fp;
struct procdesc *pdf;
+ struct filecaps fcaps;
int error, fd, fflags;
error = falloc_noinstall(td, &fp);
@@ -664,13 +659,16 @@
pdf = procdesc_alloc(flags);
if ((flags & PD_DAEMON) != 0)
fp->f_pdflags |= F_PD_NOKILL;
+ filecaps_fill(&fcaps);
+ if ((flags & PD_ATTACH_CAP) != 0)
+ cap_rights_set(&fcaps.fc_rights, CAP_PTRACE_ATTACH);
sx_xlock(&proctree_lock);
- error = pdopenpid1(td, pid, &pdf, fp);
+ error = pdopenpid1(td, pid, &pdf, fp, flags, &fcaps.fc_rights);
sx_xunlock(&proctree_lock);
if (error == 0) {
- error = finstall(td, fp, &fd, fflags, NULL);
+ error = finstall(td, fp, &fd, fflags, &fcaps);
if (error == 0) {
td->td_retval[0] = fd;
} else {
@@ -704,59 +702,88 @@
return (kern_pdopenpid(td, args->pid, args->flags));
}
+/*
+ * Get the file/process descriptor/process from the procdesc file
+ * descriptor. The process descriptor and process returns are
+ * optional. The process is returned locked. If the process return
+ * is requested, proctree_lock must be owned.
+ *
+ * The caller must fdrop(*pfp) if *pfp != NULL, regardless of the
+ * error returned, after the procdesc_lock is unlocked. The
+ * procdesc_close() takes the proctree_lock.
+ */
+int
+fget_procdesc(struct thread *td, int pdfd, const cap_rights_t *cap_rights,
+ struct file **pfp, struct procdesc **pdp, struct proc **pp)
+{
+ struct file *fp;
+ struct procdesc *pd;
+ struct proc *p;
+ int error;
+
+ if (pp != NULL)
+ sx_assert(&proctree_lock, SX_LOCKED);
+
+ *pfp = NULL;
+ error = fget(td, pdfd, cap_rights, &fp);
+ if (error != 0)
+ return (error);
+ *pfp = fp;
+ if (fp->f_type != DTYPE_PROCDESC)
+ return (EBADF);
+ pd = fp->f_data;
+ if (pp != NULL) {
+ p = pd->pd_proc;
+ if (p == NULL) {
+ return (ESRCH);
+ } else {
+ *pp = p;
+ PROC_LOCK(p);
+ }
+ }
+ if (pdp != NULL)
+ *pdp = pd;
+ return (0);
+}
+
static int
kern_pddupfd(struct thread *td, int pdfd, int fd, int flags)
{
struct proc *p;
struct file *fp, *pfp;
- struct procdesc *pd;
struct filecaps fcaps;
uint8_t fd_flags;
int error, fdr;
- error = fget(td, pdfd, &cap_pddupfd_rights, &pfp);
- if (error != 0)
- return (error);
- if (pfp->f_type != DTYPE_PROCDESC) {
- error = EBADF;
- goto out;
- }
- pd = pfp->f_data;
-again:
sx_slock(&proctree_lock);
- p = pd->pd_proc;
- if (p != NULL) {
- AUDIT_ARG_PROCESS(p);
- PROC_LOCK(p);
- sx_sunlock(&proctree_lock);
+ error = fget_procdesc(td, pdfd, &cap_pddupfd_rights, &pfp, NULL, &p);
+ if (error == 0) {
if ((p->p_flag & P_WEXIT) != 0) {
error = ESRCH;
+ PROC_UNLOCK(p);
} else {
- /*
- * Block the target process from entering
- * execve(). We need to ensure that the
- * p_candebug() predicate is stable until the
- * fget_remote() call ends even after the
- * process lock is dropped. For that, the
- * process must not change uid/suid.
- */
- if (!execve_block(td, p))
- goto again;
- error = p_candebug(td, p);
- if (error == 0)
- _PHOLD(p);
- else
- execve_unblock(td, p);
+ _PHOLD(p);
}
- PROC_UNLOCK(p);
- if (error != 0)
- goto out;
+ }
+ sx_sunlock(&proctree_lock);
+ if (error != 0)
+ goto out;
+ AUDIT_ARG_PROCESS(p);
+ PROC_LOCK_ASSERT(p, MA_OWNED);
- error = fget_remote(td, p, fd, &fcaps, &fd_flags, &fp);
- PROC_LOCK(p);
- execve_unblock(td, p);
- _PRELE(p);
+ /*
+ * Block the target process from entering execve().
+ * We need to ensure that the p_candebug() predicate
+ * is stable until the fget_remote() call ends even
+ * after the process lock is dropped. For that, the
+ * process must not change uid/suid.
+ */
+ execve_block_wait(td, p);
+ error = p_candebug(td, p);
+
+ if (error == 0) {
PROC_UNLOCK(p);
+ error = fget_remote(td, p, fd, &fcaps, &fd_flags, &fp);
if (error == 0) {
error = finstall_refed(td, fp, &fdr, O_CLOEXEC |
((fd_flags & FD_RESOLVE_BENEATH) != 0 ?
@@ -768,12 +795,14 @@
td->td_retval[0] = fdr;
}
}
- } else {
- sx_sunlock(&proctree_lock);
- error = ESRCH;
+ PROC_LOCK(p);
}
+ execve_unblock(td, p);
+ _PRELE(p);
+ PROC_UNLOCK(p);
out:
- fdrop(pfp, td);
+ if (pfp != NULL)
+ fdrop(pfp, td);
return (error);
}
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -32,7 +32,7 @@
*/
#include <sys/systm.h>
-#include <sys/caprights.h>
+#include <sys/capsicum.h>
#include <sys/filedesc.h>
#include <sys/imgact.h>
#include <sys/ktr.h>
@@ -43,6 +43,7 @@
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
+#include <sys/procdesc.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/rwlock.h>
@@ -50,6 +51,7 @@
#include <sys/sleepqueue.h>
#include <sys/sx.h>
#include <sys/syscallsubr.h>
+#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/vnode.h>
@@ -963,6 +965,11 @@
return (NULL);
}
+bool allow_ptrace_in_cap_mode = true;
+SYSCTL_BOOL(_security_bsd, OID_AUTO, allow_ptrace_in_cap_mode, CTLFLAG_RWTUN,
+ &allow_ptrace_in_cap_mode, 0,
+ "Allow ptrace(2) in capability mode");
+
int
kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
{
@@ -978,6 +985,7 @@
struct ptrace_coredump *pc;
struct thr_coredump_req *tcq;
struct thr_syscall_req *tsr;
+ struct file *pfp;
int error, num, tmp;
lwpid_t tid = 0, *buf;
#ifdef COMPAT_FREEBSD32
@@ -988,11 +996,17 @@
curp = td->td_proc;
proctree_locked = false;
p2_req_set = false;
+ pfp = NULL;
+
+ if (IN_CAPABILITY_MODE(td) && (!allow_ptrace_in_cap_mode ||
+ req == PT_ATTACH))
+ return (ECAPMODE);
/* Lock proctree before locking the process. */
switch (req) {
case PT_TRACE_ME:
case PT_ATTACH:
+ case PT_ATTACH_PD:
case PT_STEP:
case PT_CONTINUE:
case PT_TO_SCE:
@@ -1015,7 +1029,17 @@
p = td->td_proc;
PROC_LOCK(p);
} else {
- if (pid <= PID_MAX) {
+ if (req == PT_ATTACH_PD) {
+ if (pid != 0) {
+ sx_xunlock(&proctree_lock);
+ return (EINVAL);
+ }
+ error = fget_procdesc(td, data,
+ &cap_ptrace_attach_rights, &pfp, NULL, &p);
+ if (error != 0)
+ goto fail_proctree;
+ tid = pid = p->p_pid;
+ } else if (pid <= PID_MAX) {
if ((p = pfind(pid)) == NULL) {
if (proctree_locked)
sx_xunlock(&proctree_lock);
@@ -1092,6 +1116,7 @@
break;
case PT_ATTACH:
+ case PT_ATTACH_PD:
/* Self */
if (p == td->td_proc) {
error = EINVAL;
@@ -1183,6 +1208,7 @@
break;
case PT_ATTACH:
+ case PT_ATTACH_PD:
/* security check done above */
/*
* It would be nice if the tracing relationship was separate
@@ -1857,8 +1883,11 @@
p->p_flag2 &= ~P2_PTRACEREQ;
}
PROC_UNLOCK(p);
+fail_proctree:
if (proctree_locked)
sx_xunlock(&proctree_lock);
+ if (pfp != NULL)
+ fdrop(pfp, td);
return (error);
}
#undef PROC_READ
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -264,7 +264,7 @@
25 AUE_GETEUID STD|CAPENABLED {
uid_t geteuid(void);
}
-26 AUE_PTRACE STD {
+26 AUE_PTRACE STD|CAPENABLED {
int ptrace(
int req,
pid_t pid,
diff --git a/sys/sys/caprights.h b/sys/sys/caprights.h
--- a/sys/sys/caprights.h
+++ b/sys/sys/caprights.h
@@ -95,6 +95,7 @@
extern const cap_rights_t cap_pdkill_rights;
extern const cap_rights_t cap_pdwait_rights;
extern const cap_rights_t cap_pread_rights;
+extern const cap_rights_t cap_ptrace_attach_rights;
extern const cap_rights_t cap_pwrite_rights;
extern const cap_rights_t cap_read_rights;
extern const cap_rights_t cap_recv_rights;
diff --git a/sys/sys/capsicum.h b/sys/sys/capsicum.h
--- a/sys/sys/capsicum.h
+++ b/sys/sys/capsicum.h
@@ -299,7 +299,9 @@
/* Allows pddupfd(2). */
#define CAP_PDDUPFD CAPRIGHT(1, 0x0000000000800000ULL)
-#define CAP_UNUSED1_25 CAPRIGHT(1, 0x0000000001000000ULL)
+/* Allows ptrace(PT_ATTACH_PD) */
+#define CAP_PTRACE_ATTACH CAPRIGHT(1, 0x0000000001000000ULL)
+
#define CAP_UNUSED1_26 CAPRIGHT(1, 0x0000000002000000ULL)
#define CAP_UNUSED1_27 CAPRIGHT(1, 0x0000000004000000ULL)
#define CAP_UNUSED1_28 CAPRIGHT(1, 0x0000000008000000ULL)
@@ -333,7 +335,7 @@
#define CAP_UNUSED1_56 CAPRIGHT(1, 0x0080000000000000ULL)
#define CAP_UNUSED1_57 CAPRIGHT(1, 0x0100000000000000ULL)
-/* All used bits for index 1. */
+/* All default bits for index 1. */
#define CAP_ALL1 CAPRIGHT(1, 0x0000000000FFFFFFULL)
/* Backward compatibility. */
diff --git a/sys/sys/event.h b/sys/sys/event.h
--- a/sys/sys/event.h
+++ b/sys/sys/event.h
@@ -202,9 +202,12 @@
/*
* data/hint flags for EVFILT_PROC and EVFILT_PROCDESC, shared with userspace
*/
-#define NOTE_EXIT 0x80000000 /* process exited */
-#define NOTE_FORK 0x40000000 /* process forked */
-#define NOTE_EXEC 0x20000000 /* process exec'd */
+#define NOTE_EXIT 0x80000000 /* proc/procdesc: process
+ exited */
+#define NOTE_FORK 0x40000000 /* proc: process forked */
+#define NOTE_EXEC 0x20000000 /* proc: process exec'd */
+#define NOTE_PDSIGCHLD 0x10000000 /* procdesc: pdwait() info
+ available */
#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */
#define NOTE_PDATAMASK 0x000fffff /* mask for pid */
diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h
--- a/sys/sys/filedesc.h
+++ b/sys/sys/filedesc.h
@@ -243,6 +243,7 @@
bool filecaps_copy(const struct filecaps *src, struct filecaps *dst,
bool locked);
void filecaps_move(struct filecaps *src, struct filecaps *dst);
+void filecaps_fill(struct filecaps *fcaps);
void filecaps_free(struct filecaps *fcaps);
int closef(struct file *fp, struct thread *td);
diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h
--- a/sys/sys/procdesc.h
+++ b/sys/sys/procdesc.h
@@ -102,8 +102,7 @@
* In-kernel interfaces to process descriptors.
*/
int procdesc_exit(struct proc *);
-int procdesc_find(struct thread *, int fd, const cap_rights_t *,
- struct proc **);
+void procdesc_jobstate(struct proc *p);
int kern_pdgetpid(struct thread *, int fd, const cap_rights_t *,
pid_t *pidp);
void procdesc_new(struct proc *, int);
@@ -113,7 +112,9 @@
int procdesc_falloc(struct thread *, struct file **, int *, int,
struct filecaps *);
-
+int fget_procdesc(struct thread *td, int pfd,
+ const cap_rights_t *cap_rights, struct file **pfp,
+ struct procdesc **pdp, struct proc **pp);
#else /* !_KERNEL */
#include <sys/cdefs.h>
@@ -150,7 +151,8 @@
*/
#define PD_DAEMON 0x00000001 /* Don't exit when procdesc closes. */
#define PD_CLOEXEC 0x00000002 /* Close file descriptor on exec. */
+#define PD_ATTACH_CAP 0x00000004 /* Allowed PT_ATTACH_PD in cap mode. */
-#define PD_ALLOWED_AT_FORK (PD_DAEMON | PD_CLOEXEC)
+#define PD_ALLOWED_AT_FORK (PD_DAEMON | PD_CLOEXEC | PD_ATTACH_CAP)
#endif /* !_SYS_PROCDESC_H_ */
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
--- a/sys/sys/ptrace.h
+++ b/sys/sys/ptrace.h
@@ -85,6 +85,7 @@
#define PT_GETREGSET 42 /* Get a target register set */
#define PT_SETREGSET 43 /* Set a target register set */
#define PT_SC_REMOTE 44 /* Execute a syscall */
+#define PT_ATTACH_PD 45 /* Attach by process descriptor */
#define PT_FIRSTMACH 64 /* for machine-specific requests */
#define PT_LASTMACH 127
@@ -280,6 +281,7 @@
void ptrace_unsuspend(struct proc *p);
extern bool allow_ptrace;
+extern bool allow_ptrace_in_cap_mode;
#else /* !_KERNEL */
diff --git a/usr.bin/truss/main.c b/usr.bin/truss/main.c
--- a/usr.bin/truss/main.c
+++ b/usr.bin/truss/main.c
@@ -31,13 +31,14 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
/*
* The main module for truss. Surprisingly simple, but, then, the other
* files handle the bulk of the work. And, of course, the kernel has to
* do a lot of the work :).
*/
+#include <sys/capsicum.h>
+#include <sys/event.h>
#include <sys/ptrace.h>
#include <err.h>
@@ -84,6 +85,9 @@
trussinfo->outfile = stderr;
trussinfo->strsize = 32;
trussinfo->curthread = NULL;
+ trussinfo->pdkq = kqueue();
+ if (trussinfo->pdkq == -1)
+ err(1, "kqueue");
LIST_INIT(&trussinfo->proclist);
while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) {
switch (c) {
diff --git a/usr.bin/truss/setup.c b/usr.bin/truss/setup.c
--- a/usr.bin/truss/setup.c
+++ b/usr.bin/truss/setup.c
@@ -31,13 +31,15 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
/*
* Various setup functions for truss. Not the cleanest-written code,
* I'm afraid.
*/
+#include <sys/capsicum.h>
+#include <sys/event.h>
#include <sys/ptrace.h>
+#include <sys/procdesc.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/wait.h>
@@ -59,6 +61,8 @@
#include "syscall.h"
#include "extern.h"
+#define WFLAGS (WTRAPPED | WEXITED | WCONTINUED | WUNTRACED)
+
struct procabi_table {
const char *name;
struct procabi *abi;
@@ -68,8 +72,8 @@
static void enter_syscall(struct trussinfo *, struct threadinfo *,
struct ptrace_lwpinfo *);
-static void new_proc(struct trussinfo *, pid_t, lwpid_t);
-
+static void new_proc(struct trussinfo *, int, pid_t, lwpid_t);
+static void new_proc_register_kev(struct trussinfo *info, int pfd);
static struct procabi freebsd = {
.type = "FreeBSD",
@@ -148,8 +152,9 @@
setup_and_wait(struct trussinfo *info, char *command[])
{
pid_t pid;
+ int fd;
- pid = vfork();
+ pid = pdfork(&fd, PD_DAEMON | PD_CLOEXEC);
if (pid == -1)
err(1, "fork failed");
if (pid == 0) { /* Child */
@@ -158,11 +163,15 @@
err(1, "execvp %s", command[0]);
}
+ /* No error check to allow truss to run on non-cap enabled kernels */
+ cap_enter();
+ new_proc_register_kev(info, fd);
+
/* Only in the parent here */
- if (waitpid(pid, NULL, 0) < 0)
+ if (pdwait(fd, NULL, WFLAGS, NULL, NULL) < 0)
err(1, "unexpected stop in waitpid");
- new_proc(info, pid, 0);
+ new_proc(info, pid, 0, fd);
}
/*
@@ -172,19 +181,26 @@
start_tracing(struct trussinfo *info, pid_t pid)
{
int ret, retry;
+ int fd;
+
+ fd = pdopenpid(pid, PD_DAEMON | PD_CLOEXEC | PD_ATTACH_CAP);
+ if (fd == -1)
+ err(1, "Cannot open the target process");
+ cap_enter();
+ new_proc_register_kev(info, fd);
retry = 10;
do {
- ret = ptrace(PT_ATTACH, pid, NULL, 0);
+ ret = ptrace(PT_ATTACH_PD, 0, NULL, pid);
usleep(200);
} while (ret && retry-- > 0);
if (ret)
err(1, "Cannot attach to target process");
- if (waitpid(pid, NULL, 0) < 0)
+ if (pdwait(fd, NULL, WFLAGS, NULL, NULL) < 0)
err(1, "Unexpected stop in waitpid");
- new_proc(info, pid, 0);
+ new_proc(info, pid, 0, fd);
}
/*
@@ -201,17 +217,21 @@
}
static void
-detach_proc(pid_t pid)
+detach_proc(int pfd)
{
- int sig, status;
+ int error, pid, sig, status;
+
+ error = pdgetpid(pfd, &pid);
+ if (error != 0)
+ err(1, "pdgetpid");
/*
* Stop the child so that we can detach. Filter out possible
* lingering SIGTRAP events buffered in the threads.
*/
- kill(pid, SIGSTOP);
+ pdkill(pfd, SIGSTOP);
for (;;) {
- if (waitpid(pid, &status, 0) < 0)
+ if (pdwait(pfd, &status, WFLAGS, NULL, NULL) < 0)
err(1, "Unexpected error in waitpid");
sig = WIFSTOPPED(status) ? WSTOPSIG(status) : 0;
if (sig == SIGSTOP)
@@ -225,7 +245,7 @@
if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0)
err(1, "Can not detach the process");
- kill(pid, SIGCONT);
+ pdkill(pfd, SIGCONT);
}
/*
@@ -318,7 +338,22 @@
}
static void
-new_proc(struct trussinfo *info, pid_t pid, lwpid_t lwpid)
+new_proc_register_kev(struct trussinfo *info, int pfd)
+{
+ struct kevent ev[1];
+ int error;
+
+ EV_SET(&ev[0], pfd, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT | NOTE_PDSIGCHLD,
+ 0, 0);
+ error = kevent(info->pdkq, ev, nitems(ev), NULL, 0, NULL);
+ if (error == -1) {
+ err(1, "Unable to register pfd %d for notifications",
+ pfd);
+ }
+}
+
+static void
+new_proc(struct trussinfo *info, pid_t pid, lwpid_t lwpid, int pfd)
{
struct procinfo *np;
@@ -330,7 +365,13 @@
if (np->pid == pid)
errx(1, "Duplicate process for pid %ld", (long)pid);
}
-
+ if (pfd == -1) {
+ pfd = pdopenpid(pid, PD_DAEMON | PD_CLOEXEC | PD_ATTACH_CAP);
+ if (pfd == -1)
+ err(1, "pdopenid %d", pid);
+ new_proc_register_kev(info, pfd);
+ }
+
if (info->flags & FOLLOWFORKS)
if (ptrace(PT_FOLLOW_FORK, pid, NULL, 1) == -1)
err(1, "Unable to follow forks for pid %ld", (long)pid);
@@ -338,6 +379,7 @@
err(1, "Unable to enable LWP events for pid %ld", (long)pid);
np = calloc(1, sizeof(struct procinfo));
np->pid = pid;
+ np->pfd = pfd;
np->abi = find_abi(pid);
LIST_INIT(&np->threadlist);
LIST_INIT(&np->fdlist);
@@ -350,10 +392,15 @@
}
static void
-free_proc(struct procinfo *p)
+free_proc(struct trussinfo *info, struct procinfo *p)
{
struct threadinfo *t, *t2;
struct fd_domain *f, *f2;
+ struct kevent ev[1];
+
+ EV_SET(&ev[0], p->pfd, EVFILT_PROCDESC, EV_DELETE, 0, 0, 0);
+ (void)kevent(info->pdkq, ev, nitems(ev), NULL, 0, 0);
+ close(p->pfd);
LIST_FOREACH_SAFE(t, &p->threadlist, entries, t2) {
free(t);
@@ -373,8 +420,8 @@
struct procinfo *p, *p2;
LIST_FOREACH_SAFE(p, &info->proclist, entries, p2) {
- detach_proc(p->pid);
- free_proc(p);
+ detach_proc(p->pfd);
+ free_proc(info, p);
}
}
@@ -618,7 +665,7 @@
if (p->abi == NULL) {
if (ptrace(PT_DETACH, p->pid, (caddr_t)1, 0) < 0)
err(1, "Can not detach the process");
- free_proc(p);
+ free_proc(info, p);
}
}
}
@@ -789,7 +836,8 @@
{
struct ptrace_lwpinfo pl;
siginfo_t si;
- int pending_signal;
+ struct kevent ev[1];
+ int error, pending_signal;
while (!LIST_EMPTY(&info->proclist)) {
if (detaching) {
@@ -797,13 +845,25 @@
return;
}
- if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) {
+ error = kevent(info->pdkq, NULL, 0, ev, nitems(ev), 0);
+ if (error == -1) {
if (errno == EINTR)
continue;
- err(1, "Unexpected error from waitid");
+ err(1, "Unexpected error from kevent");
+ }
+ if (error == 0) {
+ /* XXXKIB ? */
+ continue;
}
- assert(si.si_signo == SIGCHLD);
+ error = pdwait(ev[0].ident, NULL, WFLAGS, NULL, &si);
+ if (error == -1) {
+ if (errno == EINTR)
+ continue;
+ err(1, "Unexpected error from pdwait");
+ }
+
+ // XXXKIB assert(si.si_signo == SIGCHLD);
switch (si.si_code) {
case CLD_EXITED:
@@ -815,7 +875,7 @@
thread_exit_syscall(info);
report_exit(info, &si);
}
- free_proc(info->curthread->proc);
+ free_proc(info, info->curthread->proc);
info->curthread = NULL;
break;
case CLD_TRAPPED:
@@ -824,7 +884,7 @@
err(1, "ptrace(PT_LWPINFO)");
if (pl.pl_flags & PL_FLAG_CHILD) {
- new_proc(info, si.si_pid, pl.pl_lwpid);
+ new_proc(info, si.si_pid, pl.pl_lwpid, -1);
assert(LIST_FIRST(&info->proclist)->abi !=
NULL);
} else if (pl.pl_flags & PL_FLAG_BORN)
diff --git a/usr.bin/truss/truss.h b/usr.bin/truss/truss.h
--- a/usr.bin/truss/truss.h
+++ b/usr.bin/truss/truss.h
@@ -106,6 +106,7 @@
struct procinfo {
LIST_ENTRY(procinfo) entries;
pid_t pid;
+ int pfd;
struct procabi *abi;
LIST_HEAD(, threadinfo) threadlist;
@@ -117,6 +118,7 @@
int flags;
int strsize;
FILE *outfile;
+ int pdkq;
struct timespec start_time;

File Metadata

Mime Type
text/plain
Expires
Sat, Jul 25, 5:09 AM (12 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35463216
Default Alt Text
D58094.id181777.diff (33 KB)

Event Timeline