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:
parent
3340c45b96
commit
8d570f64aa
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=302902
@ -2,7 +2,7 @@
|
|||||||
.\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
|
.\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
|
||||||
.\"
|
.\"
|
||||||
.\" This file is in the public domain.
|
.\" This file is in the public domain.
|
||||||
.Dd December 29, 2015
|
.Dd July 15, 2016
|
||||||
.Dt PTRACE 2
|
.Dt PTRACE 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.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
|
traced process behavior, or use of the
|
||||||
.Xr kill 2
|
.Xr kill 2
|
||||||
system call; alternatively, it may be generated by the tracing facility
|
system call; alternatively, it may be generated by the tracing facility
|
||||||
as a result of attaching, system calls, or stepping by the tracing
|
as a result of attaching, stepping by the tracing
|
||||||
process.
|
process,
|
||||||
|
or an event in the traced process.
|
||||||
The tracing process may choose to intercept the signal, using it to
|
The tracing process may choose to intercept the signal, using it to
|
||||||
observe process behavior (such as
|
observe process behavior (such as
|
||||||
.Dv SIGTRAP ) ,
|
.Dv SIGTRAP ) ,
|
||||||
@ -69,6 +70,111 @@ The
|
|||||||
system call
|
system call
|
||||||
is the mechanism by which all this happens.
|
is the mechanism by which all this happens.
|
||||||
.Pp
|
.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
|
The
|
||||||
.Fa request
|
.Fa request
|
||||||
argument specifies what operation is being performed; the meaning of
|
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
|
member of
|
||||||
.Vt "struct ptrace_lwpinfo" .
|
.Vt "struct ptrace_lwpinfo" .
|
||||||
.It PL_FLAG_CHILD
|
.It PL_FLAG_CHILD
|
||||||
The flag is set for first event reported from a new child, which is
|
The flag is set for first event reported from a new child which is
|
||||||
automatically attached due to
|
automatically attached when
|
||||||
.Dv PT_FOLLOW_FORK
|
.Dv PTRACE_FORK
|
||||||
enabled.
|
is enabled.
|
||||||
.It PL_FLAG_BORN
|
.It PL_FLAG_BORN
|
||||||
This flag is set for the first event reported from a new LWP when LWP
|
This flag is set for the first event reported from a new LWP when
|
||||||
events are enabled via
|
.Dv PTRACE_LWP
|
||||||
.Dv PT_LWP_EVENTS .
|
is enabled.
|
||||||
It is reported along with
|
It is reported along with
|
||||||
.Dv PL_FLAG_SCX
|
.Dv PL_FLAG_SCX .
|
||||||
and is always reported if LWP events are enabled.
|
|
||||||
.It PL_FLAG_EXITED
|
.It PL_FLAG_EXITED
|
||||||
This flag is set for the last event reported by an exiting LWP when
|
This flag is set for the last event reported by an exiting LWP when
|
||||||
LWP events are enabled via
|
.Dv PTRACE_LWP
|
||||||
.Dv PT_LWP_EVENTS .
|
is enabled.
|
||||||
Note that this event is not reported when the last LWP in a process exits.
|
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
|
The termination of the last thread is reported via a normal process exit
|
||||||
event.
|
event.
|
||||||
@ -456,50 +561,72 @@ This request will suspend the specified thread.
|
|||||||
.It PT_RESUME
|
.It PT_RESUME
|
||||||
This request will resume the specified thread.
|
This request will resume the specified thread.
|
||||||
.It PT_TO_SCE
|
.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
|
.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
|
.It PT_SYSCALL
|
||||||
This request will trace the specified process
|
This request will set the
|
||||||
on each system call entry and exit.
|
.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
|
.It PT_FOLLOW_FORK
|
||||||
This request controls tracing for new child processes of a traced process.
|
This request controls tracing for new child processes of a traced process.
|
||||||
If
|
If
|
||||||
.Fa data
|
.Fa data
|
||||||
is non-zero,
|
is non-zero,
|
||||||
then new child processes will enable tracing and stop before executing their
|
.Dv PTRACE_FORK
|
||||||
first instruction.
|
is set in the traced process's event tracing mask.
|
||||||
If
|
If
|
||||||
.Fa data
|
.Fa data
|
||||||
is zero, then new child processes will execute without tracing enabled.
|
is zero,
|
||||||
By default, tracing is not enabled for new child processes.
|
.Dv PTRACE_FORK
|
||||||
Child processes do not inherit this property.
|
is cleared from the traced process's event tracing mask.
|
||||||
The traced process will set the
|
|
||||||
.Dv PL_FLAG_FORKED
|
|
||||||
flag upon exit from a system call that creates a new process.
|
|
||||||
.It PT_LWP_EVENTS
|
.It PT_LWP_EVENTS
|
||||||
This request controls tracing of LWP creation and destruction.
|
This request controls tracing of LWP creation and destruction.
|
||||||
If
|
If
|
||||||
.Fa data
|
.Fa data
|
||||||
is non-zero,
|
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
|
If
|
||||||
.Fa data
|
.Fa data
|
||||||
is zero,
|
is zero,
|
||||||
then LWP creation and destruction events will not be reported.
|
.Dv PTRACE_LWP
|
||||||
By default, tracing is not enabled for LWP events.
|
is cleared from the traced process's event tracing mask.
|
||||||
Child processes do not inherit this property.
|
.It PT_GET_EVENT_MASK
|
||||||
New LWPs will stop to report an event with
|
This request reads the traced process's event tracing mask into the
|
||||||
.Dv PL_FLAG_BORN
|
integer pointed to by
|
||||||
set before executing their first instruction.
|
.Fa addr .
|
||||||
Exiting LWPs will stop to report an event with
|
The size of the integer must be passed in
|
||||||
.Dv PL_FLAG_EXITED
|
.Fa data .
|
||||||
set before completing their termination.
|
.It PT_SET_EVENT_MASK
|
||||||
.Pp
|
This request sets the traced process's event tracing mask from the
|
||||||
Note that new processes do not report an event for the creation of their
|
integer pointed to by
|
||||||
initial thread,
|
.Fa addr .
|
||||||
and exiting processes do not report an event for the termination of the
|
The size of the integer must be passed in
|
||||||
last thread.
|
.Fa data .
|
||||||
.It PT_VM_TIMESTAMP
|
.It PT_VM_TIMESTAMP
|
||||||
This request returns the generation number or timestamp of the memory map of
|
This request returns the generation number or timestamp of the memory map of
|
||||||
the traced process as the return value from
|
the traced process as the return value from
|
||||||
|
@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/priv.h>
|
#include <sys/priv.h>
|
||||||
#include <sys/proc.h>
|
#include <sys/proc.h>
|
||||||
#include <sys/pioctl.h>
|
#include <sys/pioctl.h>
|
||||||
|
#include <sys/ptrace.h>
|
||||||
#include <sys/namei.h>
|
#include <sys/namei.h>
|
||||||
#include <sys/resourcevar.h>
|
#include <sys/resourcevar.h>
|
||||||
#include <sys/rwlock.h>
|
#include <sys/rwlock.h>
|
||||||
@ -903,7 +904,8 @@ do_execve(td, args, mac_p)
|
|||||||
|
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
PROC_LOCK(p);
|
PROC_LOCK(p);
|
||||||
td->td_dbgflags |= TDB_EXEC;
|
if (p->p_ptevents & PTRACE_EXEC)
|
||||||
|
td->td_dbgflags |= TDB_EXEC;
|
||||||
PROC_UNLOCK(p);
|
PROC_UNLOCK(p);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -338,6 +338,7 @@ exit1(struct thread *td, int rval, int signo)
|
|||||||
PROC_LOCK(p);
|
PROC_LOCK(p);
|
||||||
stopprofclock(p);
|
stopprofclock(p);
|
||||||
p->p_flag &= ~(P_TRACED | P_PPWAIT | P_PPTRACE);
|
p->p_flag &= ~(P_TRACED | P_PPWAIT | P_PPTRACE);
|
||||||
|
p->p_ptevents = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stop the real interval timer. If the handler is currently
|
* 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);
|
clear_orphan(q);
|
||||||
q->p_flag &= ~(P_TRACED | P_STOPPED_TRACE);
|
q->p_flag &= ~(P_TRACED | P_STOPPED_TRACE);
|
||||||
|
q->p_ptevents = 0;
|
||||||
FOREACH_THREAD_IN_PROC(q, tdt)
|
FOREACH_THREAD_IN_PROC(q, tdt)
|
||||||
tdt->td_dbgflags &= ~TDB_SUSPEND;
|
tdt->td_dbgflags &= ~TDB_SUSPEND;
|
||||||
kern_psignal(q, SIGKILL);
|
kern_psignal(q, SIGKILL);
|
||||||
|
@ -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.
|
* but before we wait for the debugger.
|
||||||
*/
|
*/
|
||||||
_PHOLD(p2);
|
_PHOLD(p2);
|
||||||
if ((p1->p_flag & (P_TRACED | P_FOLLOWFORK)) == (P_TRACED |
|
if (p1->p_ptevents & PTRACE_FORK) {
|
||||||
P_FOLLOWFORK)) {
|
|
||||||
/*
|
/*
|
||||||
* Arrange for debugger to receive the fork event.
|
* 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) {
|
if (td->td_dbgflags & TDB_STOPATFORK) {
|
||||||
sx_xlock(&proctree_lock);
|
sx_xlock(&proctree_lock);
|
||||||
PROC_LOCK(p);
|
PROC_LOCK(p);
|
||||||
if ((p->p_pptr->p_flag & (P_TRACED | P_FOLLOWFORK)) ==
|
if (p->p_pptr->p_ptevents & PTRACE_FORK) {
|
||||||
(P_TRACED | P_FOLLOWFORK)) {
|
|
||||||
/*
|
/*
|
||||||
* If debugger still wants auto-attach for the
|
* If debugger still wants auto-attach for the
|
||||||
* parent's children, do it now.
|
* parent's children, do it now.
|
||||||
*/
|
*/
|
||||||
dbg = p->p_pptr->p_pptr;
|
dbg = p->p_pptr->p_pptr;
|
||||||
p->p_flag |= P_TRACED;
|
p->p_flag |= P_TRACED;
|
||||||
|
p->p_ptevents = PTRACE_DEFAULT;
|
||||||
p->p_oppid = p->p_pptr->p_pid;
|
p->p_oppid = p->p_pptr->p_pid;
|
||||||
CTR2(KTR_PTRACE,
|
CTR2(KTR_PTRACE,
|
||||||
"fork_return: attaching to new child pid %d: oppid %d",
|
"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);
|
PROC_LOCK(p);
|
||||||
td->td_dbgflags |= TDB_SCX;
|
td->td_dbgflags |= TDB_SCX;
|
||||||
_STOPEVENT(p, S_SCX, td->td_dbg_sc_code);
|
_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)
|
(td->td_dbgflags & TDB_BORN) != 0)
|
||||||
ptracestop(td, SIGTRAP);
|
ptracestop(td, SIGTRAP);
|
||||||
td->td_dbgflags &= ~(TDB_SCX | TDB_BORN);
|
td->td_dbgflags &= ~(TDB_SCX | TDB_BORN);
|
||||||
|
@ -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)))
|
!((prop & SA_CONT) && (p->p_flag & P_STOPPED_SIG)))
|
||||||
return (ret);
|
return (ret);
|
||||||
/*
|
/*
|
||||||
* SIGKILL: Remove procfs STOPEVENTs.
|
* SIGKILL: Remove procfs STOPEVENTs and ptrace events.
|
||||||
*/
|
*/
|
||||||
if (sig == SIGKILL) {
|
if (sig == SIGKILL) {
|
||||||
|
p->p_ptevents = 0;
|
||||||
/* from procfs_ioctl.c: PIOCBIC */
|
/* from procfs_ioctl.c: PIOCBIC */
|
||||||
p->p_stops = 0;
|
p->p_stops = 0;
|
||||||
/* from procfs_ioctl.c: PIOCCONT */
|
/* from procfs_ioctl.c: PIOCCONT */
|
||||||
|
@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/priv.h>
|
#include <sys/priv.h>
|
||||||
#include <sys/proc.h>
|
#include <sys/proc.h>
|
||||||
#include <sys/posix4.h>
|
#include <sys/posix4.h>
|
||||||
|
#include <sys/ptrace.h>
|
||||||
#include <sys/racct.h>
|
#include <sys/racct.h>
|
||||||
#include <sys/resourcevar.h>
|
#include <sys/resourcevar.h>
|
||||||
#include <sys/rwlock.h>
|
#include <sys/rwlock.h>
|
||||||
@ -254,7 +255,7 @@ thread_create(struct thread *td, struct rtprio *rtp,
|
|||||||
thread_unlock(td);
|
thread_unlock(td);
|
||||||
if (P_SHOULDSTOP(p))
|
if (P_SHOULDSTOP(p))
|
||||||
newtd->td_flags |= TDF_ASTPENDING | TDF_NEEDSUSPCHK;
|
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;
|
newtd->td_dbgflags |= TDB_BORN;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -354,7 +355,7 @@ kern_thr_exit(struct thread *td)
|
|||||||
|
|
||||||
p->p_pendingexits++;
|
p->p_pendingexits++;
|
||||||
td->td_dbgflags |= TDB_EXIT;
|
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);
|
ptracestop(td, SIGTRAP);
|
||||||
PROC_UNLOCK(p);
|
PROC_UNLOCK(p);
|
||||||
tidhash_remove(td);
|
tidhash_remove(td);
|
||||||
|
@ -87,7 +87,7 @@ syscallenter(struct thread *td, struct syscall_args *sa)
|
|||||||
PROC_LOCK(p);
|
PROC_LOCK(p);
|
||||||
td->td_dbg_sc_code = sa->code;
|
td->td_dbg_sc_code = sa->code;
|
||||||
td->td_dbg_sc_narg = sa->narg;
|
td->td_dbg_sc_narg = sa->narg;
|
||||||
if (p->p_stops & S_PT_SCE)
|
if (p->p_ptevents & PTRACE_SCE)
|
||||||
ptracestop((td), SIGTRAP);
|
ptracestop((td), SIGTRAP);
|
||||||
PROC_UNLOCK(p);
|
PROC_UNLOCK(p);
|
||||||
}
|
}
|
||||||
@ -208,7 +208,7 @@ syscallret(struct thread *td, int error, struct syscall_args *sa)
|
|||||||
*/
|
*/
|
||||||
if (traced &&
|
if (traced &&
|
||||||
((td->td_dbgflags & (TDB_FORK | TDB_EXEC)) != 0 ||
|
((td->td_dbgflags & (TDB_FORK | TDB_EXEC)) != 0 ||
|
||||||
(p->p_stops & S_PT_SCX) != 0))
|
(p->p_ptevents & PTRACE_SCX) != 0))
|
||||||
ptracestop(td, SIGTRAP);
|
ptracestop(td, SIGTRAP);
|
||||||
td->td_dbgflags &= ~(TDB_SCX | TDB_EXEC | TDB_FORK);
|
td->td_dbgflags &= ~(TDB_SCX | TDB_EXEC | TDB_FORK);
|
||||||
PROC_UNLOCK(p);
|
PROC_UNLOCK(p);
|
||||||
|
@ -586,6 +586,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
|
|||||||
struct ptrace_lwpinfo32 pl32;
|
struct ptrace_lwpinfo32 pl32;
|
||||||
struct ptrace_vm_entry32 pve32;
|
struct ptrace_vm_entry32 pve32;
|
||||||
#endif
|
#endif
|
||||||
|
int ptevents;
|
||||||
} r;
|
} r;
|
||||||
void *addr;
|
void *addr;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
@ -600,6 +601,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
|
|||||||
AUDIT_ARG_VALUE(uap->data);
|
AUDIT_ARG_VALUE(uap->data);
|
||||||
addr = &r;
|
addr = &r;
|
||||||
switch (uap->req) {
|
switch (uap->req) {
|
||||||
|
case PT_GET_EVENT_MASK:
|
||||||
case PT_GETREGS:
|
case PT_GETREGS:
|
||||||
case PT_GETFPREGS:
|
case PT_GETFPREGS:
|
||||||
case PT_GETDBREGS:
|
case PT_GETDBREGS:
|
||||||
@ -614,6 +616,12 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
|
|||||||
case PT_SETDBREGS:
|
case PT_SETDBREGS:
|
||||||
error = COPYIN(uap->addr, &r.dbreg, sizeof r.dbreg);
|
error = COPYIN(uap->addr, &r.dbreg, sizeof r.dbreg);
|
||||||
break;
|
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:
|
case PT_IO:
|
||||||
error = COPYIN(uap->addr, &r.piod, sizeof r.piod);
|
error = COPYIN(uap->addr, &r.piod, sizeof r.piod);
|
||||||
break;
|
break;
|
||||||
@ -647,7 +655,12 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
|
|||||||
case PT_GETDBREGS:
|
case PT_GETDBREGS:
|
||||||
error = COPYOUT(&r.dbreg, uap->addr, sizeof r.dbreg);
|
error = COPYOUT(&r.dbreg, uap->addr, sizeof r.dbreg);
|
||||||
break;
|
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:
|
case PT_LWPINFO:
|
||||||
|
/* NB: The size in uap->data is validated in kern_ptrace(). */
|
||||||
error = copyout(&r.pl, uap->addr, uap->data);
|
error = copyout(&r.pl, uap->addr, uap->data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -711,6 +724,8 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
|
|||||||
case PT_SYSCALL:
|
case PT_SYSCALL:
|
||||||
case PT_FOLLOW_FORK:
|
case PT_FOLLOW_FORK:
|
||||||
case PT_LWP_EVENTS:
|
case PT_LWP_EVENTS:
|
||||||
|
case PT_GET_EVENT_MASK:
|
||||||
|
case PT_SET_EVENT_MASK:
|
||||||
case PT_DETACH:
|
case PT_DETACH:
|
||||||
sx_xlock(&proctree_lock);
|
sx_xlock(&proctree_lock);
|
||||||
proctree_locked = 1;
|
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:
|
case PT_TRACE_ME:
|
||||||
/* set my trace flag and "owner" so it can read/write me */
|
/* set my trace flag and "owner" so it can read/write me */
|
||||||
p->p_flag |= P_TRACED;
|
p->p_flag |= P_TRACED;
|
||||||
|
p->p_ptevents = PTRACE_DEFAULT;
|
||||||
if (p->p_flag & P_PPWAIT)
|
if (p->p_flag & P_PPWAIT)
|
||||||
p->p_flag |= P_PPTRACE;
|
p->p_flag |= P_PPTRACE;
|
||||||
p->p_oppid = p->p_pptr->p_pid;
|
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".
|
* on a "detach".
|
||||||
*/
|
*/
|
||||||
p->p_flag |= P_TRACED;
|
p->p_flag |= P_TRACED;
|
||||||
|
p->p_ptevents = PTRACE_DEFAULT;
|
||||||
p->p_oppid = p->p_pptr->p_pid;
|
p->p_oppid = p->p_pptr->p_pid;
|
||||||
if (p->p_pptr != td->td_proc) {
|
if (p->p_pptr != td->td_proc) {
|
||||||
proc_reparent(p, 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:
|
case PT_FOLLOW_FORK:
|
||||||
CTR3(KTR_PTRACE, "PT_FOLLOW_FORK: pid %d %s -> %s", p->p_pid,
|
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");
|
data ? "enabled" : "disabled");
|
||||||
if (data)
|
if (data)
|
||||||
p->p_flag |= P_FOLLOWFORK;
|
p->p_ptevents |= PTRACE_FORK;
|
||||||
else
|
else
|
||||||
p->p_flag &= ~P_FOLLOWFORK;
|
p->p_ptevents &= ~PTRACE_FORK;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PT_LWP_EVENTS:
|
case PT_LWP_EVENTS:
|
||||||
CTR3(KTR_PTRACE, "PT_LWP_EVENTS: pid %d %s -> %s", p->p_pid,
|
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");
|
data ? "enabled" : "disabled");
|
||||||
if (data)
|
if (data)
|
||||||
p->p_flag2 |= P2_LWP_EVENTS;
|
p->p_ptevents |= PTRACE_LWP;
|
||||||
else
|
else
|
||||||
p->p_flag2 &= ~P2_LWP_EVENTS;
|
p->p_ptevents &= ~PTRACE_LWP;
|
||||||
break;
|
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_STEP:
|
||||||
case PT_CONTINUE:
|
case PT_CONTINUE:
|
||||||
case PT_TO_SCE:
|
case PT_TO_SCE:
|
||||||
@ -991,24 +1034,24 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
|
|||||||
}
|
}
|
||||||
switch (req) {
|
switch (req) {
|
||||||
case PT_TO_SCE:
|
case PT_TO_SCE:
|
||||||
p->p_stops |= S_PT_SCE;
|
p->p_ptevents |= PTRACE_SCE;
|
||||||
CTR4(KTR_PTRACE,
|
CTR4(KTR_PTRACE,
|
||||||
"PT_TO_SCE: pid %d, stops = %#x, PC = %#lx, sig = %d",
|
"PT_TO_SCE: pid %d, events = %#x, PC = %#lx, sig = %d",
|
||||||
p->p_pid, p->p_stops,
|
p->p_pid, p->p_ptevents,
|
||||||
(u_long)(uintfptr_t)addr, data);
|
(u_long)(uintfptr_t)addr, data);
|
||||||
break;
|
break;
|
||||||
case PT_TO_SCX:
|
case PT_TO_SCX:
|
||||||
p->p_stops |= S_PT_SCX;
|
p->p_ptevents |= PTRACE_SCX;
|
||||||
CTR4(KTR_PTRACE,
|
CTR4(KTR_PTRACE,
|
||||||
"PT_TO_SCX: pid %d, stops = %#x, PC = %#lx, sig = %d",
|
"PT_TO_SCX: pid %d, events = %#x, PC = %#lx, sig = %d",
|
||||||
p->p_pid, p->p_stops,
|
p->p_pid, p->p_ptevents,
|
||||||
(u_long)(uintfptr_t)addr, data);
|
(u_long)(uintfptr_t)addr, data);
|
||||||
break;
|
break;
|
||||||
case PT_SYSCALL:
|
case PT_SYSCALL:
|
||||||
p->p_stops |= S_PT_SCE | S_PT_SCX;
|
p->p_ptevents |= PTRACE_SYSCALL;
|
||||||
CTR4(KTR_PTRACE,
|
CTR4(KTR_PTRACE,
|
||||||
"PT_SYSCALL: pid %d, stops = %#x, PC = %#lx, sig = %d",
|
"PT_SYSCALL: pid %d, events = %#x, PC = %#lx, sig = %d",
|
||||||
p->p_pid, p->p_stops,
|
p->p_pid, p->p_ptevents,
|
||||||
(u_long)(uintfptr_t)addr, data);
|
(u_long)(uintfptr_t)addr, data);
|
||||||
break;
|
break;
|
||||||
case PT_CONTINUE:
|
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
|
* parent. Otherwise the debugee will be set
|
||||||
* as an orphan of the debugger.
|
* 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) {
|
if (p->p_oppid != p->p_pptr->p_pid) {
|
||||||
PROC_LOCK(p->p_pptr);
|
PROC_LOCK(p->p_pptr);
|
||||||
sigqueue_take(p->p_ksi);
|
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",
|
CTR2(KTR_PTRACE, "PT_DETACH: pid %d, sig %d",
|
||||||
p->p_pid, data);
|
p->p_pid, data);
|
||||||
p->p_oppid = 0;
|
p->p_oppid = 0;
|
||||||
p->p_stops = 0;
|
p->p_ptevents = 0;
|
||||||
|
|
||||||
/* should we send SIGCHLD? */
|
/* should we send SIGCHLD? */
|
||||||
/* childproc_continued(p); */
|
/* childproc_continued(p); */
|
||||||
|
@ -582,6 +582,7 @@ struct proc {
|
|||||||
u_int p_stype; /* (c) Stop event type. */
|
u_int p_stype; /* (c) Stop event type. */
|
||||||
char p_step; /* (c) Process is stopped. */
|
char p_step; /* (c) Process is stopped. */
|
||||||
u_char p_pfsflags; /* (c) Procfs flags. */
|
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 nlminfo *p_nlminfo; /* (?) Only used by/for lockd. */
|
||||||
struct kaioinfo *p_aioinfo; /* (y) ASYNC I/O info. */
|
struct kaioinfo *p_aioinfo; /* (y) ASYNC I/O info. */
|
||||||
struct thread *p_singlethread;/* (c + j) If single threading this is it */
|
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_ADVLOCK 0x00001 /* Process may hold a POSIX advisory lock. */
|
||||||
#define P_CONTROLT 0x00002 /* Has a controlling terminal. */
|
#define P_CONTROLT 0x00002 /* Has a controlling terminal. */
|
||||||
#define P_KPROC 0x00004 /* Kernel process. */
|
#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_PPWAIT 0x00010 /* Parent is waiting for child to exec/exit. */
|
||||||
#define P_PROFIL 0x00020 /* Has started profiling. */
|
#define P_PROFIL 0x00020 /* Has started profiling. */
|
||||||
#define P_STOPPROF 0x00040 /* Has thread requesting to stop 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 0x00000002 /* No ptrace(2) attach or coredumps. */
|
||||||
#define P2_NOTRACE_EXEC 0x00000004 /* Keep P2_NOPTRACE on exec(2). */
|
#define P2_NOTRACE_EXEC 0x00000004 /* Keep P2_NOPTRACE on exec(2). */
|
||||||
#define P2_AST_SU 0x00000008 /* Handles SU ast for kthreads. */
|
#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. */
|
/* Flags protected by proctree_lock, kept in p_treeflags. */
|
||||||
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
|
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
|
||||||
|
@ -66,6 +66,9 @@
|
|||||||
#define PT_FOLLOW_FORK 23
|
#define PT_FOLLOW_FORK 23
|
||||||
#define PT_LWP_EVENTS 24 /* report LWP birth and exit */
|
#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_GETREGS 33 /* get general-purpose registers */
|
||||||
#define PT_SETREGS 34 /* set general-purpose registers */
|
#define PT_SETREGS 34 /* set general-purpose registers */
|
||||||
#define PT_GETFPREGS 35 /* get floating-point registers */
|
#define PT_GETFPREGS 35 /* get floating-point registers */
|
||||||
@ -79,6 +82,16 @@
|
|||||||
#define PT_FIRSTMACH 64 /* for machine-specific requests */
|
#define PT_FIRSTMACH 64 /* for machine-specific requests */
|
||||||
#include <machine/ptrace.h> /* machine-specific requests, if any */
|
#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 {
|
struct ptrace_io_desc {
|
||||||
int piod_op; /* I/O operation */
|
int piod_op; /* I/O operation */
|
||||||
void *piod_offs; /* child offset */
|
void *piod_offs; /* child offset */
|
||||||
@ -136,13 +149,6 @@ struct ptrace_vm_entry {
|
|||||||
|
|
||||||
#ifdef _KERNEL
|
#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_set_pc(struct thread *_td, unsigned long _addr);
|
||||||
int ptrace_single_step(struct thread *_td);
|
int ptrace_single_step(struct thread *_td);
|
||||||
int ptrace_clear_single_step(struct thread *_td);
|
int ptrace_clear_single_step(struct thread *_td);
|
||||||
|
@ -1416,6 +1416,139 @@ ATF_TC_BODY(ptrace__siginfo, tc)
|
|||||||
ATF_REQUIRE(errno == ECHILD);
|
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)
|
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);
|
||||||
ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec);
|
ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec);
|
||||||
ATF_TP_ADD_TC(tp, ptrace__siginfo);
|
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());
|
return (atf_no_error());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user