Page MenuHomeFreeBSD

D58094.id182261.diff
No OneTemporary

D58094.id182261.diff

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_PTRACE_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_PTRACE_CAP
+Allows the
+.Xr ptrace 2
+request with the
+.Dv PT_PROCDESC
+modified on the resulting file descriptor.
.El
.Pp
The
@@ -158,6 +164,21 @@
.Er ESRCH
error.
.Pp
+The
+.Fn pdopenpid
+request is allowed in the capability mode
+.Pq see Xr capsicum 4
+.Bl -dash -compact
+.It
+when the target process is the child of the calling process
+.It
+when the calling process is the debugger of the target process.
+.El
+The debugger is attached to its target by
+.Xr ptrace 2
+of other means, e.g. by debugging its real parent with
+the follow mode on fork.
+.Pp
.Fn pdgetpid
queries the process ID (PID) in the process descriptor
.Fa fd .
diff --git a/lib/libsys/ptrace.2 b/lib/libsys/ptrace.2
--- a/lib/libsys/ptrace.2
+++ b/lib/libsys/ptrace.2
@@ -131,10 +131,19 @@
Similarly, for operations affecting only a thread, the thread ID
needs to be passed.
.Pp
+If the
+.Fa request
+argument is or-ed with the
+.Dv PT_PROCDESC
+flag, then the
+.Fa pid
+argument is interpreted as the process descriptor.
+The call operates on the process referenced by the descriptor.
+.Pp
Still, for global operations, the ID of any thread can be used as the
target, and system will perform the request on the process owning
that thread.
-If a thread operation got the process ID as
+If a thread operation got the process ID or the process descriptor as
.Fa pid ,
the system randomly selects a thread from among the threads owned
by the process.
@@ -180,6 +189,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
@@ -1044,6 +1059,60 @@
member.
Note that the request and its result do not affect the returned value from
the currently executed syscall, if any.
+.It Dv PT_GET_CHILDREN
+Returns a report describing the children of the process specified by
+.Fa pid .
+Only children visible to the current thread are reported.
+.Pp
+If the
+.Fa addr
+argument is
+.Dv NULL ,
+the instantaneous number of children is returned as
+the result of the request.
+.Pp
+If the
+.Fa addr
+argument is not
+.Dv NULL ,
+it must point to the array of
+.Bd -literal
+struct ptrace_child {
+ pid_t pid;
+ int flags;
+};
+.Ed
+elements.
+The size of the array in bytes must be passed in
+.Fa data .
+The
+.Dv PT_GET_CHILDREN
+implementation fills the array and returns the number of elements filled.
+.Pp
+For each element, the
+.Va pid
+member contains the PID of the corresponding child.
+The
+.Va flags
+member may have the following flags set:
+.Bl -tag -width PTCHLD_TRACED_BY_ME
+.It Dv PTCHLD_TRACED
+The child specified by the
+.Va pid
+member is traced.
+.It Dv PTCHLD_TRACED_BY_ME
+The child specified by the
+.Va pid
+member is traced
+by the caller (not the
+process specified by the
+.Fa pid
+argument to the function).
+.It Dv PTCHLD_EXITED
+The child process is exiting or is a zombie.
+.It Dv PTCHLD_ORPHAN
+The child process was temporarily reparented to its debugger.
+.El
.El
.Sh PT_COREDUMP and PT_SC_REMOTE usage
The process must be stopped before dumping or initiating a remote system call.
@@ -1456,6 +1525,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/amd64/linux/linux_machdep.c b/sys/amd64/linux/linux_machdep.c
--- a/sys/amd64/linux/linux_machdep.c
+++ b/sys/amd64/linux/linux_machdep.c
@@ -326,7 +326,8 @@
l_regset->fs_base = pcb->pcb_fsbase;
l_regset->gs_base = pcb->pcb_gsbase;
- error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
+ error = kern_ptrace(td, false, PT_LWPINFO, pid, &lwpinfo,
+ sizeof(lwpinfo));
if (error != 0) {
linux_msg(td, "PT_LWPINFO failed with error %d", error);
return (error);
@@ -362,10 +363,10 @@
}
if (LINUX_URO(addr, fs_base))
- return (kern_ptrace(td, PT_GETFSBASE, pid, data, 0));
+ return (kern_ptrace(td, false, PT_GETFSBASE, pid, data, 0));
if (LINUX_URO(addr, gs_base))
- return (kern_ptrace(td, PT_GETGSBASE, pid, data, 0));
- if ((error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0)) != 0)
+ return (kern_ptrace(td, false, PT_GETGSBASE, pid, data, 0));
+ if ((error = kern_ptrace(td, false, PT_GETREGS, pid, &b_reg, 0)) != 0)
return (error);
bsd_to_linux_regset(&b_reg, &reg);
val = *(&reg.r15 + ((uintptr_t)addr / sizeof(reg.r15)));
@@ -427,9 +428,9 @@
}
if (LINUX_URO(addr, fs_base))
- return (kern_ptrace(td, PT_SETFSBASE, pid, data, 0));
+ return (kern_ptrace(td, false, PT_SETFSBASE, pid, data, 0));
if (LINUX_URO(addr, gs_base))
- return (kern_ptrace(td, PT_SETGSBASE, pid, data, 0));
+ return (kern_ptrace(td, false, PT_SETGSBASE, pid, data, 0));
for (i = 0; i < nitems(linux_segregs_off); i++) {
if ((uintptr_t)addr == linux_segregs_off[i].reg) {
if (linux_invalid_selector((uintptr_t)data))
@@ -438,13 +439,13 @@
return (EIO);
}
}
- if ((error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0)) != 0)
+ if ((error = kern_ptrace(td, false, PT_GETREGS, pid, &b_reg, 0)) != 0)
return (error);
bsd_to_linux_regset(&b_reg, &reg);
*(&reg.r15 + ((uintptr_t)addr / sizeof(reg.r15))) = (uint64_t)data;
linux_to_bsd_regset(&b_reg1, &reg);
b_reg1.r_err = b_reg.r_err;
b_reg1.r_trapno = b_reg.r_trapno;
- return (kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0));
+ return (kern_ptrace(td, false, PT_SETREGS, pid, &b_reg, 0));
}
#undef LINUX_URO
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
@@ -1031,6 +1031,7 @@
register_t args[nitems(td->td_sa.args)];
struct ptrace_sc_ret psr;
int ptevents;
+ struct ptrace_child *children;
} r;
union {
struct ptrace_io_desc32 piod;
@@ -1046,6 +1047,7 @@
u_int pscr_args32[nitems(td->td_sa.args)];
void *addr;
int data, error, i;
+ bool pd_mode;
if (!allow_ptrace)
return (ENOSYS);
@@ -1056,6 +1058,9 @@
AUDIT_ARG_VALUE(uap->data);
addr = &r;
data = uap->data;
+ pd_mode = (uap->req & PT_PROCDESC) != 0;
+ uap->req &= ~PT_PROCDESC;
+
switch (uap->req) {
case PT_GET_EVENT_MASK:
case PT_GET_SC_ARGS:
@@ -1173,6 +1178,14 @@
pscr_args[i] = pscr_args32[i];
r.sr.pscr_args = pscr_args;
break;
+ case PT_GET_CHILDREN:
+ if (uap->addr == NULL)
+ addr = NULL;
+ else if (uap->data < 0)
+ error = EINVAL;
+ else
+ addr = &r.children;
+ break;
case PTINTERNAL_FIRST ... PTINTERNAL_LAST:
error = EINVAL;
break;
@@ -1183,7 +1196,7 @@
if (error)
return (error);
- error = kern_ptrace(td, uap->req, uap->pid, addr, data);
+ error = kern_ptrace(td, pd_mode, uap->req, uap->pid, addr, data);
if (error)
return (error);
@@ -1242,6 +1255,13 @@
offsetof(struct ptrace_sc_remote32, pscr_ret),
sizeof(r32.psr));
break;
+ case PT_GET_CHILDREN:
+ if (uap->addr != 0) {
+ error = copyout(r.children, uap->addr,
+ td->td_retval[0] * sizeof(struct ptrace_child));
+ free(r.children, M_TEMP);
+ }
+ break;
}
return (error);
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 */
@@ -670,6 +670,6 @@
{ .sy_narg = AS(pdrfork_args), .sy_call = (sy_call_t *)sys_pdrfork, .sy_auevent = AUE_PDRFORK, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 600 = pdrfork */
{ .sy_narg = AS(freebsd32_pdwait_args), .sy_call = (sy_call_t *)freebsd32_pdwait, .sy_auevent = AUE_PDWAIT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 601 = freebsd32_pdwait */
{ .sy_narg = AS(renameat2_args), .sy_call = (sy_call_t *)sys_renameat2, .sy_auevent = AUE_RENAMEAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 602 = renameat2 */
- { .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
+ { .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
{ .sy_narg = AS(pddupfd_args), .sy_call = (sy_call_t *)sys_pddupfd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 604 = pddupfd */
};
diff --git a/sys/compat/linux/linux_ptrace.c b/sys/compat/linux/linux_ptrace.c
--- a/sys/compat/linux/linux_ptrace.c
+++ b/sys/compat/linux/linux_ptrace.c
@@ -127,7 +127,8 @@
int error;
saved_retval = td->td_retval[0];
- error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
+ error = kern_ptrace(td, false, PT_LWPINFO, pid, &lwpinfo,
+ sizeof(lwpinfo));
td->td_retval[0] = saved_retval;
if (error != 0) {
linux_msg(td, "PT_LWPINFO failed with error %d", error);
@@ -161,7 +162,7 @@
{
int error;
- error = kern_ptrace(td, PT_READ_I, pid, addr, 0);
+ error = kern_ptrace(td, false, PT_READ_I, pid, addr, 0);
if (error == 0)
error = copyout(td->td_retval, data, sizeof(l_int));
else if (error == ENOMEM)
@@ -222,7 +223,8 @@
pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACEEXIT;
}
- return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask)));
+ return (kern_ptrace(td, false, PT_SET_EVENT_MASK, pid, &mask,
+ sizeof(mask)));
}
static int
@@ -240,7 +242,8 @@
l_siginfo_t l_siginfo;
int error, sig;
- error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
+ error = kern_ptrace(td, false, PT_LWPINFO, pid, &lwpinfo,
+ sizeof(lwpinfo));
if (error != 0) {
linux_msg(td, "PT_LWPINFO failed with error %d", error);
return (error);
@@ -266,7 +269,7 @@
struct linux_pt_regset l_regset;
int error;
- error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
+ error = kern_ptrace(td, false, PT_GETREGS, pid, &b_reg, 0);
if (error != 0)
return (error);
@@ -290,7 +293,7 @@
if (error != 0)
return (error);
linux_to_bsd_regset(&b_reg, &l_regset);
- error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0);
+ error = kern_ptrace(td, false, PT_SETREGS, pid, &b_reg, 0);
return (error);
}
@@ -309,7 +312,7 @@
return (error);
}
- error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
+ error = kern_ptrace(td, false, PT_GETREGS, pid, &b_reg, 0);
if (error != 0)
return (error);
@@ -351,7 +354,7 @@
return (error);
}
- error = kern_ptrace(td, PT_GETFPREGS, pid, &b_fpreg, 0);
+ error = kern_ptrace(td, false, PT_GETFPREGS, pid, &b_fpreg, 0);
if (error != 0)
return (error);
@@ -387,13 +390,15 @@
return (error);
}
- error = kern_ptrace(td, PT_GETXSTATE_INFO, pid, &info, sizeof(info));
+ error = kern_ptrace(td, false, PT_GETXSTATE_INFO, pid, &info,
+ sizeof(info));
if (error != 0)
return (error);
xstate = malloc(info.xsave_len, M_LINUX, M_WAITOK | M_ZERO);
- error = kern_ptrace(td, PT_GETXSTATE, pid, xstate, info.xsave_len);
+ error = kern_ptrace(td, false, PT_GETXSTATE, pid, xstate,
+ info.xsave_len);
if (error != 0) {
free(xstate, M_LINUX);
return (error);
@@ -454,7 +459,8 @@
struct syscall_info si;
int error;
- error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
+ error = kern_ptrace(td, false, PT_LWPINFO, pid, &lwpinfo,
+ sizeof(lwpinfo));
if (error != 0) {
linux_msg(td, "PT_LWPINFO failed with error %d", error);
return (error);
@@ -465,7 +471,7 @@
if (lwpinfo.pl_flags & PL_FLAG_SCE) {
si.op = LINUX_PTRACE_SYSCALL_INFO_ENTRY;
si.entry.nr = lwpinfo.pl_syscall_code;
- error = kern_ptrace(td, PTLINUX_GET_SC_ARGS, pid,
+ error = kern_ptrace(td, false, PTLINUX_GET_SC_ARGS, pid,
si.entry.args, sizeof(si.entry.args));
if (error != 0) {
linux_msg(td,
@@ -474,7 +480,8 @@
}
} else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
si.op = LINUX_PTRACE_SYSCALL_INFO_EXIT;
- error = kern_ptrace(td, PT_GET_SC_RET, pid, &sr, sizeof(sr));
+ error = kern_ptrace(td, false, PT_GET_SC_RET, pid, &sr,
+ sizeof(sr));
if (error != 0) {
linux_msg(td, "PT_GET_SC_RET failed with error %d",
@@ -505,7 +512,7 @@
si.op = LINUX_PTRACE_SYSCALL_INFO_NONE;
}
- error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
+ error = kern_ptrace(td, false, PT_GETREGS, pid, &b_reg, 0);
if (error != 0)
return (error);
@@ -534,7 +541,7 @@
switch (uap->req) {
case LINUX_PTRACE_TRACEME:
- error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0);
+ error = kern_ptrace(td, false, PT_TRACE_ME, 0, 0, 0);
break;
case LINUX_PTRACE_PEEKTEXT:
case LINUX_PTRACE_PEEKDATA:
@@ -552,13 +559,14 @@
break;
case LINUX_PTRACE_POKETEXT:
case LINUX_PTRACE_POKEDATA:
- error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data);
+ error = kern_ptrace(td, false, PT_WRITE_D, pid, addr,
+ uap->data);
if (error != 0)
goto out;
/*
* Linux expects this syscall to write 64 bits, not 32.
*/
- error = kern_ptrace(td, PT_WRITE_D, pid,
+ error = kern_ptrace(td, false, PT_WRITE_D, pid,
(void *)(uap->addr + 4), uap->data >> 32);
break;
case LINUX_PTRACE_POKEUSER:
@@ -568,16 +576,17 @@
error = map_signum(uap->data, &sig);
if (error != 0)
break;
- error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig);
+ error = kern_ptrace(td, false, PT_CONTINUE, pid, (void *)1,
+ sig);
break;
case LINUX_PTRACE_KILL:
- error = kern_ptrace(td, PT_KILL, pid, addr, uap->data);
+ error = kern_ptrace(td, false, PT_KILL, pid, addr, uap->data);
break;
case LINUX_PTRACE_SINGLESTEP:
error = map_signum(uap->data, &sig);
if (error != 0)
break;
- error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig);
+ error = kern_ptrace(td, false, PT_STEP, pid, (void *)1, sig);
break;
case LINUX_PTRACE_GETREGS:
error = linux_ptrace_getregs(td, pid, (void *)uap->data);
@@ -586,19 +595,21 @@
error = linux_ptrace_setregs(td, pid, (void *)uap->data);
break;
case LINUX_PTRACE_ATTACH:
- error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
+ error = kern_ptrace(td, false, PT_ATTACH, pid, addr,
+ uap->data);
break;
case LINUX_PTRACE_DETACH:
error = map_signum(uap->data, &sig);
if (error != 0)
break;
- error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig);
+ error = kern_ptrace(td, false, PT_DETACH, pid, (void *)1, sig);
break;
case LINUX_PTRACE_SYSCALL:
error = map_signum(uap->data, &sig);
if (error != 0)
break;
- error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig);
+ error = kern_ptrace(td, false, PT_SYSCALL, pid, (void *)1,
+ sig);
break;
case LINUX_PTRACE_SETOPTIONS:
error = linux_ptrace_setoptions(td, pid, uap->data);
diff --git a/sys/i386/linux/linux_ptrace_machdep.c b/sys/i386/linux/linux_ptrace_machdep.c
--- a/sys/i386/linux/linux_ptrace_machdep.c
+++ b/sys/i386/linux/linux_ptrace_machdep.c
@@ -259,13 +259,13 @@
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
case PTRACE_KILL:
- error = kern_ptrace(td, req, pid, addr, uap->data);
+ error = kern_ptrace(td, false, req, pid, addr, uap->data);
break;
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA: {
/* need to preserve return value */
int rval = td->td_retval[0];
- error = kern_ptrace(td, req, pid, addr, 0);
+ error = kern_ptrace(td, false, req, pid, addr, 0);
if (error == 0)
error = copyout(td->td_retval, (void *)uap->data,
sizeof(l_int));
@@ -273,20 +273,20 @@
break;
}
case PTRACE_DETACH:
- error = kern_ptrace(td, PT_DETACH, pid, (void *)1,
+ error = kern_ptrace(td, false, PT_DETACH, pid, (void *)1,
map_signum(uap->data));
break;
case PTRACE_SINGLESTEP:
case PTRACE_CONT:
- error = kern_ptrace(td, req, pid, (void *)1,
+ error = kern_ptrace(td, false, req, pid, (void *)1,
map_signum(uap->data));
break;
case PTRACE_ATTACH:
- error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
+ error = kern_ptrace(td, false, PT_ATTACH, pid, addr, uap->data);
break;
case PTRACE_GETREGS:
/* Linux is using data where FreeBSD is using addr */
- error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
+ error = kern_ptrace(td, false, PT_GETREGS, pid, &u.bsd_reg, 0);
if (error == 0) {
map_regs_to_linux(&u.bsd_reg, &r.reg);
error = copyout(&r.reg, (void *)uap->data,
@@ -303,7 +303,8 @@
break;
case PTRACE_GETFPREGS:
/* Linux is using data where FreeBSD is using addr */
- error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0);
+ error = kern_ptrace(td, false, PT_GETFPREGS, pid,
+ &u.bsd_fpreg, 0);
if (error == 0) {
map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg);
error = copyout(&r.fpreg, (void *)uap->data,
@@ -315,7 +316,7 @@
error = copyin((void *)uap->data, &r.fpreg, sizeof(r.fpreg));
if (error == 0) {
map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg);
- error = kern_ptrace(td, PT_SETFPREGS, pid,
+ error = kern_ptrace(td, false, PT_SETFPREGS, pid,
&u.bsd_fpreg, 0);
}
break;
@@ -409,7 +410,8 @@
* as necessary.
*/
if (uap->addr < sizeof(struct linux_pt_reg)) {
- error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
+ error = kern_ptrace(td, false, PT_GETREGS, pid,
+ &u.bsd_reg, 0);
if (error != 0)
break;
@@ -424,7 +426,8 @@
(l_int)uap->data;
map_regs_from_linux(&u.bsd_reg, &r.reg);
- error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
+ error = kern_ptrace(td, false; PT_SETREGS, pid,
+ &u.bsd_reg, 0);
}
/*
@@ -432,8 +435,8 @@
*/
if (uap->addr >= LINUX_DBREG_OFFSET &&
uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) {
- error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg,
- 0);
+ error = kern_ptrace(td, false, PT_GETDBREGS, pid,
+ &u.bsd_dbreg, 0);
if (error != 0)
break;
@@ -447,7 +450,7 @@
*(l_int *)((char *)&u.bsd_dbreg + uap->addr) =
uap->data;
- error = kern_ptrace(td, PT_SETDBREGS, pid,
+ error = kern_ptrace(td, false, PT_SETDBREGS, pid,
&u.bsd_dbreg, 0);
}
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 */
@@ -669,6 +669,6 @@
{ .sy_narg = AS(pdrfork_args), .sy_call = (sy_call_t *)sys_pdrfork, .sy_auevent = AUE_PDRFORK, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 600 = pdrfork */
{ .sy_narg = AS(pdwait_args), .sy_call = (sy_call_t *)sys_pdwait, .sy_auevent = AUE_PDWAIT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 601 = pdwait */
{ .sy_narg = AS(renameat2_args), .sy_call = (sy_call_t *)sys_renameat2, .sy_auevent = AUE_RENAMEAT, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 602 = renameat2 */
- { .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
+ { .sy_narg = AS(pdopenpid_args), .sy_call = (sy_call_t *)sys_pdopenpid, .sy_auevent = AUE_PDOPENPID, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 603 = pdopenpid */
{ .sy_narg = AS(pddupfd_args), .sy_call = (sy_call_t *)sys_pddupfd, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 604 = pddupfd */
};
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
@@ -720,6 +720,9 @@
}
} else
PROC_LOCK(p->p_pptr);
+ nq = proc_realparent(p);
+ if (p->p_pptr != nq)
+ wakeup(nq);
sx_xunlock(&proctree_lock);
if (signal_parent == 1) {
@@ -982,13 +985,16 @@
* lock as part of its work.
*/
void
-proc_reap(struct thread *td, struct proc *p, int *status, int options)
+proc_reap(struct thread *td, struct proc *p, int *status, int options,
+ int zombieref)
{
struct proc *q, *t;
sx_assert(&proctree_lock, SA_XLOCKED);
PROC_LOCK_ASSERT(p, MA_OWNED);
KASSERT(p->p_state == PRS_ZOMBIE, ("proc_reap: !PRS_ZOMBIE"));
+ MPASS(zombieref == P2_ZOMBIEREF_PARENT ||
+ zombieref == P2_ZOMBIEREF_PROCDESC);
mtx_spin_wait_unlocked(&p->p_slock);
@@ -1006,6 +1012,14 @@
return;
}
+ p->p_flag2 &= ~zombieref;
+ if ((p->p_flag2 & (P2_ZOMBIEREF_PROCDESC | P2_ZOMBIEREF_PARENT)) !=
+ 0) {
+ PROC_UNLOCK(p);
+ sx_xunlock(&proctree_lock);
+ return;
+ }
+
PROC_LOCK(q);
sigqueue_take(p->p_ksi);
PROC_UNLOCK(q);
@@ -1187,7 +1201,7 @@
switch (idtype) {
case P_ALL:
- if (p->p_procdesc == NULL ||
+ if ((p->p_flag2 & P2_ZOMBIEREF_PARENT) != 0 ||
(p->p_pptr == td->td_proc &&
(p->p_flag & P_TRACED) != 0)) {
break;
@@ -1241,14 +1255,19 @@
return (0);
}
- if (p_canwait(td, p)) {
+ if (p_canwait(td, p) != 0 ||
+ ((options & WEXITED) == 0 && p->p_state == PRS_ZOMBIE) ||
+ (p->p_flag2 & P2_ZOMBIEREF_PARENT) == 0) {
PROC_UNLOCK(p);
return (0);
}
- if ((options & WEXITED) == 0 && p->p_state == PRS_ZOMBIE) {
- PROC_UNLOCK(p);
- return (0);
+ if (check_only) {
+ if ((p->p_flag2 & P2_ORPHAN_REPORTED) != 0) {
+ PROC_UNLOCK(p);
+ return (0);
+ }
+ p->p_flag2 |= P2_ORPHAN_REPORTED;
}
/*
@@ -1275,7 +1294,7 @@
wait_fill_wrusage(p, wrusage);
if (p->p_state == PRS_ZOMBIE && !check_only) {
- proc_reap(td, p, status, options);
+ proc_reap(td, p, status, options, P2_ZOMBIEREF_PARENT);
return (-1);
}
return (1);
@@ -1519,8 +1538,9 @@
KASSERT(ret != -1, ("reaped an orphan (pid %d)",
(int)td->td_retval[0]));
PROC_UNLOCK(p);
- nfound++;
- break;
+ td->td_retval[0] = p->p_pid;
+ sx_xunlock(&proctree_lock);
+ return (0);
}
}
}
@@ -1583,7 +1603,8 @@
error = p_canwait(td, p);
if (error != 0)
break;
- if ((options & WEXITED) == 0 && p->p_state == PRS_ZOMBIE) {
+ if (((options & WEXITED) == 0 && p->p_state == PRS_ZOMBIE) ||
+ (p->p_flag2 & P2_ZOMBIEREF_PROCDESC) == 0) {
error = ESRCH;
break;
}
@@ -1592,7 +1613,7 @@
wait_fill_wrusage(p, wrusage);
if (p->p_state == PRS_ZOMBIE) {
- proc_reap(td, p, status, options);
+ proc_reap(td, p, status, options, P2_ZOMBIEREF_PROCDESC);
goto exit_unlocked;
}
@@ -1665,6 +1686,14 @@
child->p_pptr = parent;
if (set_oppid)
child->p_oppid = parent->p_pid;
+
+ /*
+ * When reparenting the child to the reaper, re-enable
+ * waitpid(2) for it, so that the zombie can be collected.
+ */
+ if ((child->p_flag & P_TRACED) == 0 && child->p_reaper == parent &&
+ (child->p_flag2 & P2_ZOMBIEREF_PARENT) == 0)
+ child->p_flag2 |= P2_ZOMBIEREF_PARENT;
}
static void
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_PTRACE_CAP) == 0)
+ cap_rights_clear(&fcaps.fc_rights, CAP_PTRACE);
+ 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_PTRACE_CAP) == 0)
+ cap_rights_clear(&fcaps.fc_rights, CAP_PTRACE);
+ fr.fr_pd_fcaps = &fcaps;
error = fork1(td, &fr);
if (error == 0) {
td->td_retval[0] = pid;
@@ -547,6 +558,8 @@
P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC | P2_NO_NEW_PRIVS |
P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC | P2_LOGSIGEXIT_CTL |
P2_LOGSIGEXIT_ENABLE);
+ p2->p_flag2 |= (fr->fr_flags & RFPROCDESC) != 0 ?
+ P2_ZOMBIEREF_PROCDESC : P2_ZOMBIEREF_PARENT;
p2->p_swtick = ticks;
if (p1->p_flag & P_PROFIL)
startprofclock(p2);
@@ -1050,8 +1063,10 @@
if (flags & RFPROCDESC) {
error = procdesc_falloc(td, &fp_procdesc, fr->fr_pd_fd,
fr->fr_pd_flags, fr->fr_pd_fcaps);
- if (error != 0)
+ if (error != 0) {
+ filecaps_free(fr->fr_pd_fcaps);
goto fail2;
+ }
AUDIT_ARG_FD(*fr->fr_pd_fd);
}
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>
@@ -2399,12 +2400,19 @@
return (EINVAL);
name = (int *)arg1;
+ sx_slock(&proctree_lock);
error = pget((pid_t)name[0], PGET_CANSEE, &p);
if (error != 0)
- return (error);
- sv_name = p->p_sysent->sv_name;
+ goto out;
+ error = p_canopen(curthread, p);
+ if (error == 0)
+ sv_name = p->p_sysent->sv_name;
PROC_UNLOCK(p);
- return (sysctl_handle_string(oidp, sv_name, 0, req));
+out:
+ sx_sunlock(&proctree_lock);
+ if (error == 0)
+ error = sysctl_handle_string(oidp, sv_name, 0, req);
+ return (error);
}
#ifdef KINFO_OVMENTRY_SIZE
@@ -3430,7 +3438,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_prot.c b/sys/kern/kern_prot.c
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -46,14 +46,13 @@
* System calls related to processes and protection
*/
-#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_inet6.h"
-#include <sys/param.h>
#include <sys/systm.h>
#include <sys/abi_compat.h>
#include <sys/acct.h>
+#include <sys/capsicum.h>
#include <sys/imgact.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
@@ -2309,6 +2308,24 @@
return (uidsubset && grpsubset);
}
+int
+p_canopen(struct thread *td, struct proc *p)
+{
+#ifdef INVARIANTS
+ if (IN_CAPABILITY_MODE(td))
+ sx_assert(&proctree_lock, SX_LOCKED);
+#endif
+
+ /*
+ * Allow implicit parent in cap mode: either real parent or
+ * debugger can open pid.
+ */
+ if (!IN_CAPABILITY_MODE(td) || (allow_ptrace_in_cap_mode &&
+ (td->td_proc == p->p_pptr || p->p_oppid == td->td_proc->p_pid)))
+ return (0);
+ return (ECAPMODE);
+}
+
/*-
* Determine whether td may debug p.
* Returns: 0 for permitted, an errno value otherwise
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,7 @@
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_rights = CAP_RIGHTS_INITIALIZER(CAP_PTRACE);
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
@@ -307,6 +307,7 @@
PROCDESC_UNLOCK(pd);
pd->pd_proc = NULL;
p->p_procdesc = NULL;
+ p->p_flag2 &= ~P2_ZOMBIEREF_PROCDESC;
procdesc_free(pd);
return (true);
}
@@ -316,7 +317,7 @@
/* Wakeup all waiters for this procdesc' process exit. */
wakeup(&p->p_procdesc);
- return (false);
+ return ((p->p_flag2 & P2_ZOMBIEREF_PARENT) != 0);
}
void
@@ -418,7 +419,7 @@
* process's reference to the process descriptor when it
* calls back into procdesc_reap().
*/
- proc_reap(curthread, p, NULL, 0);
+ proc_reap(curthread, p, NULL, 0, P2_ZOMBIEREF_PROCDESC);
} else if (pd->pd_fpcount == 0) /* last procdesc */ {
/*
* If the process is not yet dead, we need to kill it,
@@ -429,11 +430,16 @@
*/
pd->pd_proc = NULL;
p->p_procdesc = NULL;
+ p->p_flag2 &= ~P2_ZOMBIEREF_PROCDESC;
pd->pd_pid = -1;
procdesc_free(pd);
- /* Failed finstall() should not cause reaping. */
- if ((fp->f_pdflags & F_PD_NOFINSTALL) == 0) {
+ /*
+ * A reference for waitpid() or failed
+ * finstall() should not cause reaping.
+ */
+ if ((fp->f_pdflags & F_PD_NOFINSTALL) == 0 ||
+ (p->p_flag2 & P2_ZOMBIEREF_PARENT) == 0) {
/*
* Next, reparent it to its reaper
* (usually init(8)) so that there's
@@ -493,16 +499,25 @@
procdesc_kqops_event(struct knote *kn, long hint)
{
struct procdesc *pd;
+ struct proc *p;
u_int event;
pd = kn->kn_fp->f_data;
if (hint == 0) {
/*
* Initial test after registration. Generate notes in
- * case the process already terminated before registration.
+ * case the process already terminated before
+ * registration or is stopped or traced with an event
+ * pending.
*/
- event = (pd->pd_flags & PDF_EXITED) != 0 ? (NOTE_EXIT |
- NOTE_PDSIGCHLD) : 0;
+ p = pd->pd_proc;
+ if ((pd->pd_flags & PDF_EXITED) != 0)
+ event = NOTE_EXIT | NOTE_PDSIGCHLD;
+ else if ((atomic_load_int(&p->p_flag) & (P_STOPPED_SIG |
+ P_STOPPED_TRACE)) != 0)
+ event = NOTE_PDSIGCHLD;
+ else
+ event = 0;
} else {
/* Mask off extra data. */
event = (u_int)hint & NOTE_PCTRLMASK;
@@ -631,8 +646,15 @@
PROC_UNLOCK(p);
return (EBUSY);
}
+ error = p_canopen(td, p);
+ if (error != 0) {
+ PROC_UNLOCK(p);
+ return (error);
+ }
+
pd = p->p_procdesc;
if (pd != NULL) {
+ MPASS((p->p_flag2 & P2_ZOMBIEREF_PROCDESC) != 0);
refcount_acquire(&pd->pd_refcount);
PROCDESC_LOCK(pd);
MPASS(pd->pd_fpcount > 0);
@@ -644,6 +666,8 @@
pd->pd_proc = p;
pd->pd_pid = p->p_pid;
p->p_procdesc = pd;
+ MPASS((p->p_flag2 & P2_ZOMBIEREF_PROCDESC) == 0);
+ p->p_flag2 |= P2_ZOMBIEREF_PROCDESC;
}
procdesc_finit(pd, fp);
PROC_UNLOCK(p);
@@ -655,6 +679,7 @@
{
struct file *fp;
struct procdesc *pdf;
+ struct filecaps fcaps;
int error, fd, fflags;
error = falloc_noinstall(td, &fp);
@@ -664,13 +689,16 @@
pdf = procdesc_alloc(flags);
if ((flags & PD_DAEMON) != 0)
fp->f_pdflags |= F_PD_NOKILL;
+ filecaps_fill(&fcaps);
+ if ((flags & PD_PTRACE_CAP) == 0)
+ cap_rights_clear(&fcaps.fc_rights, CAP_PTRACE);
sx_xlock(&proctree_lock);
error = pdopenpid1(td, pid, &pdf, fp);
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 {
@@ -679,6 +707,7 @@
* return file descriptor to userspace.
*/
fp->f_pdflags |= F_PD_NOKILL | F_PD_NOFINSTALL;
+ filecaps_free(&fcaps);
}
}
fdrop(fp, td);
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>
@@ -733,10 +735,12 @@
syscallarg_t args[nitems(td->td_sa.args)];
struct ptrace_sc_ret psr;
int ptevents;
+ struct ptrace_child *children;
} r;
syscallarg_t pscr_args[nitems(td->td_sa.args)];
void *addr;
int error;
+ bool pd_mode;
if (!allow_ptrace)
return (ENOSYS);
@@ -746,6 +750,9 @@
AUDIT_ARG_CMD(uap->req);
AUDIT_ARG_VALUE(uap->data);
addr = &r;
+ pd_mode = (uap->req & PT_PROCDESC) != 0;
+ uap->req &= ~PT_PROCDESC;
+
switch (uap->req) {
case PT_GET_EVENT_MASK:
case PT_LWPINFO:
@@ -816,6 +823,14 @@
break;
r.sr.pscr_args = pscr_args;
break;
+ case PT_GET_CHILDREN:
+ if (uap->addr == NULL)
+ addr = NULL;
+ else if (uap->data < 0)
+ error = EINVAL;
+ else
+ addr = &r.children;
+ break;
case PTINTERNAL_FIRST ... PTINTERNAL_LAST:
error = EINVAL;
break;
@@ -826,7 +841,7 @@
if (error != 0)
return (error);
- error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data);
+ error = kern_ptrace(td, pd_mode, uap->req, uap->pid, addr, uap->data);
if (error != 0)
return (error);
@@ -870,6 +885,13 @@
offsetof(struct ptrace_sc_remote, pscr_ret),
sizeof(r.sr.pscr_ret));
break;
+ case PT_GET_CHILDREN:
+ if (uap->addr != NULL) {
+ error = copyout(r.children, uap->addr,
+ td->td_retval[0] * sizeof(struct ptrace_child));
+ free(r.children, M_TEMP);
+ }
+ break;
}
return (error);
@@ -965,6 +987,56 @@
return (0);
}
+static int
+ptrace_count_children(struct thread *td, struct proc *p)
+{
+ struct proc *pp;
+ int error, num;
+
+ sx_assert(&proctree_lock, SX_LOCKED);
+ num = 0;
+ LIST_FOREACH(pp, &p->p_children, p_sibling) {
+ PROC_LOCK(pp);
+ error = p_cansee(td, pp);
+ PROC_UNLOCK(pp);
+ if (error != 0)
+ continue;
+ num++;
+ }
+ LIST_FOREACH(pp, &p->p_orphans, p_orphan) {
+ PROC_LOCK(pp);
+ error = p_cansee(td, pp);
+ PROC_UNLOCK(pp);
+ if (error != 0)
+ continue;
+ num++;
+ }
+ return (num);
+}
+
+static bool
+ptrace_report_child(struct thread *td, struct proc *p, struct proc *pp,
+ struct ptrace_child *ptc)
+{
+ sx_assert(&proctree_lock, SX_LOCKED);
+
+ PROC_LOCK(pp);
+ if (p_cansee(td, pp) != 0) {
+ PROC_UNLOCK(pp);
+ return (false);
+ }
+ ptc->pid = pp->p_pid;
+ if ((pp->p_flag & P_TRACED) != 0) {
+ ptc->flags |= PTCHLD_TRACED;
+ if (pp->p_pptr == td->td_proc)
+ ptc->flags |= PTCHLD_TRACED_BY_ME;
+ }
+ if ((pp->p_flag & P_WEXIT) != 0)
+ ptc->flags |= PTCHLD_EXITED;
+ PROC_UNLOCK(pp);
+ return (true);
+}
+
static struct thread *
ptrace_sel_coredump_thread(struct proc *p)
{
@@ -980,8 +1052,14 @@
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)
+kern_ptrace(struct thread *td, bool pd_mode, int req, int pid, void *addr,
+ int data)
{
struct iovec iov;
struct uio uio;
@@ -995,7 +1073,9 @@
struct ptrace_coredump *pc;
struct thr_coredump_req *tcq;
struct thr_syscall_req *tsr;
- int error, num, tmp;
+ struct file *pfp;
+ struct ptrace_child *children, *ptc;
+ int error, num, num1, tmp;
lwpid_t tid = 0, *buf;
#ifdef COMPAT_FREEBSD32
int wrap32 = 0, safe = 0;
@@ -1005,6 +1085,11 @@
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 && !pd_mode)))
+ return (ECAPMODE);
/* Lock proctree before locking the process. */
switch (req) {
@@ -1021,6 +1106,7 @@
case PT_SET_EVENT_MASK:
case PT_DETACH:
case PT_GET_SC_ARGS:
+ case PT_GET_CHILDREN:
sx_xlock(&proctree_lock);
proctree_locked = true;
break;
@@ -1032,7 +1118,17 @@
p = td->td_proc;
PROC_LOCK(p);
} else {
- if (pid <= PID_MAX) {
+ if (pd_mode) {
+ if (!proctree_locked)
+ sx_slock(&proctree_lock);
+ error = fget_procdesc(td, pid, &cap_ptrace_rights,
+ &pfp, NULL, &p);
+ if (!proctree_locked)
+ sx_sunlock(&proctree_lock);
+ 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);
@@ -1132,8 +1228,14 @@
/* Allow thread to clear single step for itself */
if (td->td_tid == tid)
break;
+ goto default_check;
- /* FALLTHROUGH */
+ case PT_GET_CHILDREN:
+ if (p == curp)
+ break;
+ goto default_check;
+
+default_check:
default:
/*
* Check for ptrace eligibility before waiting for
@@ -1864,6 +1966,50 @@
free(tsr, M_TEMP);
break;
+ case PT_GET_CHILDREN:
+ PROC_UNLOCK(p);
+get_children_repeat:
+ num = ptrace_count_children(td, p);
+ if (addr == NULL) {
+ td->td_retval[0] = num;
+ PROC_LOCK(p);
+ break;
+ }
+ if (data < num * sizeof(struct ptrace_child)) {
+ error = ENOMEM;
+ PROC_LOCK(p);
+ break;
+ }
+ sx_xunlock(&proctree_lock);
+ children = mallocarray(num, sizeof(struct ptrace_child),
+ M_TEMP, M_WAITOK | M_ZERO);
+ sx_xlock(&proctree_lock);
+ num1 = ptrace_count_children(td, p);
+ if (num1 > num) {
+ free(children, M_TEMP);
+ goto get_children_repeat;
+ }
+ num = num1;
+ num1 = 0;
+ td->td_retval[0] = num;
+ LIST_FOREACH(pp, &p->p_children, p_sibling) {
+ MPASS(num1 < num);
+ ptc = &children[num1];
+ if (ptrace_report_child(td, p, pp, ptc))
+ num1++;
+ }
+ LIST_FOREACH(pp, &p->p_orphans, p_orphan) {
+ MPASS(num1 < num);
+ ptc = &children[num1];
+ if (ptrace_report_child(td, p, pp, ptc)) {
+ num1++;
+ ptc->flags |= PTCHLD_ORPHAN;
+ }
+ }
+ *(struct ptrace_child **)addr = children;
+ PROC_LOCK(p);
+ break;
+
default:
#ifdef __HAVE_PTRACE_MACHDEP
if (req >= PT_FIRSTMACH) {
@@ -1886,8 +2032,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,
@@ -3429,7 +3429,7 @@
int flags
);
}
-603 AUE_PDOPENPID STD {
+603 AUE_PDOPENPID STD|CAPENABLED {
int pdopenpid(
pid_t pid,
int flags
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_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_PROCDESC) */
+#define CAP_PTRACE 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,8 +335,8 @@
#define CAP_UNUSED1_56 CAPRIGHT(1, 0x0080000000000000ULL)
#define CAP_UNUSED1_57 CAPRIGHT(1, 0x0100000000000000ULL)
-/* All used bits for index 1. */
-#define CAP_ALL1 CAPRIGHT(1, 0x0000000000FFFFFFULL)
+/* All default bits for index 1. */
+#define CAP_ALL1 CAPRIGHT(1, 0x0000000001FFFFFFULL)
/* Backward compatibility. */
#define CAP_POLL_EVENT CAP_EVENT
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/proc.h b/sys/sys/proc.h
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -903,6 +903,9 @@
#define P2_LOGSIGEXIT_ENABLE 0x00800000 /* Disable logging on sigexit */
#define P2_LOGSIGEXIT_CTL 0x01000000 /* Override kern.logsigexit */
#define P2_HWT 0x02000000 /* Process is using HWT. */
+#define P2_ZOMBIEREF_PARENT 0x04000000
+#define P2_ZOMBIEREF_PROCDESC 0x08000000
+#define P2_ORPHAN_REPORTED 0x10000000
/* Flags protected by proctree_lock, kept in p_treeflags. */
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
@@ -1186,6 +1189,7 @@
void maybe_yield(void);
void mi_switch(int flags);
int p_candebug(struct thread *td, struct proc *p);
+int p_canopen(struct thread *td, struct proc *p);
int p_cansee(struct thread *td, struct proc *p);
int p_cansched(struct thread *td, struct proc *p);
int p_cansignal(struct thread *td, struct proc *p, int signum);
@@ -1205,7 +1209,8 @@
void proc_linkup0(struct proc *p, struct thread *td);
void proc_linkup(struct proc *p, struct thread *td);
struct proc *proc_realparent(struct proc *child);
-void proc_reap(struct thread *td, struct proc *p, int *status, int options);
+void proc_reap(struct thread *td, struct proc *p, int *status, int options,
+ int zombieref);
void proc_reparent(struct proc *child, struct proc *newparent, bool set_oppid);
void proc_set_p2_wexit(struct proc *p);
void proc_set_traced(struct proc *p, bool stop);
diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h
--- a/sys/sys/procdesc.h
+++ b/sys/sys/procdesc.h
@@ -153,7 +153,8 @@
*/
#define PD_DAEMON 0x00000001 /* Don't exit when procdesc closes. */
#define PD_CLOEXEC 0x00000002 /* Close file descriptor on exec. */
+#define PD_PTRACE_CAP 0x00000004 /* Allow PT_PROCDESC in cap mode. */
-#define PD_ALLOWED_AT_FORK (PD_DAEMON | PD_CLOEXEC)
+#define PD_ALLOWED_AT_FORK (PD_DAEMON | PD_CLOEXEC | PD_PTRACE_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
@@ -36,6 +36,8 @@
#include <sys/param.h>
#include <machine/reg.h>
+#define PT_PROCDESC 0x80000000 /* pid is procdesc */
+
#define PT_TRACE_ME 0 /* child declares it's being traced */
#define PT_READ_I 1 /* read word in child's I space */
#define PT_READ_D 2 /* read word in child's D space */
@@ -86,6 +88,7 @@
#define PT_SETREGSET 43 /* Set a target register set */
#define PT_SC_REMOTE 44 /* Execute a syscall */
#define PT_SET_SC_RET 45 /* Set (fake) syscall results */
+#define PT_GET_CHILDREN 46 /* Report children */
#define PT_FIRSTMACH 64 /* for machine-specific requests */
#define PT_LASTMACH 127
@@ -206,6 +209,17 @@
syscallarg_t *pscr_args;
};
+#define PTCHLD_TRACED 0x00000001
+#define PTCHLD_TRACED_BY_ME 0x00000002
+#define PTCHLD_EXITED 0x00000004
+#define PTCHLD_ORPHAN 0x00000008
+
+struct ptrace_child {
+ pid_t pid;
+ int flags;
+ uint64ptr_t rsrv[3];
+};
+
#ifdef _KERNEL
#include <sys/proc.h>
@@ -281,6 +295,7 @@
void ptrace_unsuspend(struct proc *p);
extern bool allow_ptrace;
+extern bool allow_ptrace_in_cap_mode;
#else /* !_KERNEL */
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -308,8 +308,8 @@
int kern_preadv(struct thread *td, int fd, struct uio *auio, off_t offset);
int kern_pselect(struct thread *td, int nd, fd_set *in, fd_set *ou,
fd_set *ex, struct timeval *tvp, sigset_t *uset, int abi_nfdbits);
-int kern_ptrace(struct thread *td, int req, pid_t pid, void *addr,
- int data);
+int kern_ptrace(struct thread *td, bool pd_mode, int req, pid_t pid,
+ void *addr, int data);
int kern_pwrite(struct thread *td, int fd, const void *buf, size_t nbyte,
off_t offset);
int kern_pwritev(struct thread *td, int fd, struct uio *auio, off_t offset);
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>
@@ -57,8 +58,8 @@
usage(void)
{
fprintf(stderr, "%s\n%s\n",
- "usage: truss [-cfaedDHS] [-o file] [-s strsize] -p pid",
- " truss [-cfaedDHS] [-o file] [-s strsize] command [args]");
+ "usage: truss [-cfaedyYDHS] [-o file] [-s strsize] -p pid",
+ " truss [-cfaedyYDHS] [-o file] [-s strsize] command [args]");
exit(1);
}
@@ -85,7 +86,7 @@
trussinfo->strsize = 32;
trussinfo->curthread = NULL;
LIST_INIT(&trussinfo->proclist);
- while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) {
+ while ((c = getopt(ac, av, "p:o:facedyYDs:SH")) != -1) {
switch (c) {
case 'p': /* specified pid */
pid = atoi(optarg);
@@ -121,6 +122,13 @@
if (errstr)
errx(1, "maximum string size is %s: %s", errstr, optarg);
break;
+ case 'y':
+ trussinfo->cap_mode = true;
+ break;
+ case 'Y':
+ trussinfo->cap_mode = true;
+ trussinfo->force_cap_mode = true;
+ break;
case 'S': /* Don't trace signals */
trussinfo->flags |= NOSIGS;
break;
@@ -146,6 +154,12 @@
err(1, "cannot open %s", fname);
}
+ if (trussinfo->cap_mode) {
+ trussinfo->pdkq = kqueue();
+ if (trussinfo->pdkq == -1)
+ err(1, "kqueue");
+ }
+
/*
* If truss starts the process itself, it will ignore some signals --
* they should be passed off to the process, which may or may not
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,9 @@
static void enter_syscall(struct trussinfo *, struct threadinfo *,
struct ptrace_lwpinfo *);
-static void new_proc(struct trussinfo *, pid_t, lwpid_t);
-
+static bool new_proc(struct trussinfo *, int, pid_t, lwpid_t, bool, bool);
+static void new_proc_register_kev(struct trussinfo *info, int pfd);
+static struct procinfo *find_proc(struct trussinfo *info, pid_t pid);
static struct procabi freebsd = {
.type = "FreeBSD",
@@ -138,6 +143,59 @@
#endif
};
+#if 0
+static int
+t_ptrace(struct trussinfo *info, int req, int pid, caddr_t addr, int data)
+{
+ return (ptrace(req | (info->cap_mode ? PT_PROCDESC : 0), pid,
+ addr, data));
+}
+#endif
+
+static int
+truss_ptrace(struct trussinfo *info, int req, struct procinfo *p, caddr_t addr,
+ int data)
+{
+ if (info->cap_mode)
+ return (ptrace(req | PT_PROCDESC, p->pfd, addr, data));
+ return (ptrace(req, p->pid, addr, data));
+}
+
+static int
+t_wait(struct trussinfo *info, int pid, int *status, int wflags)
+{
+ if (info->cap_mode)
+ return (pdwait(pid, status, wflags, NULL, NULL));
+ return (waitpid(pid, status, wflags));
+}
+
+static int
+truss_wait(struct trussinfo *info, struct procinfo *p, int *status,
+ int wflags)
+{
+ if (info->cap_mode)
+ return (pdwait(p->pfd, status, wflags, NULL, NULL));
+ return (waitpid(p->pid, status, wflags));
+}
+
+#if 0
+static int
+t_kill(struct trussinfo *info, int pid, int sig)
+{
+ if (info->cap_mode)
+ return (pdkill(pid, sig));
+ return (kill(pid, sig));
+}
+#endif
+
+static int
+truss_kill(struct trussinfo *info, struct procinfo *p, int sig)
+{
+ if (info->cap_mode)
+ return (pdkill(p->pfd, sig));
+ return (kill(p->pid, sig));
+}
+
/*
* setup_and_wait() is called to start a process. All it really does
* is fork(), enable tracing in the child, and then exec the given
@@ -148,21 +206,38 @@
setup_and_wait(struct trussinfo *info, char *command[])
{
pid_t pid;
+ int fd, res;
- pid = vfork();
- if (pid == -1)
- err(1, "fork failed");
+ if (info->cap_mode) {
+ pid = pdfork(&fd, PD_DAEMON | PD_CLOEXEC | PD_PTRACE_CAP);
+ if (pid == -1)
+ err(1, "fork failed");
+ } else {
+ pid = vfork();
+ fd = -1;
+ }
if (pid == 0) { /* Child */
ptrace(PT_TRACE_ME, 0, 0, 0);
execvp(command[0], command);
err(1, "execvp %s", command[0]);
}
+ if (info->cap_mode) {
+ if (cap_enter() == -1) {
+ if (info->force_cap_mode)
+ err(1, "cap_enter");
+ else
+ warn("cap_enter");
+ }
+ new_proc_register_kev(info, fd);
+ }
+
/* Only in the parent here */
- if (waitpid(pid, NULL, 0) < 0)
+ res = t_wait(info, info->cap_mode ? fd : pid, NULL, WFLAGS);
+ if (res < 0)
err(1, "unexpected stop in waitpid");
- new_proc(info, pid, 0);
+ new_proc(info, pid, 0, fd, false, false);
}
/*
@@ -171,20 +246,37 @@
void
start_tracing(struct trussinfo *info, pid_t pid)
{
- int ret, retry;
+ int fd, ret, retry;
+
+ if (info->cap_mode) {
+ fd = pdopenpid(pid, PD_DAEMON | PD_CLOEXEC | PD_PTRACE_CAP);
+ if (fd == -1)
+ err(1, "Cannot open the target process");
+ if (cap_enter() == -1) {
+ if (info->force_cap_mode)
+ err(1, "cap_enter");
+ else
+ warn("cap_enter");
+ }
+ new_proc_register_kev(info, fd);
+ } else {
+ fd = -1;
+ }
retry = 10;
do {
- ret = ptrace(PT_ATTACH, pid, NULL, 0);
+ ret = info->cap_mode ? ptrace(PT_ATTACH | PT_PROCDESC, fd,
+ NULL, 0) : ptrace(PT_ATTACH, pid, NULL, 0);
usleep(200);
} while (ret && retry-- > 0);
if (ret)
err(1, "Cannot attach to target process");
- if (waitpid(pid, NULL, 0) < 0)
+ ret = t_wait(info, info->cap_mode ? fd : pid, NULL, WFLAGS);
+ if (ret < 0)
err(1, "Unexpected stop in waitpid");
- new_proc(info, pid, 0);
+ new_proc(info, pid, 0, fd, false, false);
}
/*
@@ -201,31 +293,32 @@
}
static void
-detach_proc(pid_t pid)
+detach_proc(struct trussinfo *info, struct procinfo *p)
{
- int sig, status;
+ int error, sig, status;
/*
* Stop the child so that we can detach. Filter out possible
* lingering SIGTRAP events buffered in the threads.
*/
- kill(pid, SIGSTOP);
+ truss_kill(info, p, SIGSTOP);
for (;;) {
- if (waitpid(pid, &status, 0) < 0)
+ error = truss_wait(info, p, &status, WFLAGS);
+ if (error < 0)
err(1, "Unexpected error in waitpid");
sig = WIFSTOPPED(status) ? WSTOPSIG(status) : 0;
if (sig == SIGSTOP)
break;
if (sig == SIGTRAP)
sig = 0;
- if (ptrace(PT_CONTINUE, pid, (caddr_t)1, sig) < 0)
+ if (truss_ptrace(info, PT_CONTINUE, p, (caddr_t)1, sig) < 0)
err(1, "Can not continue for detach");
}
- if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0)
+ if (truss_ptrace(info, PT_DETACH, p, (caddr_t)1, 0) < 0)
err(1, "Can not detach the process");
- kill(pid, SIGCONT);
+ truss_kill(info, p, SIGCONT);
}
/*
@@ -297,12 +390,12 @@
lwpid_t *lwps;
int i, nlwps;
- nlwps = ptrace(PT_GETNUMLWPS, p->pid, NULL, 0);
+ nlwps = truss_ptrace(info, PT_GETNUMLWPS, p, NULL, 0);
if (nlwps == -1)
err(1, "Unable to fetch number of LWPs");
assert(nlwps > 0);
lwps = calloc(nlwps, sizeof(*lwps));
- nlwps = ptrace(PT_GETLWPLIST, p->pid, (caddr_t)lwps, nlwps);
+ nlwps = truss_ptrace(info, PT_GETLWPLIST, p, (caddr_t)lwps, nlwps);
if (nlwps == -1)
err(1, "Unable to fetch LWP list");
for (i = 0; i < nlwps; i++) {
@@ -318,27 +411,56 @@
}
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;
+
+ if (!info->cap_mode)
+ return;
+ EV_SET(&ev[0], pfd, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT |
+ NOTE_PDSIGCHLD | NOTE_FORK, 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 bool
+new_proc(struct trussinfo *info, pid_t pid, lwpid_t lwpid, int pfd,
+ bool allow_known, bool wait_for)
{
struct procinfo *np;
/*
- * If this happens it means there is a bug in truss. Unfortunately
- * this will kill any processes truss is attached to.
+ * If this happens it means there is a bug in truss.
+ * Unfortunately this will kill any processes truss is
+ * attached to.
*/
- LIST_FOREACH(np, &info->proclist, entries) {
- if (np->pid == pid)
+ if (find_proc(info, pid) != NULL) {
+ if (allow_known)
+ return (false);
+ else
errx(1, "Duplicate process for pid %ld", (long)pid);
}
+ if (pfd == -1 && info->cap_mode) {
+ pfd = pdopenpid(pid, PD_DAEMON | PD_CLOEXEC | PD_PTRACE_CAP);
+ if (pfd == -1)
+ err(1, "pdopenid %d", pid);
+ if (wait_for)
+ t_wait(info, pfd, NULL, WFLAGS);
+ 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);
- if (ptrace(PT_LWP_EVENTS, pid, NULL, 1) == -1)
- 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);
+ np->herald_printed = false;
+ if ((info->flags & FOLLOWFORKS) != 0 && truss_ptrace(info,
+ PT_FOLLOW_FORK, np, NULL, 1) == -1)
+ err(1, "Unable to follow forks for pid %ld", (long)pid);
+ if (truss_ptrace(info, PT_LWP_EVENTS, np, NULL, 1) == -1)
+ err(1, "Unable to enable LWP events for pid %ld", (long)pid);
LIST_INIT(&np->threadlist);
LIST_INIT(&np->fdlist);
LIST_INSERT_HEAD(&info->proclist, np, entries);
@@ -347,13 +469,21 @@
new_thread(np, lwpid);
else
add_threads(info, np);
+ return (true);
}
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];
+
+ if (info->cap_mode) {
+ 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 +503,8 @@
struct procinfo *p, *p2;
LIST_FOREACH_SAFE(p, &info->proclist, entries, p2) {
- detach_proc(p->pid);
- free_proc(p);
+ detach_proc(info, p);
+ free_proc(info, p);
}
}
@@ -616,9 +746,9 @@
assert(LIST_NEXT(LIST_FIRST(&p->threadlist), entries) == NULL);
p->abi = find_abi(p->pid);
if (p->abi == NULL) {
- if (ptrace(PT_DETACH, p->pid, (caddr_t)1, 0) < 0)
+ if (truss_ptrace(info, PT_DETACH, p, (caddr_t)1, 0) < 0)
err(1, "Can not detach the process");
- free_proc(p);
+ free_proc(info, p);
}
}
}
@@ -701,6 +831,9 @@
struct threadinfo *t;
t = info->curthread;
+ if (t->proc->herald_printed)
+ return;
+ t->proc->herald_printed = true;
clock_gettime(CLOCK_REALTIME, &t->after);
t->before = t->after;
print_line_prefix(info);
@@ -780,6 +913,85 @@
}
+static void
+eventloop_handle_trapped(struct trussinfo *info, pid_t si_pid, int si_status,
+ siginfo_t *si)
+{
+ struct ptrace_lwpinfo pl;
+ int pending_signal;
+
+ if (ptrace(PT_LWPINFO, si_pid, (caddr_t)&pl, sizeof(pl)) == -1)
+ err(1, "ptrace(PT_LWPINFO)");
+
+ if ((pl.pl_flags & PL_FLAG_CHILD) != 0) {
+ new_proc(info, si_pid, pl.pl_lwpid, -1, true, false);
+ assert(LIST_FIRST(&info->proclist)->abi != NULL);
+ } else if ((pl.pl_flags & PL_FLAG_BORN) != 0) {
+ new_thread(find_proc(info, si_pid),
+ pl.pl_lwpid);
+ }
+ find_thread(info, si_pid, pl.pl_lwpid);
+
+ pending_signal = 0;
+ if (si_status == SIGTRAP && (pl.pl_flags & (PL_FLAG_BORN |
+ PL_FLAG_EXITED | PL_FLAG_SCE | PL_FLAG_SCX)) != 0) {
+ if ((pl.pl_flags & PL_FLAG_BORN) != 0) {
+ if ((info->flags & COUNTONLY) == 0)
+ report_thread_birth(info);
+ } else if ((pl.pl_flags & PL_FLAG_EXITED) != 0) {
+ if ((info->flags & COUNTONLY) == 0)
+ report_thread_death(info);
+ free_thread(info->curthread);
+ info->curthread = NULL;
+ } else if ((pl.pl_flags & PL_FLAG_SCE) != 0) {
+ enter_syscall(info, info->curthread, &pl);
+ } else if ((pl.pl_flags & PL_FLAG_SCX) != 0) {
+ exit_syscall(info, &pl);
+ }
+ } else if ((pl.pl_flags & PL_FLAG_CHILD) != 0) {
+ if ((info->flags & COUNTONLY) == 0)
+ report_new_child(info);
+ } else if (si != NULL) {
+ if ((info->flags & NOSIGS) == 0)
+ report_signal(info, si, &pl);
+ pending_signal = si->si_status;
+ }
+ if (ptrace(PT_SYSCALL, si_pid, (caddr_t)1, pending_signal) == -1)
+ err(1, "ptrace(PT_SYSCALL)");
+}
+
+static void
+eventloop_handle_note_fork(struct trussinfo *info)
+{
+ struct ptrace_child *ptcs;
+ int cnt, i;
+
+ cnt = ptrace(PT_GET_CHILDREN, getpid(), NULL, 0);
+ if (cnt == -1)
+ err(1, "Unexpected error from ptrace(PT_GET_CHILDREN) size");
+ if (cnt == 0)
+ return;
+ ptcs = calloc(cnt, sizeof(*ptcs));
+ if (ptcs == NULL)
+ err(1, "No memory");
+ cnt = ptrace(PT_GET_CHILDREN, getpid(), (caddr_t)ptcs,
+ cnt * sizeof(*ptcs));
+ if (cnt == -1)
+ err(1, "Unexpected error from ptrace(PT_GET_CHILDREN) data");
+ for (i = 0; i < cnt; i++) {
+ if ((ptcs[i].flags & (PTCHLD_TRACED | PTCHLD_TRACED_BY_ME |
+ PTCHLD_EXITED)) != (PTCHLD_TRACED | PTCHLD_TRACED_BY_ME))
+ continue;
+ if (new_proc(info, ptcs[i].pid, 0, -1, true, true)) {
+ if ((info->flags & COUNTONLY) == 0)
+ report_new_child(info);
+ eventloop_handle_trapped(info, ptcs[i].pid, SIGTRAP,
+ NULL);
+ }
+ }
+ free(ptcs);
+}
+
/*
* Wait for events until all the processes have exited or truss has been
* asked to stop.
@@ -787,9 +999,10 @@
void
eventloop(struct trussinfo *info)
{
- struct ptrace_lwpinfo pl;
siginfo_t si;
- int pending_signal;
+ struct kevent ev[1];
+ int cnt, error;
+ bool has_si;
while (!LIST_EMPTY(&info->proclist)) {
if (detaching) {
@@ -797,11 +1010,43 @@
return;
}
- if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) {
- if (errno == EINTR)
+ has_si = false;
+ if (info->cap_mode) {
+ cnt = kevent(info->pdkq, NULL, 0, ev, nitems(ev),
+ NULL);
+ if (cnt == -1) {
+ if (errno == EINTR)
+ continue;
+ err(1, "Unexpected error from kevent");
+ }
+ if (cnt == 0) {
+ /* XXXKIB ? */
continue;
- err(1, "Unexpected error from waitid");
+ }
+ if ((ev[0].fflags & (NOTE_EXIT | NOTE_PDSIGCHLD)) !=
+ 0) {
+ error = pdwait(ev[0].ident, NULL,
+ WFLAGS | WNOHANG, NULL, &si);
+ if (error == -1) {
+ if (errno == EINTR ||
+ errno == EWOULDBLOCK)
+ continue;
+ err(1, "Unexpected error from pdwait");
+ }
+ has_si = true;
+ }
+ if ((ev[0].fflags & NOTE_FORK) != 0)
+ eventloop_handle_note_fork(info);
+ } else {
+ if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) {
+ if (errno == EINTR)
+ continue;
+ err(1, "Unexpected error from waitid");
+ }
+ has_si = true;
}
+ if (!has_si)
+ continue;
assert(si.si_signo == SIGCHLD);
@@ -815,50 +1060,12 @@
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:
- if (ptrace(PT_LWPINFO, si.si_pid, (caddr_t)&pl,
- sizeof(pl)) == -1)
- err(1, "ptrace(PT_LWPINFO)");
-
- if (pl.pl_flags & PL_FLAG_CHILD) {
- new_proc(info, si.si_pid, pl.pl_lwpid);
- assert(LIST_FIRST(&info->proclist)->abi !=
- NULL);
- } else if (pl.pl_flags & PL_FLAG_BORN)
- new_thread(find_proc(info, si.si_pid),
- pl.pl_lwpid);
- find_thread(info, si.si_pid, pl.pl_lwpid);
-
- if (si.si_status == SIGTRAP &&
- (pl.pl_flags & (PL_FLAG_BORN|PL_FLAG_EXITED|
- PL_FLAG_SCE|PL_FLAG_SCX)) != 0) {
- if (pl.pl_flags & PL_FLAG_BORN) {
- if ((info->flags & COUNTONLY) == 0)
- report_thread_birth(info);
- } else if (pl.pl_flags & PL_FLAG_EXITED) {
- if ((info->flags & COUNTONLY) == 0)
- report_thread_death(info);
- free_thread(info->curthread);
- info->curthread = NULL;
- } else if (pl.pl_flags & PL_FLAG_SCE)
- enter_syscall(info, info->curthread, &pl);
- else if (pl.pl_flags & PL_FLAG_SCX)
- exit_syscall(info, &pl);
- pending_signal = 0;
- } else if (pl.pl_flags & PL_FLAG_CHILD) {
- if ((info->flags & COUNTONLY) == 0)
- report_new_child(info);
- pending_signal = 0;
- } else {
- if ((info->flags & NOSIGS) == 0)
- report_signal(info, &si, &pl);
- pending_signal = si.si_status;
- }
- ptrace(PT_SYSCALL, si.si_pid, (caddr_t)1,
- pending_signal);
+ eventloop_handle_trapped(info, si.si_pid,
+ si.si_status, &si);
break;
case CLD_STOPPED:
errx(1, "waitid reported CLD_STOPPED");
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,7 +106,9 @@
struct procinfo {
LIST_ENTRY(procinfo) entries;
pid_t pid;
+ int pfd;
struct procabi *abi;
+ bool herald_printed;
LIST_HEAD(, threadinfo) threadlist;
LIST_HEAD(, fd_domain) fdlist;
@@ -117,6 +119,9 @@
int flags;
int strsize;
FILE *outfile;
+ int pdkq;
+ bool cap_mode;
+ bool force_cap_mode;
struct timespec start_time;
diff --git a/usr.bin/truss/truss.1 b/usr.bin/truss/truss.1
--- a/usr.bin/truss/truss.1
+++ b/usr.bin/truss/truss.1
@@ -7,7 +7,7 @@
.Nd trace system calls
.Sh SYNOPSIS
.Nm
-.Op Fl facedDHS
+.Op Fl facedyDHSY
.Op Fl o Ar file
.Op Fl s Ar strsize
.Fl p Ar pid
@@ -62,6 +62,22 @@
(Normally,
.Nm
displays signal as well as system call events.)
+.It Fl y
+Enter the capability mode
+.Pq see Xr capsicum 4
+after opening the output file, and creating and
+attaching to the target process.
+The error to enter the mode is not fatal, but a warning is
+issued to the standard error.
+.It Fl Y
+Same as
+.Fl y ,
+but make the error to enter the capability mode fatal.
+Since the traced process is spawn and attached to
+before the entry into the mode, it is killed by
+the termination of the
+.Nm
+itself.
.It Fl o Ar file
Print the output to the specified
.Ar file

File Metadata

Mime Type
text/plain
Expires
Fri, Jul 24, 5:06 PM (7 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35448758
Default Alt Text
D58094.id182261.diff (64 KB)

Event Timeline