From owner-svn-src-all@freebsd.org Fri Aug 19 20:18:00 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 9EEB7BBE5F3; Fri, 19 Aug 2016 20:18:00 +0000 (UTC) (envelope-from jhb@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 7A2341266; Fri, 19 Aug 2016 20:18:00 +0000 (UTC) (envelope-from jhb@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u7JKHxNW083439; Fri, 19 Aug 2016 20:17:59 GMT (envelope-from jhb@FreeBSD.org) Received: (from jhb@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u7JKHxqe083432; Fri, 19 Aug 2016 20:17:59 GMT (envelope-from jhb@FreeBSD.org) Message-Id: <201608192017.u7JKHxqe083432@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: jhb set sender to jhb@FreeBSD.org using -f From: John Baldwin Date: Fri, 19 Aug 2016 20:17:59 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r304499 - in stable: 10/lib/libc/sys 10/sys/kern 10/sys/sys 10/tests/sys/kern 11/lib/libc/sys 11/sys/kern 11/sys/sys 11/tests/sys/kern X-SVN-Group: stable-11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 19 Aug 2016 20:18:00 -0000 Author: jhb Date: Fri Aug 19 20:17:57 2016 New Revision: 304499 URL: https://svnweb.freebsd.org/changeset/base/304499 Log: MFC 303001: Add PTRACE_VFORK to trace vfork events. First, PL_FLAG_FORKED events now also set a PL_FLAG_VFORKED flag when the new child was created via vfork() rather than fork(). Second, a new PL_FLAG_VFORK_DONE event can now be enabled via the PTRACE_VFORK event mask. This new stop is reported after the vfork parent resumes due to the child calling exit or exec. Debuggers can use this stop to reinsert breakpoints in the vfork parent process before it resumes. Modified: stable/11/lib/libc/sys/ptrace.2 stable/11/sys/kern/kern_fork.c stable/11/sys/kern/subr_syscall.c stable/11/sys/kern/sys_process.c stable/11/sys/sys/proc.h stable/11/sys/sys/ptrace.h stable/11/tests/sys/kern/ptrace_test.c Directory Properties: stable/11/ (props changed) Changes in other areas also in this revision: Modified: stable/10/lib/libc/sys/ptrace.2 stable/10/sys/kern/kern_fork.c stable/10/sys/kern/subr_syscall.c stable/10/sys/kern/sys_process.c stable/10/sys/sys/proc.h stable/10/sys/sys/ptrace.h stable/10/tests/sys/kern/ptrace_test.c Directory Properties: stable/10/ (props changed) Modified: stable/11/lib/libc/sys/ptrace.2 ============================================================================== --- stable/11/lib/libc/sys/ptrace.2 Fri Aug 19 19:31:55 2016 (r304498) +++ stable/11/lib/libc/sys/ptrace.2 Fri Aug 19 20:17:57 2016 (r304499) @@ -151,6 +151,11 @@ The process ID of the new child process .Va pl_child_pid member of .Vt "struct ptrace_lwpinfo" . +If the new child process was created via +.Xr vfork 2 , +the traced process's stop will also include the +.Dv PL_FLAG_VFORKED +flag. 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. @@ -173,6 +178,33 @@ Note that new processes do not report an initial thread, and exiting processes do not report an event for the termination of the last thread. +.It Dv PTRACE_VFORK +Report a stop event when a parent process resumes after a +.Xr vfork 2 . +.Pp +When a thread in the traced process creates a new child process via +.Xr vfork 2 , +the stop that reports +.Dv PL_FLAG_FORKED +and +.Dv PL_FLAG_SCX +occurs just after the child process is created, +but before the thread waits for the child process to stop sharing process +memory. +If a debugger is not tracing the new child process, +it must ensure that no breakpoints are enabled in the shared process +memory before detaching from the new child process. +This means that no breakpoints are enabled in the parent process either. +.Pp +The +.Dv PTRACE_VFORK +flag enables a new stop that indicates when the new child process stops +sharing the process memory of the parent process. +A debugger can reinsert breakpoints in the parent process and resume it +in response to this event. +This event is indicated by setting the +.Dv PL_FLAG_VFORK_DONE +flag. .El .Pp The default tracing event mask when attaching to a process via @@ -501,6 +533,16 @@ 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. +.It PL_FLAG_VFORKED +Indicates that the thread is returning from a call to +.Xr vfork 2 +that created a new child process. +This flag is set in addition to +.Dv PL_FLAG_FORKED . +.It PL_FLAG_VFORK_DONE +Indicates that the thread has resumed after a child process created via +.Xr vfork 2 +has stopped sharing its address space with the traced process. .El .It pl_sigmask The current signal mask of the LWP Modified: stable/11/sys/kern/kern_fork.c ============================================================================== --- stable/11/sys/kern/kern_fork.c Fri Aug 19 19:31:55 2016 (r304498) +++ stable/11/sys/kern/kern_fork.c Fri Aug 19 20:17:57 2016 (r304499) @@ -736,6 +736,7 @@ do_fork(struct thread *td, struct fork_r if (fr->fr_flags & RFPPWAIT) { td->td_pflags |= TDP_RFPPWAIT; td->td_rfppwait_p = p2; + td->td_dbgflags |= TDB_VFORK; } PROC_UNLOCK(p2); Modified: stable/11/sys/kern/subr_syscall.c ============================================================================== --- stable/11/sys/kern/subr_syscall.c Fri Aug 19 19:31:55 2016 (r304498) +++ stable/11/sys/kern/subr_syscall.c Fri Aug 19 20:17:57 2016 (r304499) @@ -242,5 +242,13 @@ again: cv_timedwait(&p2->p_pwait, &p2->p_mtx, hz); } PROC_UNLOCK(p2); + + if (td->td_dbgflags & TDB_VFORK) { + PROC_LOCK(p); + if (p->p_ptevents & PTRACE_VFORK) + ptracestop(td, SIGTRAP); + td->td_dbgflags &= ~TDB_VFORK; + PROC_UNLOCK(p); + } } } Modified: stable/11/sys/kern/sys_process.c ============================================================================== --- stable/11/sys/kern/sys_process.c Fri Aug 19 19:31:55 2016 (r304498) +++ stable/11/sys/kern/sys_process.c Fri Aug 19 20:17:57 2016 (r304499) @@ -1000,7 +1000,7 @@ kern_ptrace(struct thread *td, int req, } tmp = *(int *)addr; if ((tmp & ~(PTRACE_EXEC | PTRACE_SCE | PTRACE_SCX | - PTRACE_FORK | PTRACE_LWP)) != 0) { + PTRACE_FORK | PTRACE_LWP | PTRACE_VFORK)) != 0) { error = EINVAL; break; } @@ -1321,7 +1321,11 @@ kern_ptrace(struct thread *td, int req, if (td2->td_dbgflags & TDB_FORK) { pl->pl_flags |= PL_FLAG_FORKED; pl->pl_child_pid = td2->td_dbg_forked; - } + if (td2->td_dbgflags & TDB_VFORK) + pl->pl_flags |= PL_FLAG_VFORKED; + } else if ((td2->td_dbgflags & (TDB_SCX | TDB_VFORK)) == + TDB_VFORK) + pl->pl_flags |= PL_FLAG_VFORK_DONE; if (td2->td_dbgflags & TDB_CHILD) pl->pl_flags |= PL_FLAG_CHILD; if (td2->td_dbgflags & TDB_BORN) Modified: stable/11/sys/sys/proc.h ============================================================================== --- stable/11/sys/sys/proc.h Fri Aug 19 19:31:55 2016 (r304498) +++ stable/11/sys/sys/proc.h Fri Aug 19 20:17:57 2016 (r304499) @@ -422,6 +422,7 @@ do { \ #define TDB_CHILD 0x00000100 /* New child indicator for ptrace() */ #define TDB_BORN 0x00000200 /* New LWP indicator for ptrace() */ #define TDB_EXIT 0x00000400 /* Exiting LWP indicator for ptrace() */ +#define TDB_VFORK 0x00000800 /* vfork indicator for ptrace() */ #define TDB_FSTP 0x00001000 /* The thread is PT_ATTACH leader */ /* Modified: stable/11/sys/sys/ptrace.h ============================================================================== --- stable/11/sys/sys/ptrace.h Fri Aug 19 19:31:55 2016 (r304498) +++ stable/11/sys/sys/ptrace.h Fri Aug 19 20:17:57 2016 (r304499) @@ -89,6 +89,7 @@ #define PTRACE_SYSCALL (PTRACE_SCE | PTRACE_SCX) #define PTRACE_FORK 0x0008 #define PTRACE_LWP 0x0010 +#define PTRACE_VFORK 0x0020 #define PTRACE_DEFAULT (PTRACE_EXEC) @@ -124,6 +125,8 @@ struct ptrace_lwpinfo { #define PL_FLAG_CHILD 0x80 /* I am from child */ #define PL_FLAG_BORN 0x100 /* new LWP */ #define PL_FLAG_EXITED 0x200 /* exiting LWP */ +#define PL_FLAG_VFORKED 0x400 /* new child via vfork */ +#define PL_FLAG_VFORK_DONE 0x800 /* vfork parent has resumed */ sigset_t pl_sigmask; /* LWP signal mask */ sigset_t pl_siglist; /* LWP pending signal */ struct __siginfo pl_siginfo; /* siginfo for signal */ Modified: stable/11/tests/sys/kern/ptrace_test.c ============================================================================== --- stable/11/tests/sys/kern/ptrace_test.c Fri Aug 19 19:31:55 2016 (r304498) +++ stable/11/tests/sys/kern/ptrace_test.c Fri Aug 19 20:17:57 2016 (r304499) @@ -1549,6 +1549,130 @@ ATF_TC_BODY(ptrace__event_mask, tc) ATF_REQUIRE(errno == ECHILD); } +/* + * Verify that the expected ptrace events are reported for PTRACE_VFORK. + */ +ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork); +ATF_TC_BODY(ptrace__ptrace_vfork, tc) +{ + struct ptrace_lwpinfo pl; + pid_t fpid, wpid; + int events, status; + + ATF_REQUIRE((fpid = fork()) != -1); + if (fpid == 0) { + trace_me(); + follow_fork_parent(true); + } + + /* 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_GET_EVENT_MASK, fpid, (caddr_t)&events, + sizeof(events)) == 0); + events |= PTRACE_VFORK; + ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, + sizeof(events)) == 0); + + /* Continue the child ignoring the SIGSTOP. */ + ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1); + + /* The next event should report the end of the vfork. */ + wpid = wait(&status); + ATF_REQUIRE(wpid == fpid); + 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_VFORK_DONE) != 0); + + ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == fpid); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); +} + +ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork_follow); +ATF_TC_BODY(ptrace__ptrace_vfork_follow, tc) +{ + struct ptrace_lwpinfo pl[2]; + pid_t children[2], fpid, wpid; + int events, status; + + ATF_REQUIRE((fpid = fork()) != -1); + if (fpid == 0) { + trace_me(); + follow_fork_parent(true); + } + + /* Parent process. */ + children[0] = fpid; + + /* The first wait() should report the stop from SIGSTOP. */ + wpid = waitpid(children[0], &status, 0); + ATF_REQUIRE(wpid == children[0]); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); + + ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, children[0], (caddr_t)&events, + sizeof(events)) == 0); + events |= PTRACE_FORK | PTRACE_VFORK; + ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, children[0], (caddr_t)&events, + sizeof(events)) == 0); + + /* Continue the child ignoring the SIGSTOP. */ + ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); + + /* Wait for both halves of the fork event to get reported. */ + children[1] = handle_fork_events(children[0], pl); + ATF_REQUIRE(children[1] > 0); + + ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORKED) != 0); + + ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); + ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); + + /* + * The child can't exit until the grandchild reports status, so the + * grandchild should report its exit first to the debugger. + */ + wpid = waitpid(children[1], &status, 0); + ATF_REQUIRE(wpid == children[1]); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 2); + + /* + * The child should report it's vfork() completion before it + * exits. + */ + wpid = wait(&status); + ATF_REQUIRE(wpid == children[0]); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); + ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl[0], sizeof(pl[0])) != + -1); + ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORK_DONE) != 0); + + ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == children[0]); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); +} + ATF_TP_ADD_TCS(tp) { @@ -1574,6 +1698,8 @@ ATF_TP_ADD_TCS(tp) 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); + ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork); + ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork_follow); return (atf_no_error()); }