Module Name:    src
Committed By:   kamil
Date:           Wed Feb 22 23:43:44 UTC 2017

Modified Files:
        src/lib/libc/sys: ptrace.2
        src/sys/kern: sys_ptrace_common.c
        src/sys/sys: ptrace.h
        src/tests/kernel: t_ptrace_wait.c

Log Message:
Introduce new ptrace(2) API to allow/prevent exection of LWP

Introduce new API for debuggers to allow/prevent execution of the specified
thread.

New ptrace(2) operations:

     PT_RESUME     Allow execution of a specified thread, change its state
                   from suspended to continued.  The addr argument is unused.
                   The data argument specifies the LWP ID.

                   This call is equivalent to _lwp_continue(2) called by a
                   traced process.  This call does not change the general
                   process state from stopped to continued.

     PT_SUSPEND    Prevent execution of a specified thread, change its state
                   from continued to suspended.  The addr argument is unused.
                   The data argument specifies the requested LWP ID.

                   This call is equivalent to _lwp_suspend(2) called by a
                   traced process.  This call does not change the general
                   process state from continued to stopped.

This interface is modeled after FreeBSD, however with NetBSD specific arguments
passed to ptrace(2) -- FreeBSD passes only thread id, NetBSD passes process and
thread id.

Extend PT_LWPINFO operation in ptrace(2) to report suspended threads. In the
ptrace_lwpinfo structure in pl_event next to PL_EVENT_NONE and PL_EVENT_SIGNAL
add new value PL_EVENT_SUSPENDED.

Add new errno(2) value EDEADLK that might be returned by ptrace(2). It prevents
dead-locking in a scenario of resuming a process or thread that is prevented
from execution. This fixes bug that old API was vulnerable to this scenario.

Kernel bump delayed till introduction of PT_GETDBREGS/PT_SETDBREGS soon.

Add new ATF tests:
 - resume1
   Verify that a thread can be suspended by a debugger and later
   resumed by the debugger

 - suspend1
   Verify that a thread can be suspended by a debugger and later
   resumed by a tracee

 - suspend2
   Verify that the while the only thread within a process is
   suspended, the whole process cannot be unstopped

Sponsored by <The NetBSD Foundation>


To generate a diff of this commit:
cvs rdiff -u -r1.59 -r1.60 src/lib/libc/sys/ptrace.2
cvs rdiff -u -r1.14 -r1.15 src/sys/kern/sys_ptrace_common.c
cvs rdiff -u -r1.56 -r1.57 src/sys/sys/ptrace.h
cvs rdiff -u -r1.70 -r1.71 src/tests/kernel/t_ptrace_wait.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libc/sys/ptrace.2
diff -u src/lib/libc/sys/ptrace.2:1.59 src/lib/libc/sys/ptrace.2:1.60
--- src/lib/libc/sys/ptrace.2:1.59	Sun Feb 12 06:09:53 2017
+++ src/lib/libc/sys/ptrace.2	Wed Feb 22 23:43:43 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: ptrace.2,v 1.59 2017/02/12 06:09:53 kamil Exp $
+.\"	$NetBSD: ptrace.2,v 1.60 2017/02/22 23:43:43 kamil Exp $
 .\"
 .\" This file is in the public domain.
 .Dd February 12, 2016
@@ -355,6 +355,7 @@ Possible values are:
 .Bl -tag -width 30n -offset indent -compact
 .It Dv PL_EVENT_NONE
 .It Dv PL_EVENT_SIGNAL
+.It Dv PL_EVENT_SUSPENDED
 .El
 .Pp
 The
@@ -539,6 +540,34 @@ The
 .Fa data
 argument contains the LWP ID of the thread whose mask is to be read.
 If zero is supplied, the first thread of the process is read.
+.It Dv PT_RESUME
+Allow execution of a specified thread,
+change its state from suspended to continued.
+The
+.Fa addr
+argument is unused.
+The
+.Fa data
+argument specifies the LWP ID.
+.Pp
+This call is equivalent to
+.Xr _lwp_continue 2
+called by a traced process.
+This call does not change the general process state from stopped to continued.
+.It Dv PT_SUSPEND
+Prevent execution of a specified thread,
+change its state from continued to suspended.
+The
+.Fa addr
+argument is unused.
+The
+.Fa data
+argument specifies the requested LWP ID.
+.Pp
+This call is equivalent to
+.Xr _lwp_suspend 2
+called by a traced process.
+This call does not change the general process state from continued to stopped.
 .El
 .Pp
 Additionally, the following requests exist but are
