From ea52eb8a1de290e2e0425b6dec5f6aa833d5e279 Mon Sep 17 00:00:00 2001 From: jhb Date: Mon, 15 Aug 2016 21:10:41 +0000 Subject: [PATCH] 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. git-svn-id: svn://svn.freebsd.org/base/stable/10@304188 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- bin/ps/ps.1 | 3 +- lib/libc/sys/ptrace.2 | 219 +++++++++++++++++++++++++++------- sys/i386/linux/linux_ptrace.c | 4 +- sys/kern/kern_exec.c | 4 +- sys/kern/kern_exit.c | 2 + sys/kern/kern_fork.c | 10 +- sys/kern/kern_sig.c | 3 +- sys/kern/kern_thr.c | 5 +- sys/kern/subr_syscall.c | 4 +- sys/kern/sys_process.c | 77 +++++++++--- sys/sys/proc.h | 4 +- sys/sys/ptrace.h | 20 ++-- tests/sys/kern/ptrace_test.c | 198 ++++++++++++++++++++++++++++++ 13 files changed, 471 insertions(+), 82 deletions(-) diff --git a/bin/ps/ps.1 b/bin/ps/ps.1 index 17bfa419c..0ff3268e3 100644 --- a/bin/ps/ps.1 +++ b/bin/ps/ps.1 @@ -29,7 +29,7 @@ .\" @(#)ps.1 8.3 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" -.Dd December 9, 2014 +.Dd August 12, 2016 .Dt PS 1 .Os .Sh NAME @@ -310,7 +310,6 @@ the include file .It Dv "P_ADVLOCK" Ta No "0x00001" Ta "Process may hold a POSIX advisory lock" .It Dv "P_CONTROLT" Ta No "0x00002" Ta "Has a controlling terminal" .It Dv "P_KTHREAD" Ta No "0x00004" Ta "Kernel thread" -.It Dv "P_FOLLOWFORK" Ta No "0x00008" Ta "Attach debugger to new children" .It Dv "P_PPWAIT" Ta No "0x00010" Ta "Parent is waiting for child to exec/exit" .It Dv "P_PROFIL" Ta No "0x00020" Ta "Has started profiling" .It Dv "P_STOPPROF" Ta No "0x00040" Ta "Has thread in requesting to stop prof" diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2 index 786f4576d..fc5c0ea2e 100644 --- a/lib/libc/sys/ptrace.2 +++ b/lib/libc/sys/ptrace.2 @@ -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 28, 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,121 @@ 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 +or +.Dv SIGSTOP +signals. +The tracing process can use the +.Dv PT_LWPINFO +request to determine which events are associated with a +.Dv SIGTRAP +or +.Dv SIGSTOP +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 . +The signal stop for a new child process enabled via +.Dv PTRACE_FORK +will report a +.Dv SIGSTOP +signal. +All other additional signal stops use +.Dv SIGTRAP . +.Pp +Each traced process has a tracing event mask. +An event in the traced process only reports a +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 +484,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 +571,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 diff --git a/sys/i386/linux/linux_ptrace.c b/sys/i386/linux/linux_ptrace.c index 2925e6bc0..fbe66b451 100644 --- a/sys/i386/linux/linux_ptrace.c +++ b/sys/i386/linux/linux_ptrace.c @@ -69,7 +69,7 @@ __FBSDID("$FreeBSD$"); #define PTRACE_ATTACH 16 #define PTRACE_DETACH 17 -#define PTRACE_SYSCALL 24 +#define LINUX_PTRACE_SYSCALL 24 #define PTRACE_GETREGS 12 #define PTRACE_SETREGS 13 @@ -473,7 +473,7 @@ linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) break; } - case PTRACE_SYSCALL: + case LINUX_PTRACE_SYSCALL: /* fall through */ default: printf("linux: ptrace(%u, ...) not implemented\n", diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 8357ab8a4..6b34066b0 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -899,7 +900,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 f9244af7a..b6a697c8b 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -336,6 +336,7 @@ exit1(struct thread *td, int rv) rv = p->p_xstat; /* Event handler could change exit status */ 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 @@ -519,6 +520,7 @@ exit1(struct thread *td, int rv) */ 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 3d7bf095d..600cb02df 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -411,6 +411,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, __rangeof(struct proc, p_startzero, p_endzero)); p2->p_treeflag = 0; p2->p_filemon = NULL; + p2->p_ptevents = 0; /* Tell the prison that we exist. */ prison_proc_hold(p2->p_ucred->cr_prison); @@ -710,8 +711,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, if ((flags & RFMEM) == 0 && dtrace_fasttrap_fork) dtrace_fasttrap_fork(p1, p2); #endif - 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. * @@ -1057,14 +1057,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", @@ -1091,7 +1091,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 65675ddfd..5996c606e 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -2175,9 +2175,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 8b199675c..b01aecbaa 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -252,7 +253,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; PROC_UNLOCK(p); @@ -343,7 +344,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 1ad7dd068..f2b83f09e 100644 --- a/sys/kern/subr_syscall.c +++ b/sys/kern/subr_syscall.c @@ -88,7 +88,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); } @@ -215,7 +215,7 @@ syscallret(struct thread *td, int error, struct syscall_args *sa __unused) */ 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 66e9097d4..57b9fb7c6 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -542,6 +542,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; @@ -556,6 +557,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: @@ -570,6 +572,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; @@ -603,7 +611,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; } @@ -667,6 +680,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; @@ -842,6 +857,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; @@ -860,6 +876,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); @@ -898,24 +915,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: @@ -948,24 +991,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: @@ -984,7 +1027,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); @@ -1001,7 +1044,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); */ diff --git a/sys/sys/proc.h b/sys/sys/proc.h index c4b51e7d1..34e292498 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -611,6 +611,7 @@ struct proc { our subtree. */ struct pgrp *p_pgrp; /* (c + e) Pointer to process group. */ struct filemon *p_filemon; /* (c) filemon-specific data. */ + u_int p_ptevents; /* (c) ptrace() event mask. */ }; #define p_session p_pgrp->pg_session @@ -638,7 +639,7 @@ struct proc { #define P_ADVLOCK 0x00001 /* Process may hold a POSIX advisory lock. */ #define P_CONTROLT 0x00002 /* Has a controlling terminal. */ #define P_KTHREAD 0x00004 /* Kernel thread (*). */ -#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. */ @@ -677,7 +678,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 */ diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h index a75d911d1..e2b6a5f4f 100644 --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -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-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); diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c index 54ef2cda6..44ab15b29 100644 --- a/tests/sys/kern/ptrace_test.c +++ b/tests/sys/kern/ptrace_test.c @@ -1269,6 +1269,200 @@ ATF_TC_BODY(ptrace__lwp_events_exec, tc) ATF_REQUIRE(errno == ECHILD); } +static void +handler(int sig __unused) +{ +} + +static void +signal_main(void) +{ + + signal(SIGINFO, handler); + raise(SIGINFO); + exit(0); +} + +/* + * Verify that the expected ptrace event is reported for a signal. + */ +ATF_TC_WITHOUT_HEAD(ptrace__siginfo); +ATF_TC_BODY(ptrace__siginfo, tc) +{ + struct ptrace_lwpinfo pl; + pid_t fpid, wpid; + int status; + + ATF_REQUIRE((fpid = fork()) != -1); + if (fpid == 0) { + trace_me(); + signal_main(); + } + + /* 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); + + ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); + + /* The next event should be for the SIGINFO. */ + wpid = waitpid(fpid, &status, 0); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGINFO); + + ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); + ATF_REQUIRE(pl.pl_event == PL_EVENT_SIGNAL); + ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); + ATF_REQUIRE(pl.pl_siginfo.si_code == SI_LWP); + ATF_REQUIRE(pl.pl_siginfo.si_pid == wpid); + + 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); +} + +/* + * 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) { @@ -1289,6 +1483,10 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_thread); 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()); } -- 2.42.0