Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 19 Aug 2016 20:17:58 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@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
Message-ID:  <201608192017.u7JKHw4g083420@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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/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)

Changes in other areas also in this revision:
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)

Modified: stable/10/lib/libc/sys/ptrace.2
==============================================================================
--- stable/10/lib/libc/sys/ptrace.2	Fri Aug 19 19:31:55 2016	(r304498)
+++ stable/10/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/10/sys/kern/kern_fork.c
==============================================================================
--- stable/10/sys/kern/kern_fork.c	Fri Aug 19 19:31:55 2016	(r304498)
+++ stable/10/sys/kern/kern_fork.c	Fri Aug 19 20:17:57 2016	(r304499)
@@ -728,6 +728,7 @@ do_fork(struct thread *td, int flags, st
 	if (flags & RFPPWAIT) {
 		td->td_pflags |= TDP_RFPPWAIT;
 		td->td_rfppwait_p = p2;
+		td->td_dbgflags |= TDB_VFORK;
 	}
 	PROC_UNLOCK(p2);
 	if ((flags & RFSTOPPED) == 0) {

Modified: stable/10/sys/kern/subr_syscall.c
==============================================================================
--- stable/10/sys/kern/subr_syscall.c	Fri Aug 19 19:31:55 2016	(r304498)
+++ stable/10/sys/kern/subr_syscall.c	Fri Aug 19 20:17:57 2016	(r304499)
@@ -249,5 +249,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/10/sys/kern/sys_process.c
==============================================================================
--- stable/10/sys/kern/sys_process.c	Fri Aug 19 19:31:55 2016	(r304498)
+++ stable/10/sys/kern/sys_process.c	Fri Aug 19 20:17:57 2016	(r304499)
@@ -957,7 +957,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;
 		}
@@ -1296,7 +1296,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/10/sys/sys/proc.h
==============================================================================
--- stable/10/sys/sys/proc.h	Fri Aug 19 19:31:55 2016	(r304498)
+++ stable/10/sys/sys/proc.h	Fri Aug 19 20:17:57 2016	(r304499)
@@ -398,6 +398,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/10/sys/sys/ptrace.h
==============================================================================
--- stable/10/sys/sys/ptrace.h	Fri Aug 19 19:31:55 2016	(r304498)
+++ stable/10/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/10/tests/sys/kern/ptrace_test.c
==============================================================================
--- stable/10/tests/sys/kern/ptrace_test.c	Fri Aug 19 19:31:55 2016	(r304498)
+++ stable/10/tests/sys/kern/ptrace_test.c	Fri Aug 19 20:17:57 2016	(r304499)
@@ -1463,6 +1463,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)
 {
 
@@ -1487,6 +1611,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());
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201608192017.u7JKHw4g083420>