@@ -692,6 +721,8 @@ A request (other than
 .Dv PT_ATTACH )
 specified a process that wasn't stopped.
 .El
+.It Bq Er EDEADLK
+An attempt to unstop a process with locked threads.
 .It Bq Er EINVAL
 .Bl -bullet -compact
 .It
@@ -761,3 +792,10 @@ should be able to sidestep this.
 .Dv PTRACE_VFORK
 is currently unimplemented and it will return
 .Er ENOTSUP .
+.Pp
+.Dv PT_SET_SIGINFO ,
+.Dv PT_RESUME
+and
+.Dv PT_SUSPEND
+can change the image of process returned by
+.Dv PT_LWPINFO .

Index: src/sys/kern/sys_ptrace_common.c
diff -u src/sys/kern/sys_ptrace_common.c:1.14 src/sys/kern/sys_ptrace_common.c:1.15
--- src/sys/kern/sys_ptrace_common.c:1.14	Sun Feb 12 06:09:52 2017
+++ src/sys/kern/sys_ptrace_common.c	Wed Feb 22 23:43:43 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_ptrace_common.c,v 1.14 2017/02/12 06:09:52 kamil Exp $	*/
+/*	$NetBSD: sys_ptrace_common.c,v 1.15 2017/02/22 23:43:43 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -118,7 +118,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.14 2017/02/12 06:09:52 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.15 2017/02/22 23:43:43 kamil Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ptrace.h"
@@ -236,6 +236,8 @@ ptrace_listener_cb(kauth_cred_t cred, ka
 	case PT_SYSCALL:
 	case PT_SYSCALLEMU:
 	case PT_DUMPCORE:
+	case PT_RESUME:
+	case PT_SUSPEND:
 		result = KAUTH_RESULT_ALLOW;
 		break;
 
@@ -457,6 +459,8 @@ do_ptrace(struct ptrace_methods *ptm, st
 	case  PT_SET_EVENT_MASK:
 	case  PT_GET_EVENT_MASK:
 	case  PT_GET_PROCESS_STATE:
+	case  PT_RESUME:
+	case  PT_SUSPEND:
 		/*
 		 * You can't do what you want to the process if:
 		 *	(1) It's not being traced at all,
@@ -755,6 +759,34 @@ do_ptrace(struct ptrace_methods *ptm, st
 			break;
 		}
 
+		/* Prevent process deadlock */
+		if (resume_all) {
+#ifdef PT_STEP
+			if (req == PT_STEP) {
+				if (lt->l_flag & LW_WSUSPEND) {
+					error = EDEADLK;
+					break;
+				}
+			} else
+#endif
+			{
+				error = EDEADLK;
+				LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
+					if ((lt2->l_flag & LW_WSUSPEND) == 0) {
+						error = 0;
+						break;
+					}
+				}
+				if (error != 0)
+					break;
+			}
+		} else {
+			if (lt->l_flag & LW_WSUSPEND) {
+				error = EDEADLK;
+				break;
+			}
+		}
+
 		/* If the address parameter is not (int *)1, set the pc. */
 		if ((int *)addr != (int *)1) {
 			error = process_set_pc(lt, addr);
@@ -968,15 +1000,18 @@ do_ptrace(struct ptrace_methods *ptm, st
 		if (lt) {
 			lwp_addref(lt);
 			pl.pl_lwpid = lt->l_lid;
+
+			if (lt->l_flag & LW_WSUSPEND)
+				pl.pl_event = PL_EVENT_SUSPENDED;
 			/*
 			 * If we match the lwp, or it was sent to every lwp,
 			 * we set PL_EVENT_SIGNAL.
 			 * XXX: ps_lwp == 0 means everyone and noone, so
 			 * check ps_signo too.
 			 */
-			if (lt->l_lid == t->p_sigctx.ps_lwp
-			    || (t->p_sigctx.ps_lwp == 0 &&
-			        t->p_sigctx.ps_info._signo))
+			else if (lt->l_lid == t->p_sigctx.ps_lwp
+			         || (t->p_sigctx.ps_lwp == 0 &&
+			             t->p_sigctx.ps_info._signo))
 				pl.pl_event = PL_EVENT_SIGNAL;
 		}
 		mutex_exit(t->p_lock);
@@ -1072,6 +1107,37 @@ do_ptrace(struct ptrace_methods *ptm, st
 			
 		break;
 
+	case  PT_RESUME:
+		write = 1;
+
+	case  PT_SUSPEND:
+		/* write = 0 done above. */
+
+		tmp = data;
+		if (tmp != 0 && t->p_nlwps > 1) {
+			lwp_delref(lt);
+			mutex_enter(t->p_lock);
+			lt = lwp_find(t, tmp);
+			if (lt == NULL) {
+				mutex_exit(t->p_lock);
+				error = ESRCH;
+				break;
+			}
+			lwp_addref(lt);
+			mutex_exit(t->p_lock);
+		}
+		if (lt->l_flag & LW_SYSTEM) {
+			error = EINVAL;
+		} else {
+			lwp_lock(lt);
+			if (write == 0)
+				lt->l_flag |= LW_WSUSPEND;
+			else
+				lt->l_flag &= ~LW_WSUSPEND;
+			lwp_unlock(lt);
+		}
+		break;
+
 #ifdef PT_SETREGS
 	case  PT_SETREGS:
 		write = 1;

Index: src/sys/sys/ptrace.h
diff -u src/sys/sys/ptrace.h:1.56 src/sys/sys/ptrace.h:1.57
--- src/sys/sys/ptrace.h:1.56	Sun Feb 12 06:09:52 2017
+++ src/sys/sys/ptrace.h	Wed Feb 22 23:43:43 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ptrace.h,v 1.56 2017/02/12 06:09:52 kamil Exp $	*/
+/*	$NetBSD: ptrace.h,v 1.57 2017/02/22 23:43:43 kamil Exp $	*/
 
 /*-
  * Copyright (c) 1984, 1993
@@ -57,6 +57,8 @@
 #define	PT_GET_SIGINFO		20	/* get signal state, defined below */
 #define	PT_SET_SIGMASK		21	/* set signal mask */
 #define	PT_GET_SIGMASK		22	/* get signal mask */
+#define	PT_RESUME		23	/* allow execution of the LWP */
+#define	PT_SUSPEND		24	/* prevent execution of the LWP */
 
 #define	PT_FIRSTMACH		32	/* for machine-specific requests */
 #include <machine/ptrace.h>		/* machine-specific requests, if any */
@@ -83,8 +85,10 @@
 /* 18 */    "PT_GET_PROCESS_STATE", \
 /* 19 */    "PT_SET_SIGINFO", \
 /* 20 */    "PT_GET_SIGINFO", \
-/* 20 */    "PT_GET_SIGMASK", \
-/* 20 */    "PT_GET_SIGMASK",
+/* 21 */    "PT_GET_SIGMASK", \
+/* 22 */    "PT_GET_SIGMASK", \
+/* 23 */    "PT_RESUME", \
+/* 24 */    "PT_SUSPEND",
 
 /* PT_{G,S}EVENT_MASK */
 typedef struct ptrace_event {
@@ -135,8 +139,9 @@ struct ptrace_lwpinfo {
 	/* Add fields at the end */
 };
 
-#define PL_EVENT_NONE	0
-#define PL_EVENT_SIGNAL	1
+#define PL_EVENT_NONE		0
+#define PL_EVENT_SIGNAL		1
+#define PL_EVENT_SUSPENDED	2
 
 /*
  * Hardware Watchpoints

Index: src/tests/kernel/t_ptrace_wait.c
diff -u src/tests/kernel/t_ptrace_wait.c:1.70 src/tests/kernel/t_ptrace_wait.c:1.71
--- src/tests/kernel/t_ptrace_wait.c:1.70	Sun Feb 12 06:09:52 2017
+++ src/tests/kernel/t_ptrace_wait.c	Wed Feb 22 23:43:43 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: t_ptrace_wait.c,v 1.70 2017/02/12 06:09:52 kamil Exp $	*/
+/*	$NetBSD: t_ptrace_wait.c,v 1.71 2017/02/22 23:43:43 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: t_ptrace_wait.c,v 1.70 2017/02/12 06:09:52 kamil Exp $");
+__RCSID("$NetBSD: t_ptrace_wait.c,v 1.71 2017/02/22 23:43:43 kamil Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -6947,6 +6947,338 @@ ATF_TC_BODY(setsigmask4, tc)
 	TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
 }
 
+static void
+lwp_main_stop(void *arg)
+{
+	the_lwp_id = _lwp_self();
+
+	raise(SIGTRAP);
+
+	_lwp_exit();
+}
+
+ATF_TC(suspend1);
+ATF_TC_HEAD(suspend1, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that a thread can be suspended by a debugger and later "
+	    "resumed by a tracee");
+}
+
+ATF_TC_BODY(suspend1, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	ucontext_t uc;
+	lwpid_t lid;
+	static const size_t ssize = 16*1024;
+	void *stack;
+	struct ptrace_lwpinfo pl;
+	struct ptrace_siginfo psi;
+	volatile int go = 0;
+
+	printf("Before forking process PID=%d\n", getpid());
+	ATF_REQUIRE((child = fork()) != -1);
+	if (child == 0) {
+		printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+		FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+		printf("Before raising %s from child\n", strsignal(sigval));
+		FORKEE_ASSERT(raise(sigval) == 0);
+
+		printf("Before allocating memory for stack in child\n");
+		FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+		printf("Before making context for new lwp in child\n");
+		_lwp_makecontext(&uc, lwp_main_stop, NULL, NULL, stack, ssize);
+
+		printf("Before creating new in child\n");
+		FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+		while (go == 0)
+			continue;
+
+		raise(SIGINT);
+
+		FORKEE_ASSERT(_lwp_continue(lid) == 0);
+
+		printf("Before waiting for lwp %d to exit\n", lid);
+		FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+		printf("Before verifying that reported %d and running lid %d "
+		    "are the same\n", lid, the_lwp_id);
+		FORKEE_ASSERT_EQ(lid, the_lwp_id);
+
+		printf("Before exiting of the child process\n");
+		_exit(exitval);
+	}
+	printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+	printf("Before calling %s() for the child\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_stopped(status, sigval);
+
+	printf("Before resuming the child process where it left off and "
+	    "without signal to be sent\n");
+	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+	printf("Before calling %s() for the child - expected stopped "
+	    "SIGTRAP\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_stopped(status, SIGTRAP);
+
+	printf("Before reading siginfo and lwpid_t\n");
+	ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1);
+
+	printf("Before suspending LWP %d\n", psi.psi_lwpid);
+	ATF_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1);
+
+        printf("Write new go to tracee (PID=%d) from tracer (PID=%d)\n",
+	    child, getpid());
+	ATF_REQUIRE(ptrace(PT_WRITE_D, child, __UNVOLATILE(&go), 1) != -1);
+
+	printf("Before resuming the child process where it left off and "
+	    "without signal to be sent\n");
+	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+	printf("Before calling %s() for the child - expected stopped "
+	    "SIGINT\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_stopped(status, SIGINT);
+
+	pl.pl_lwpid = 0;
+
+	ATF_REQUIRE(ptrace(PT_LWPINFO, child, &pl, sizeof(pl)) != -1);
+	while (pl.pl_lwpid != 0) {
+
+		ATF_REQUIRE(ptrace(PT_LWPINFO, child, &pl, sizeof(pl)) != -1);
+		switch (pl.pl_lwpid) {
+		case 1:
+			ATF_REQUIRE_EQ(pl.pl_event, PL_EVENT_SIGNAL);
+			break;
+		case 2:
+			ATF_REQUIRE_EQ(pl.pl_event, PL_EVENT_SUSPENDED);
+			break;
+		}
+	}
+
+	printf("Before resuming the child process where it left off and "
+	    "without signal to be sent\n");
+	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+	printf("Before calling %s() for the child - expected exited\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_exited(status, exitval);
+
+	printf("Before calling %s() for the child - expected no process\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(suspend2);
+ATF_TC_HEAD(suspend2, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that the while the only thread within a process is "
+	    "suspended, the whole process cannot be unstopped");
+}
+
+ATF_TC_BODY(suspend2, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct ptrace_siginfo psi;
+
+	printf("Before forking process PID=%d\n", getpid());
+	ATF_REQUIRE((child = fork()) != -1);
+	if (child == 0) {
+		printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+		FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+		printf("Before raising %s from child\n", strsignal(sigval));
+		FORKEE_ASSERT(raise(sigval) == 0);
+
+		printf("Before exiting of the child process\n");
+		_exit(exitval);
+	}
+	printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+	printf("Before calling %s() for the child\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_stopped(status, sigval);
+
+	printf("Before reading siginfo and lwpid_t\n");
+	ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1);
+
+	printf("Before suspending LWP %d\n", psi.psi_lwpid);
+	ATF_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1);
+
+	printf("Before resuming the child process where it left off and "
+	    "without signal to be sent\n");
+	ATF_REQUIRE_ERRNO(EDEADLK,
+	    ptrace(PT_CONTINUE, child, (void *)1, 0) == -1);
+
+	printf("Before resuming LWP %d\n", psi.psi_lwpid);
+	ATF_REQUIRE(ptrace(PT_RESUME, child, NULL, psi.psi_lwpid) != -1);
+
+	printf("Before resuming the child process where it left off and "
+	    "without signal to be sent\n");
+	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+	printf("Before calling %s() for the child - expected exited\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_exited(status, exitval);
+
+	printf("Before calling %s() for the child - expected no process\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(resume1);
+ATF_TC_HEAD(resume1, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that a thread can be suspended by a debugger and later "
+	    "resumed by the debugger");
+}
+
+ATF_TC_BODY(resume1, tc)
+{
+	struct msg_fds fds;
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	ucontext_t uc;
+	lwpid_t lid;
+	static const size_t ssize = 16*1024;
+	void *stack;
+	struct ptrace_lwpinfo pl;
+	struct ptrace_siginfo psi;
+
+	ATF_REQUIRE(msg_open(&fds) == 0);
+
+	printf("Before forking process PID=%d\n", getpid());
+	ATF_REQUIRE((child = fork()) != -1);
+	if (child == 0) {
+		printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+		FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+		printf("Before raising %s from child\n", strsignal(sigval));
+		FORKEE_ASSERT(raise(sigval) == 0);
+
+		printf("Before allocating memory for stack in child\n");
+		FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+		printf("Before making context for new lwp in child\n");
+		_lwp_makecontext(&uc, lwp_main_stop, NULL, NULL, stack, ssize);
+
+		printf("Before creating new in child\n");
+		FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+		CHILD_TO_PARENT("Message", fds, msg);
+
+		raise(SIGINT);
+
+		printf("Before waiting for lwp %d to exit\n", lid);
+		FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+		printf("Before verifying that reported %d and running lid %d "
+		    "are the same\n", lid, the_lwp_id);
+		FORKEE_ASSERT_EQ(lid, the_lwp_id);
+
+		printf("Before exiting of the child process\n");
+		_exit(exitval);
+	}
+	printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+	printf("Before calling %s() for the child\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_stopped(status, sigval);
+
+	printf("Before resuming the child process where it left off and "
+	    "without signal to be sent\n");
+	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+	printf("Before calling %s() for the child - expected stopped "
+	    "SIGTRAP\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_stopped(status, SIGTRAP);
+
+	printf("Before reading siginfo and lwpid_t\n");
+	ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1);
+
+	printf("Before suspending LWP %d\n", psi.psi_lwpid);
+	ATF_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1);
+
+	PARENT_FROM_CHILD("Message", fds, msg);
+
+	printf("Before resuming the child process where it left off and "
+	    "without signal to be sent\n");
+	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+	printf("Before calling %s() for the child - expected stopped "
+	    "SIGINT\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_stopped(status, SIGINT);
+
+	pl.pl_lwpid = 0;
+
+	ATF_REQUIRE(ptrace(PT_LWPINFO, child, &pl, sizeof(pl)) != -1);
+	while (pl.pl_lwpid != 0) {
+		ATF_REQUIRE(ptrace(PT_LWPINFO, child, &pl, sizeof(pl)) != -1);
+		switch (pl.pl_lwpid) {
+		case 1:
+			ATF_REQUIRE_EQ(pl.pl_event, PL_EVENT_SIGNAL);
+			break;
+		case 2:
+			ATF_REQUIRE_EQ(pl.pl_event, PL_EVENT_SUSPENDED);
+			break;
+		}
+	}
+
+	printf("Before resuming LWP %d\n", psi.psi_lwpid);
+	ATF_REQUIRE(ptrace(PT_RESUME, child, NULL, psi.psi_lwpid) != -1);
+
+	printf("Before resuming the child process where it left off and "
+	    "without signal to be sent\n");
+	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+	printf("Before calling %s() for the child - expected exited\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+	validate_status_exited(status, exitval);
+
+	printf("Before calling %s() for the child - expected no process\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+
+	msg_close(&fds);
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 	setvbuf(stdout, NULL, _IONBF, 0);
@@ -7060,6 +7392,11 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, signal9);
 	ATF_TP_ADD_TC(tp, signal10);
 
+	ATF_TP_ADD_TC(tp, suspend1);
+	ATF_TP_ADD_TC(tp, suspend2);
+
+	ATF_TP_ADD_TC(tp, resume1);
+
 	ATF_TP_ADD_TC(tp, getsigmask1);
 	ATF_TP_ADD_TC(tp, getsigmask2);
 

Reply via email to