Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F163487373
D58094.id182261.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
64 KB
Referenced Files
None
Subscribers
None
D58094.id182261.diff
View Options
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, ®);
val = *(®.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, ®);
*(®.r15 + ((uintptr_t)addr / sizeof(reg.r15))) = (uint64_t)data;
linux_to_bsd_regset(&b_reg1, ®);
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
Details
Attached
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)
Attached To
Mode
D58094: Capsicumise truss(1)
Attached
Detach File
Event Timeline
Log In to Comment