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);