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.

Reviewed by:	kib
MFC after:	1 month
Differential Revision:	https://reviews.freebsd.org/D7044
This commit is contained in:
John Baldwin 2016-07-15 15:32:09 +00:00
parent 3340c45b96
commit 8d570f64aa
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=302902
11 changed files with 395 additions and 78 deletions

View File

@ -2,7 +2,7 @@
.\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
.\"
.\" This file is in the public domain.
.Dd December 29, 2015
.Dd July 15, 2016
.Dt PTRACE 2
.Os
.Sh NAME
@ -58,8 +58,9 @@ The signal may be a normal process signal, generated as a result of
traced process behavior, or use of the
.Xr kill 2
system call; alternatively, it may be generated by the tracing facility
as a result of attaching, system calls, or stepping by the tracing
process.
as a result of attaching, stepping by the tracing
process,
or an event in the traced process.
The tracing process may choose to intercept the signal, using it to
observe process behavior (such as
.Dv SIGTRAP ) ,
@ -69,6 +70,111 @@ The
system call
is the mechanism by which all this happens.
.Pp
A traced process may report additional signal stops corresponding to
events in the traced process.
These additional signal stops are reported as
.Dv SIGTRAP
signals.
The tracing process can use the
.Dv PT_LWPINFO
request to determine which events are associated with a
.Dv SIGTRAP
signal.
Note that multiple events may be associated with a single signal.
For example, events indicated by the
.Dv PL_FLAG_BORN ,
.Dv PL_FLAG_FORKED ,
and
.Dv PL_FLAG_EXEC
flags are also reported as a system call exit event
.Pq Dv PL_FLAG_SCX .
.Pp
Each traced process has a tracing event mask.
An event in the traced process only reports a
.Dv SIGTRAP
signal stop if the corresponding flag is set in the tracing event mask.
The current set of tracing event flags include:
.Bl -tag -width ".Dv PTRACE_SYSCALL"
.It Dv PTRACE_EXEC
Report a stop for a successful invocation of
.Xr execve 2 .
This event is indicated by the
.Dv PL_FLAG_EXEC
flag in the
.Va pl_flags
member of
.Vt "struct ptrace_lwpinfo" .
.It Dv PTRACE_SCE
Report a stop on each system call entry.
This event is indicated by the
.Dv PL_FLAG_SCE
flag in the
.Va pl_flags
member of
.Vt "struct ptrace_lwpinfo" .
.It Dv PTRACE_SCX
Report a stop on each system call exit.
This event is indicated by the
.Dv PL_FLAG_SCX
flag in the
.Va pl_flags
member of
.Vt "struct ptrace_lwpinfo" .
.It Dv PTRACE_SYSCALL
Report stops for both system call entry and exit.
.It Dv PTRACE_FORK
This event flag controls tracing for new child processes of a traced process.
.Pp
When this event flag is enabled,
new child processes will enable tracing and stop before executing their
first instruction.
The new child process will include the
.Dv PL_FLAG_CHILD
flag in the
.Va pl_flags
member of
.Vt "struct ptrace_lwpinfo" .
The traced process will report a stop that includes the
.Dv PL_FLAG_FORKED
flag.
The process ID of the new child process will also be present in the
.Va pl_child_pid
member of
.Vt "struct ptrace_lwpinfo" .
Note that new child processes will be attached with the default
tracing event mask;
they do not inherit the event mask of the traced process.
.Pp
When this event flag is not enabled,
new child processes will execute without tracing enabled.
.It Dv PTRACE_LWP
This event flag controls tracing of LWP
.Pq kernel thread
creation and destruction.
When this event is enabled,
new LWPs will stop and report an event with
.Dv PL_FLAG_BORN
set before executing their first instruction,
and exiting LWPs will stop and report an event with
.Dv PL_FLAG_EXITED
set before completing their termination.
.Pp
Note that new processes do not report an event for the creation of their
initial thread,
and exiting processes do not report an event for the termination of the
last thread.
.El
.Pp
The default tracing event mask when attaching to a process via
.Dv PT_ATTACH ,
.Dv PT_TRACE_ME ,
or
.Dv PTRACE_FORK
includes only
.Dv PTRACE_EXEC
events.
All other event flags are disabled.
.Pp
The
.Fa request
argument specifies what operation is being performed; the meaning of
@ -368,21 +474,20 @@ The process identifier of the new process is available in the
member of
.Vt "struct ptrace_lwpinfo" .
.It PL_FLAG_CHILD
The flag is set for first event reported from a new child, which is
automatically attached due to
.Dv PT_FOLLOW_FORK
enabled.
The flag is set for first event reported from a new child which is
automatically attached when
.Dv PTRACE_FORK
is enabled.
.It PL_FLAG_BORN
This flag is set for the first event reported from a new LWP when LWP
events are enabled via
.Dv PT_LWP_EVENTS .
This flag is set for the first event reported from a new LWP when
.Dv PTRACE_LWP
is enabled.
It is reported along with
.Dv PL_FLAG_SCX
and is always reported if LWP events are enabled.
.Dv PL_FLAG_SCX .
.It PL_FLAG_EXITED
This flag is set for the last event reported by an exiting LWP when
LWP events are enabled via
.Dv PT_LWP_EVENTS .
.Dv PTRACE_LWP
is enabled.
Note that this event is not reported when the last LWP in a process exits.
The termination of the last thread is reported via a normal process exit
event.
@ -456,50 +561,72 @@ This request will suspend the specified thread.
.It PT_RESUME
This request will resume the specified thread.
.It PT_TO_SCE
This request will trace the specified process on each system call entry.
This request will set the
.Dv PTRACE_SCE
event flag to trace all future system call entries and continue the process.
The
.Fa addr
and
.Fa data
arguments are used the same as for
.Dv PT_CONTINUE.
.It PT_TO_SCX
This request will trace the specified process on each system call exit.
This request will set the
.Dv PTRACE_SCX
event flag to trace all future system call exits and continue the process.
The
.Fa addr
and
.Fa data
arguments are used the same as for
.Dv PT_CONTINUE.
.It PT_SYSCALL
This request will trace the specified process
on each system call entry and exit.
This request will set the
.Dv PTRACE_SYSCALL
event flag to trace all future system call entries and exits and continue
the process.
The
.Fa addr
and
.Fa data
arguments are used the same as for
.Dv PT_CONTINUE.
.It PT_FOLLOW_FORK
This request controls tracing for new child processes of a traced process.
If
.Fa data
is non-zero,
then new child processes will enable tracing and stop before executing their
first instruction.
.Dv PTRACE_FORK
is set in the traced process's event tracing mask.
If
.Fa data
is zero, then new child processes will execute without tracing enabled.
By default, tracing is not enabled for new child processes.
Child processes do not inherit this property.
The traced process will set the
.Dv PL_FLAG_FORKED
flag upon exit from a system call that creates a new process.
is zero,
.Dv PTRACE_FORK
is cleared from the traced process's event tracing mask.
.It PT_LWP_EVENTS
This request controls tracing of LWP creation and destruction.
If
.Fa data
is non-zero,
then LWPs will stop to report creation and destruction events.
.Dv PTRACE_LWP
is set in the traced process's event tracing mask.
If
.Fa data
is zero,
then LWP creation and destruction events will not be reported.
By default, tracing is not enabled for LWP events.
Child processes do not inherit this property.
New LWPs will stop to report an event with
.Dv PL_FLAG_BORN
set before executing their first instruction.
Exiting LWPs will stop to report an event with
.Dv PL_FLAG_EXITED
set before completing their termination.
.Pp
Note that new processes do not report an event for the creation of their
initial thread,
and exiting processes do not report an event for the termination of the
last thread.
.Dv PTRACE_LWP
is cleared from the traced process's event tracing mask.
.It PT_GET_EVENT_MASK
This request reads the traced process's event tracing mask into the
integer pointed to by
.Fa addr .
The size of the integer must be passed in
.Fa data .
.It PT_SET_EVENT_MASK
This request sets the traced process's event tracing mask from the
integer pointed to by
.Fa addr .
The size of the integer must be passed in
.Fa data .
.It PT_VM_TIMESTAMP
This request returns the generation number or timestamp of the memory map of
the traced process as the return value from

View File

@ -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>
@ -903,7 +904,8 @@ do_execve(td, args, mac_p)
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);
/*

View File

@ -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);

View File

@ -720,8 +720,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 +1067,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 +1101,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);

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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); */

View File

@ -582,6 +582,7 @@ struct proc {
u_int p_stype; /* (c) Stop event type. */
char p_step; /* (c) Process is stopped. */
u_char p_pfsflags; /* (c) Procfs flags. */
u_int p_ptevents; /* (c) ptrace() event mask. */
struct nlminfo *p_nlminfo; /* (?) Only used by/for lockd. */
struct kaioinfo *p_aioinfo; /* (y) ASYNC I/O info. */
struct thread *p_singlethread;/* (c + j) If single threading this is it */
@ -672,7 +673,7 @@ struct proc {
#define P_ADVLOCK 0x00001 /* Process may hold a POSIX advisory lock. */
#define P_CONTROLT 0x00002 /* Has a controlling terminal. */
#define P_KPROC 0x00004 /* Kernel process. */
#define P_FOLLOWFORK 0x00008 /* Attach parent debugger to children. */
#define P_UNUSED3 0x00008 /* --available-- */
#define P_PPWAIT 0x00010 /* Parent is waiting for child to exec/exit. */
#define P_PROFIL 0x00020 /* Has started profiling. */
#define P_STOPPROF 0x00040 /* Has thread requesting to stop profiling. */
@ -711,7 +712,6 @@ struct proc {
#define P2_NOTRACE 0x00000002 /* No ptrace(2) attach or coredumps. */
#define P2_NOTRACE_EXEC 0x00000004 /* Keep P2_NOPTRACE on exec(2). */
#define P2_AST_SU 0x00000008 /* Handles SU ast for kthreads. */
#define P2_LWP_EVENTS 0x00000010 /* Report LWP events via ptrace(2). */
/* Flags protected by proctree_lock, kept in p_treeflags. */
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */

View File

@ -66,6 +66,9 @@
#define PT_FOLLOW_FORK 23
#define PT_LWP_EVENTS 24 /* report LWP birth and exit */
#define PT_GET_EVENT_MASK 25 /* get mask of optional events */
#define PT_SET_EVENT_MASK 26 /* set mask of optional events */
#define PT_GETREGS 33 /* get general-purpose registers */
#define PT_SETREGS 34 /* set general-purpose registers */
#define PT_GETFPREGS 35 /* get floating-point registers */
@ -79,6 +82,16 @@
#define PT_FIRSTMACH 64 /* for machine-specific requests */
#include <machine/ptrace.h> /* machine-specific requests, if any */
/* Events used with PT_GET_EVENT_MASK and PT_SET_EVENT_MASK */
#define PTRACE_EXEC 0x0001
#define PTRACE_SCE 0x0002
#define PTRACE_SCX 0x0004
#define PTRACE_SYSCALL (PTRACE_SCE | PTRACE_SCX)
#define PTRACE_FORK 0x0008
#define PTRACE_LWP 0x0010
#define PTRACE_DEFAULT (PTRACE_EXEC)
struct ptrace_io_desc {
int piod_op; /* I/O operation */
void *piod_offs; /* child offset */
@ -136,13 +149,6 @@ struct ptrace_vm_entry {
#ifdef _KERNEL
/*
* The flags below are used for ptrace(2) tracing and have no relation
* to procfs. They are stored in struct proc's p_stops member.
*/
#define S_PT_SCE 0x000010000
#define S_PT_SCX 0x000020000
int ptrace_set_pc(struct thread *_td, unsigned long _addr);
int ptrace_single_step(struct thread *_td);
int ptrace_clear_single_step(struct thread *_td);

View File

@ -1416,6 +1416,139 @@ ATF_TC_BODY(ptrace__siginfo, tc)
ATF_REQUIRE(errno == ECHILD);
}
/*
* Verify that the expected ptrace events are reported for PTRACE_EXEC.
*/
ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_disable);
ATF_TC_BODY(ptrace__ptrace_exec_disable, tc)
{
pid_t fpid, wpid;
int events, status;
ATF_REQUIRE((fpid = fork()) != -1);
if (fpid == 0) {
trace_me();
exec_thread(NULL);
}
/* The first wait() should report the stop from SIGSTOP. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(wpid == fpid);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
events = 0;
ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events,
sizeof(events)) == 0);
ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
/* Should get one event at exit. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 0);
wpid = wait(&status);
ATF_REQUIRE(wpid == -1);
ATF_REQUIRE(errno == ECHILD);
}
ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_enable);
ATF_TC_BODY(ptrace__ptrace_exec_enable, tc)
{
struct ptrace_lwpinfo pl;
pid_t fpid, wpid;
int events, status;
ATF_REQUIRE((fpid = fork()) != -1);
if (fpid == 0) {
trace_me();
exec_thread(NULL);
}
/* The first wait() should report the stop from SIGSTOP. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(wpid == fpid);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
events = PTRACE_EXEC;
ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events,
sizeof(events)) == 0);
ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
/* The next event should be for the child process's exec. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
ATF_REQUIRE((pl.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
(PL_FLAG_EXEC | PL_FLAG_SCX));
ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
/* The last event should be for the child process's exit. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 0);
wpid = wait(&status);
ATF_REQUIRE(wpid == -1);
ATF_REQUIRE(errno == ECHILD);
}
ATF_TC_WITHOUT_HEAD(ptrace__event_mask);
ATF_TC_BODY(ptrace__event_mask, tc)
{
pid_t fpid, wpid;
int events, status;
ATF_REQUIRE((fpid = fork()) != -1);
if (fpid == 0) {
trace_me();
exit(0);
}
/* The first wait() should report the stop from SIGSTOP. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(wpid == fpid);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
/* PT_FOLLOW_FORK should toggle the state of PTRACE_FORK. */
ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 1) != -1);
ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
sizeof(events)) == 0);
ATF_REQUIRE(events & PTRACE_FORK);
ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 0) != -1);
ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
sizeof(events)) == 0);
ATF_REQUIRE(!(events & PTRACE_FORK));
/* PT_LWP_EVENTS should toggle the state of PTRACE_LWP. */
ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 1) != -1);
ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
sizeof(events)) == 0);
ATF_REQUIRE(events & PTRACE_LWP);
ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 0) != -1);
ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
sizeof(events)) == 0);
ATF_REQUIRE(!(events & PTRACE_LWP));
ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
/* Should get one event at exit. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 0);
wpid = wait(&status);
ATF_REQUIRE(wpid == -1);
ATF_REQUIRE(errno == ECHILD);
}
ATF_TP_ADD_TCS(tp)
{
@ -1438,6 +1571,9 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, ptrace__lwp_events);
ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec);
ATF_TP_ADD_TC(tp, ptrace__siginfo);
ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_disable);
ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_enable);
ATF_TP_ADD_TC(tp, ptrace__event_mask);
return (atf_no_error());
}