summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2016-08-15 21:10:41 +0000
committerjhb <jhb@FreeBSD.org>2016-08-15 21:10:41 +0000
commite4842c451d2fcb20d4e7734a423b4dda17c5e79b (patch)
tree5e1aaaba2757e8de80c939a791a338c87196b1dd /sys/kern
parentbdf6ecc5e6663cf315031bec7696e4418f02bd24 (diff)
downloadFreeBSD-src-e4842c451d2fcb20d4e7734a423b4dda17c5e79b.zip
FreeBSD-src-e4842c451d2fcb20d4e7734a423b4dda17c5e79b.tar.gz
MFC 302900,302902,302921,303461,304009:
Add a mask of optional ptrace() events. 302900: Add a test for user signal delivery. This test verifies we get the correct ptrace event details when a signal is posted to a traced process from userland. 302902: Add a mask of optional ptrace() events. ptrace() now stores a mask of optional events in p_ptevents. Currently this mask is a single integer, but it can be expanded into an array of integers in the future. Two new ptrace requests can be used to manipulate the event mask: PT_GET_EVENT_MASK fetches the current event mask and PT_SET_EVENT_MASK sets the current event mask. The current set of events include: - PTRACE_EXEC: trace calls to execve(). - PTRACE_SCE: trace system call entries. - PTRACE_SCX: trace syscam call exits. - PTRACE_FORK: trace forks and auto-attach to new child processes. - PTRACE_LWP: trace LWP events. The S_PT_SCX and S_PT_SCE events in the procfs p_stops flags have been replaced by PTRACE_SCE and PTRACE_SCX. PTRACE_FORK replaces P_FOLLOW_FORK and PTRACE_LWP replaces P2_LWP_EVENTS. The PT_FOLLOW_FORK and PT_LWP_EVENTS ptrace requests remain for compatibility but now simply toggle corresponding flags in the event mask. While here, document that PT_SYSCALL, PT_TO_SCE, and PT_TO_SCX both modify the event mask and continue the traced process. 302921: Rename PTRACE_SYSCALL to LINUX_PTRACE_SYSCALL. 303461: Note that not all optional ptrace events use SIGTRAP. New child processes attached due to PTRACE_FORK use SIGSTOP instead of SIGTRAP. All other ptrace events use SIGTRAP. 304009: Remove description of P_FOLLOWFORK as this flag was removed.
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_exec.c4
-rw-r--r--sys/kern/kern_exit.c2
-rw-r--r--sys/kern/kern_fork.c10
-rw-r--r--sys/kern/kern_sig.c3
-rw-r--r--sys/kern/kern_thr.c5
-rw-r--r--sys/kern/subr_syscall.c4
-rw-r--r--sys/kern/sys_process.c77
7 files changed, 77 insertions, 28 deletions
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 6466a3e..df8e903 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/pioctl.h>
+#include <sys/ptrace.h>
#include <sys/namei.h>
#include <sys/resourcevar.h>
#include <sys/rwlock.h>
@@ -905,7 +906,8 @@ exec_fail_dealloc:
if (error == 0) {
PROC_LOCK(p);
- td->td_dbgflags |= TDB_EXEC;
+ if (p->p_ptevents & PTRACE_EXEC)
+ td->td_dbgflags |= TDB_EXEC;
PROC_UNLOCK(p);
/*
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 929c2c0..3b7b9da 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -338,6 +338,7 @@ exit1(struct thread *td, int rval, int signo)
PROC_LOCK(p);
stopprofclock(p);
p->p_flag &= ~(P_TRACED | P_PPWAIT | P_PPTRACE);
+ p->p_ptevents = 0;
/*
* Stop the real interval timer. If the handler is currently
@@ -475,6 +476,7 @@ exit1(struct thread *td, int rval, int signo)
*/
clear_orphan(q);
q->p_flag &= ~(P_TRACED | P_STOPPED_TRACE);
+ q->p_ptevents = 0;
FOREACH_THREAD_IN_PROC(q, tdt)
tdt->td_dbgflags &= ~TDB_SUSPEND;
kern_psignal(q, SIGKILL);
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 2c98551..84bef4f 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -414,6 +414,7 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
bzero(&p2->p_startzero,
__rangeof(struct proc, p_startzero, p_endzero));
+ p2->p_ptevents = 0;
/* Tell the prison that we exist. */
prison_proc_hold(p2->p_ucred->cr_prison);
@@ -720,8 +721,7 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
* but before we wait for the debugger.
*/
_PHOLD(p2);
- if ((p1->p_flag & (P_TRACED | P_FOLLOWFORK)) == (P_TRACED |
- P_FOLLOWFORK)) {
+ if (p1->p_ptevents & PTRACE_FORK) {
/*
* Arrange for debugger to receive the fork event.
*
@@ -1068,14 +1068,14 @@ fork_return(struct thread *td, struct trapframe *frame)
if (td->td_dbgflags & TDB_STOPATFORK) {
sx_xlock(&proctree_lock);
PROC_LOCK(p);
- if ((p->p_pptr->p_flag & (P_TRACED | P_FOLLOWFORK)) ==
- (P_TRACED | P_FOLLOWFORK)) {
+ if (p->p_pptr->p_ptevents & PTRACE_FORK) {
/*
* If debugger still wants auto-attach for the
* parent's children, do it now.
*/
dbg = p->p_pptr->p_pptr;
p->p_flag |= P_TRACED;
+ p->p_ptevents = PTRACE_DEFAULT;
p->p_oppid = p->p_pptr->p_pid;
CTR2(KTR_PTRACE,
"fork_return: attaching to new child pid %d: oppid %d",
@@ -1102,7 +1102,7 @@ fork_return(struct thread *td, struct trapframe *frame)
PROC_LOCK(p);
td->td_dbgflags |= TDB_SCX;
_STOPEVENT(p, S_SCX, td->td_dbg_sc_code);
- if ((p->p_stops & S_PT_SCX) != 0 ||
+ if ((p->p_ptevents & PTRACE_SCX) != 0 ||
(td->td_dbgflags & TDB_BORN) != 0)
ptracestop(td, SIGTRAP);
td->td_dbgflags &= ~(TDB_SCX | TDB_BORN);
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 2a5e6de..1da4b99 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -2191,9 +2191,10 @@ tdsendsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
!((prop & SA_CONT) && (p->p_flag & P_STOPPED_SIG)))
return (ret);
/*
- * SIGKILL: Remove procfs STOPEVENTs.
+ * SIGKILL: Remove procfs STOPEVENTs and ptrace events.
*/
if (sig == SIGKILL) {
+ p->p_ptevents = 0;
/* from procfs_ioctl.c: PIOCBIC */
p->p_stops = 0;
/* from procfs_ioctl.c: PIOCCONT */
diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c
index 293574c..10e7b2d 100644
--- a/sys/kern/kern_thr.c
+++ b/sys/kern/kern_thr.c
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/posix4.h>
+#include <sys/ptrace.h>
#include <sys/racct.h>
#include <sys/resourcevar.h>
#include <sys/rwlock.h>
@@ -254,7 +255,7 @@ thread_create(struct thread *td, struct rtprio *rtp,
thread_unlock(td);
if (P_SHOULDSTOP(p))
newtd->td_flags |= TDF_ASTPENDING | TDF_NEEDSUSPCHK;
- if (p->p_flag2 & P2_LWP_EVENTS)
+ if (p->p_ptevents & PTRACE_LWP)
newtd->td_dbgflags |= TDB_BORN;
/*
@@ -354,7 +355,7 @@ kern_thr_exit(struct thread *td)
p->p_pendingexits++;
td->td_dbgflags |= TDB_EXIT;
- if (p->p_flag & P_TRACED && p->p_flag2 & P2_LWP_EVENTS)
+ if (p->p_ptevents & PTRACE_LWP)
ptracestop(td, SIGTRAP);
PROC_UNLOCK(p);
tidhash_remove(td);
diff --git a/sys/kern/subr_syscall.c b/sys/kern/subr_syscall.c
index f7bbdfb..aad2a1e 100644
--- a/sys/kern/subr_syscall.c
+++ b/sys/kern/subr_syscall.c
@@ -87,7 +87,7 @@ syscallenter(struct thread *td, struct syscall_args *sa)
PROC_LOCK(p);
td->td_dbg_sc_code = sa->code;
td->td_dbg_sc_narg = sa->narg;
- if (p->p_stops & S_PT_SCE)
+ if (p->p_ptevents & PTRACE_SCE)
ptracestop((td), SIGTRAP);
PROC_UNLOCK(p);
}
@@ -208,7 +208,7 @@ syscallret(struct thread *td, int error, struct syscall_args *sa)
*/
if (traced &&
((td->td_dbgflags & (TDB_FORK | TDB_EXEC)) != 0 ||
- (p->p_stops & S_PT_SCX) != 0))
+ (p->p_ptevents & PTRACE_SCX) != 0))
ptracestop(td, SIGTRAP);
td->td_dbgflags &= ~(TDB_SCX | TDB_EXEC | TDB_FORK);
PROC_UNLOCK(p);
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index af71ea8..f1477ce 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -586,6 +586,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
struct ptrace_lwpinfo32 pl32;
struct ptrace_vm_entry32 pve32;
#endif
+ int ptevents;
} r;
void *addr;
int error = 0;
@@ -600,6 +601,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
AUDIT_ARG_VALUE(uap->data);
addr = &r;
switch (uap->req) {
+ case PT_GET_EVENT_MASK:
case PT_GETREGS:
case PT_GETFPREGS:
case PT_GETDBREGS:
@@ -614,6 +616,12 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
case PT_SETDBREGS:
error = COPYIN(uap->addr, &r.dbreg, sizeof r.dbreg);
break;
+ case PT_SET_EVENT_MASK:
+ if (uap->data != sizeof(r.ptevents))
+ error = EINVAL;
+ else
+ error = copyin(uap->addr, &r.ptevents, uap->data);
+ break;
case PT_IO:
error = COPYIN(uap->addr, &r.piod, sizeof r.piod);
break;
@@ -647,7 +655,12 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
case PT_GETDBREGS:
error = COPYOUT(&r.dbreg, uap->addr, sizeof r.dbreg);
break;
+ case PT_GET_EVENT_MASK:
+ /* NB: The size in uap->data is validated in kern_ptrace(). */
+ error = copyout(&r.ptevents, uap->addr, uap->data);
+ break;
case PT_LWPINFO:
+ /* NB: The size in uap->data is validated in kern_ptrace(). */
error = copyout(&r.pl, uap->addr, uap->data);
break;
}
@@ -711,6 +724,8 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
case PT_SYSCALL:
case PT_FOLLOW_FORK:
case PT_LWP_EVENTS:
+ case PT_GET_EVENT_MASK:
+ case PT_SET_EVENT_MASK:
case PT_DETACH:
sx_xlock(&proctree_lock);
proctree_locked = 1;
@@ -885,6 +900,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
case PT_TRACE_ME:
/* set my trace flag and "owner" so it can read/write me */
p->p_flag |= P_TRACED;
+ p->p_ptevents = PTRACE_DEFAULT;
if (p->p_flag & P_PPWAIT)
p->p_flag |= P_PPTRACE;
p->p_oppid = p->p_pptr->p_pid;
@@ -903,6 +919,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
* on a "detach".
*/
p->p_flag |= P_TRACED;
+ p->p_ptevents = PTRACE_DEFAULT;
p->p_oppid = p->p_pptr->p_pid;
if (p->p_pptr != td->td_proc) {
proc_reparent(p, td->td_proc);
@@ -941,24 +958,50 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
case PT_FOLLOW_FORK:
CTR3(KTR_PTRACE, "PT_FOLLOW_FORK: pid %d %s -> %s", p->p_pid,
- p->p_flag & P_FOLLOWFORK ? "enabled" : "disabled",
+ p->p_ptevents & PTRACE_FORK ? "enabled" : "disabled",
data ? "enabled" : "disabled");
if (data)
- p->p_flag |= P_FOLLOWFORK;
+ p->p_ptevents |= PTRACE_FORK;
else
- p->p_flag &= ~P_FOLLOWFORK;
+ p->p_ptevents &= ~PTRACE_FORK;
break;
case PT_LWP_EVENTS:
CTR3(KTR_PTRACE, "PT_LWP_EVENTS: pid %d %s -> %s", p->p_pid,
- p->p_flag2 & P2_LWP_EVENTS ? "enabled" : "disabled",
+ p->p_ptevents & PTRACE_LWP ? "enabled" : "disabled",
data ? "enabled" : "disabled");
if (data)
- p->p_flag2 |= P2_LWP_EVENTS;
+ p->p_ptevents |= PTRACE_LWP;
else
- p->p_flag2 &= ~P2_LWP_EVENTS;
+ p->p_ptevents &= ~PTRACE_LWP;
+ break;
+
+ case PT_GET_EVENT_MASK:
+ if (data != sizeof(p->p_ptevents)) {
+ error = EINVAL;
+ break;
+ }
+ CTR2(KTR_PTRACE, "PT_GET_EVENT_MASK: pid %d mask %#x", p->p_pid,
+ p->p_ptevents);
+ *(int *)addr = p->p_ptevents;
break;
+ case PT_SET_EVENT_MASK:
+ if (data != sizeof(p->p_ptevents)) {
+ error = EINVAL;
+ break;
+ }
+ tmp = *(int *)addr;
+ if ((tmp & ~(PTRACE_EXEC | PTRACE_SCE | PTRACE_SCX |
+ PTRACE_FORK | PTRACE_LWP)) != 0) {
+ error = EINVAL;
+ break;
+ }
+ CTR3(KTR_PTRACE, "PT_SET_EVENT_MASK: pid %d mask %#x -> %#x",
+ p->p_pid, p->p_ptevents, tmp);
+ p->p_ptevents = tmp;
+ break;
+
case PT_STEP:
case PT_CONTINUE:
case PT_TO_SCE:
@@ -991,24 +1034,24 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
}
switch (req) {
case PT_TO_SCE:
- p->p_stops |= S_PT_SCE;
+ p->p_ptevents |= PTRACE_SCE;
CTR4(KTR_PTRACE,
- "PT_TO_SCE: pid %d, stops = %#x, PC = %#lx, sig = %d",
- p->p_pid, p->p_stops,
+ "PT_TO_SCE: pid %d, events = %#x, PC = %#lx, sig = %d",
+ p->p_pid, p->p_ptevents,
(u_long)(uintfptr_t)addr, data);
break;
case PT_TO_SCX:
- p->p_stops |= S_PT_SCX;
+ p->p_ptevents |= PTRACE_SCX;
CTR4(KTR_PTRACE,
- "PT_TO_SCX: pid %d, stops = %#x, PC = %#lx, sig = %d",
- p->p_pid, p->p_stops,
+ "PT_TO_SCX: pid %d, events = %#x, PC = %#lx, sig = %d",
+ p->p_pid, p->p_ptevents,
(u_long)(uintfptr_t)addr, data);
break;
case PT_SYSCALL:
- p->p_stops |= S_PT_SCE | S_PT_SCX;
+ p->p_ptevents |= PTRACE_SYSCALL;
CTR4(KTR_PTRACE,
- "PT_SYSCALL: pid %d, stops = %#x, PC = %#lx, sig = %d",
- p->p_pid, p->p_stops,
+ "PT_SYSCALL: pid %d, events = %#x, PC = %#lx, sig = %d",
+ p->p_pid, p->p_ptevents,
(u_long)(uintfptr_t)addr, data);
break;
case PT_CONTINUE:
@@ -1027,7 +1070,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
* parent. Otherwise the debugee will be set
* as an orphan of the debugger.
*/
- p->p_flag &= ~(P_TRACED | P_WAITED | P_FOLLOWFORK);
+ p->p_flag &= ~(P_TRACED | P_WAITED);
if (p->p_oppid != p->p_pptr->p_pid) {
PROC_LOCK(p->p_pptr);
sigqueue_take(p->p_ksi);
@@ -1044,7 +1087,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
CTR2(KTR_PTRACE, "PT_DETACH: pid %d, sig %d",
p->p_pid, data);
p->p_oppid = 0;
- p->p_stops = 0;
+ p->p_ptevents = 0;
/* should we send SIGCHLD? */
/* childproc_continued(p); */
OpenPOWER on IntegriCloud