Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F163532268
D58094.id181777.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
33 KB
Referenced Files
None
Subscribers
None
D58094.id181777.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D58094: Capsicumise truss(1)
Attached
Detach File
Event Timeline
Log In to Comment