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>
This commit is contained in:
parent
c56d982263
commit
f9b2093d06
|
@ -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 .
|
||||
|
|
|
@ -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, kauth_action_t action, void *cookie,
|
|||
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, struct lwp *l, int req, pid_t pid,
|
|||
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, struct lwp *l, int req, pid_t pid,
|
|||
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, struct lwp *l, int req, pid_t pid,
|
|||
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, struct lwp *l, int req, pid_t pid,
|
|||
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue