Add PT_GETREGSET

This adds the PT_GETREGSET and PT_SETREGSET ptrace types. These can be
used to access all the registers from a specified core dump note type.
The NT_PRSTATUS and NT_FPREGSET notes are initially supported. Other
machine-dependant types are expected to be added in the future.

The ptrace addr points to a struct iovec pointing at memory to hold the
registers along with its length. On success the length in the iovec is
updated to tell userspace the actual length the kernel wrote or, if the
base address is NULL, the length the kernel would have written.

Because the data field is an int the arguments are backwards when
compared to the Linux PTRACE_GETREGSET call.

Reviewed by:	kib
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D19831
This commit is contained in:
Andrew Turner 2022-01-24 11:24:17 +00:00
parent 5d5f44623e
commit 548a2ec49b
18 changed files with 412 additions and 13 deletions

View File

@ -2,7 +2,7 @@
.\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
.\"
.\" This file is in the public domain.
.Dd January 22, 2022
.Dd January 27, 2022
.Dt PTRACE 2
.Os
.Sh NAME
@ -511,6 +511,40 @@ it loads the traced process's debug registers from the
.In machine/reg.h )
pointed to by
.Fa addr .
.It Dv PT_GETREGSET
This request reads the registers from the traced process.
The
.Fa data
argument specifies the register set to read, with the
.Fa addr
argument pointing at a
.Vt "struct iovec"
where the
.Va iov_base
field points to a register set specific structure to hold the registers,
and the
.Va iov_len
field holds the length of the structure.
.It Dv PT_SETREGSET
This request writes to the registers of the traced process.
The
.Fa data
argument specifies the register set to write to, with the
.Fa addr
argument pointing at a
.Vt "struct iovec"
where the
.Va iov_base
field points to a register set specific structure to hold the registers,
and the
.Va iov_len
field holds the length of the structure.
If
.Va iov_base
is NULL the kernel will return the expected length of the register set
specific structure in the
.Va iov_len
field and not change the target register set.
.It Dv PT_LWPINFO
This request can be used to obtain information about the kernel thread,
also known as light-weight process, that caused the traced process to stop.

View File

@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/proc.h>
#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/syscall.h>
@ -95,6 +96,8 @@ struct sysentvec elf64_freebsd_sysvec_la48 = {
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_set_fork_retval = x86_set_fork_retval,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
struct sysentvec elf64_freebsd_sysvec_la57 = {
@ -137,6 +140,8 @@ struct sysentvec elf64_freebsd_sysvec_la57 = {
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_set_fork_retval= x86_set_fork_retval,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
static void

View File

@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/proc.h>
@ -103,6 +104,8 @@ struct sysentvec elf32_freebsd_sysvec = {
.sv_hwcap2 = &elf_hwcap2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);

View File

@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/proc.h>
#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/syscall.h>
@ -120,6 +121,8 @@ static struct sysentvec elf32_freebsd_sysvec = {
.sv_hwcap2 = &elf32_hwcap2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);

View File

@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/proc.h>
#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/syscall.h>
@ -98,6 +99,8 @@ static struct sysentvec elf64_freebsd_sysvec = {
.sv_hwcap2 = &elf_hwcap2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec);

View File

@ -964,6 +964,7 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
struct dbreg32 dbreg;
struct fpreg32 fpreg;
struct reg32 reg;
struct iovec vec;
register_t args[nitems(td->td_sa.args)];
struct ptrace_sc_ret psr;
int ptevents;
@ -975,6 +976,7 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
struct ptrace_coredump32 pc;
uint32_t args[nitems(td->td_sa.args)];
struct ptrace_sc_ret32 psr;
struct iovec32 vec;
} r32;
void *addr;
int data, error, i;
@ -1024,6 +1026,22 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
case PT_SETDBREGS:
error = copyin(uap->addr, &r.dbreg, sizeof(r.dbreg));
break;
case PT_SETREGSET:
error = copyin(uap->addr, &r32.vec, sizeof(r32.vec));
if (error != 0)
break;
r.vec.iov_len = r32.vec.iov_len;
r.vec.iov_base = PTRIN(r32.vec.iov_base);
break;
case PT_GETREGSET:
error = copyin(uap->addr, &r32.vec, sizeof(r32.vec));
if (error != 0)
break;
r.vec.iov_len = r32.vec.iov_len;
r.vec.iov_base = PTRIN(r32.vec.iov_base);
break;
case PT_SET_EVENT_MASK:
if (uap->data != sizeof(r.ptevents))
error = EINVAL;
@ -1102,6 +1120,10 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
case PT_GETDBREGS:
error = copyout(&r.dbreg, uap->addr, sizeof(r.dbreg));
break;
case PT_GETREGSET:
r32.vec.iov_len = r.vec.iov_len;
error = copyout(&r32.vec, uap->addr, sizeof(r32.vec));
break;
case PT_GET_EVENT_MASK:
/* NB: The size in uap->data is validated in kern_ptrace(). */
error = copyout(&r.ptevents, uap->addr, uap->data);

View File

@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
#define __ELF_WORD_SIZE 32
#include <sys/param.h>
#include <sys/elf.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
#include <sys/imgact.h>
@ -44,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/procfs.h>
#include <sys/reg.h>
#include <sys/resourcevar.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
@ -141,6 +143,8 @@ struct sysentvec ia32_freebsd_sysvec = {
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_set_fork_retval = x86_set_fork_retval,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf_ia32_sysvec, &ia32_freebsd_sysvec);

View File

@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/reg.h>
#include <sys/proc.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
@ -90,6 +91,8 @@ struct sysentvec elf32_freebsd_sysvec = {
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_set_fork_retval = x86_set_fork_retval,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);

View File

@ -2186,16 +2186,16 @@ __elfN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep)
*sizep = sizeof(*psinfo);
}
static void
__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep)
static bool
__elfN(get_prstatus)(struct regset *rs, struct thread *td, void *buf,
size_t *sizep)
{
struct thread *td;
elf_prstatus_t *status;
td = arg;
if (sb != NULL) {
KASSERT(*sizep == sizeof(*status), ("invalid size"));
status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK);
if (buf != NULL) {
KASSERT(*sizep == sizeof(*status), ("%s: invalid size",
__func__));
status = buf;
status->pr_version = PRSTATUS_VERSION;
status->pr_statussz = sizeof(elf_prstatus_t);
status->pr_gregsetsz = sizeof(elf_gregset_t);
@ -2208,12 +2208,97 @@ __elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep)
#else
fill_regs(td, &status->pr_reg);
#endif
}
*sizep = sizeof(*status);
return (true);
}
static bool
__elfN(set_prstatus)(struct regset *rs, struct thread *td, void *buf,
size_t size)
{
elf_prstatus_t *status;
KASSERT(size == sizeof(*status), ("%s: invalid size", __func__));
status = buf;
#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32
set_regs32(td, &status->pr_reg);
#else
set_regs(td, &status->pr_reg);
#endif
return (true);
}
static struct regset __elfN(regset_prstatus) = {
.note = NT_PRSTATUS,
.size = sizeof(elf_prstatus_t),
.get = __elfN(get_prstatus),
.set = __elfN(set_prstatus),
};
ELF_REGSET(__elfN(regset_prstatus));
static void
__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep)
{
struct thread *td;
elf_prstatus_t *status;
td = arg;
if (sb != NULL) {
KASSERT(*sizep == sizeof(*status), ("%s: invalid size",
__func__));
status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK);
__elfN(get_prstatus)(NULL, td, status, sizep);
sbuf_bcat(sb, status, sizeof(*status));
free(status, M_TEMP);
}
*sizep = sizeof(*status);
}
static bool
__elfN(get_fpregset)(struct regset *rs, struct thread *td, void *buf,
size_t *sizep)
{
elf_prfpregset_t *fpregset;
if (buf != NULL) {
KASSERT(*sizep == sizeof(*fpregset), ("%s: invalid size",
__func__));
fpregset = buf;
#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32
fill_fpregs32(td, fpregset);
#else
fill_fpregs(td, fpregset);
#endif
}
*sizep = sizeof(fpregset);
return (true);
}
static bool
__elfN(set_fpregset)(struct regset *rs, struct thread *td, void *buf,
size_t size)
{
elf_prfpregset_t *fpregset;
fpregset = buf;
KASSERT(size == sizeof(*fpregset), ("%s: invalid size", __func__));
#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32
set_fpregs32(td, fpregset);
#else
set_fpregs(td, fpregset);
#endif
return (true);
}
static struct regset __elfN(regset_fpregset) = {
.note = NT_FPREGSET,
.size = sizeof(elf_prfpregset_t),
.get = __elfN(get_fpregset),
.set = __elfN(set_fpregset),
};
ELF_REGSET(__elfN(regset_fpregset));
static void
__elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep)
{
@ -2224,11 +2309,7 @@ __elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep)
if (sb != NULL) {
KASSERT(*sizep == sizeof(*fpregset), ("invalid size"));
fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK);
#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32
fill_fpregs32(td, fpregset);
#else
fill_fpregs(td, fpregset);
#endif
__elfN(get_fpregset)(NULL, td, fpregset, sizep);
sbuf_bcat(sb, fpregset, sizeof(*fpregset));
free(fpregset, M_TEMP);
}

View File

@ -438,6 +438,8 @@ struct sysentvec null_sysvec = {
.sv_thread_detach = NULL,
.sv_trap = NULL,
.sv_set_fork_retval = null_set_fork_retval,
.sv_regset_begin = NULL,
.sv_regset_end = NULL,
};
/*

View File

@ -70,6 +70,9 @@ __FBSDID("$FreeBSD$");
#include <sys/procfs.h>
#endif
/* Assert it's safe to unlock a process, e.g. to allocate working memory */
#define PROC_ASSERT_TRACEREQ(p) MPASS(((p)->p_flag2 & P2_PTRACEREQ) != 0)
/*
* Functions implemented using PROC_ACTION():
*
@ -153,6 +156,125 @@ proc_write_fpregs(struct thread *td, struct fpreg *fpregs)
PROC_ACTION(set_fpregs(td, fpregs));
}
static struct regset *
proc_find_regset(struct thread *td, int note)
{
struct regset **regsetp, **regset_end, *regset;
struct sysentvec *sv;
sv = td->td_proc->p_sysent;
regsetp = sv->sv_regset_begin;
if (regsetp == NULL)
return (NULL);
regset_end = sv->sv_regset_end;
MPASS(regset_end != NULL);
for (; regsetp < regset_end; regsetp++) {
regset = *regsetp;
if (regset->note != note)
continue;
return (regset);
}
return (NULL);
}
static int
proc_read_regset(struct thread *td, int note, struct iovec *iov)
{
struct regset *regset;
struct proc *p;
void *buf;
size_t size;
int error;
regset = proc_find_regset(td, note);
if (regset == NULL)
return (EINVAL);
if (iov->iov_base == NULL) {
iov->iov_len = regset->size;
if (iov->iov_len == 0)
return (EINVAL);
return (0);
}
/* The length is wrong, return an error */
if (iov->iov_len != regset->size)
return (EINVAL);
if (regset->get == NULL)
return (EINVAL);
error = 0;
size = regset->size;
p = td->td_proc;
/* Drop the proc lock while allocating the temp buffer */
PROC_ASSERT_TRACEREQ(p);
PROC_UNLOCK(p);
buf = malloc(size, M_TEMP, M_WAITOK);
PROC_LOCK(p);
if (!regset->get(regset, td, buf, &size)) {
error = EINVAL;
} else {
KASSERT(size == regset->size,
("%s: Getter function changed the size", __func__));
iov->iov_len = size;
PROC_UNLOCK(p);
error = copyout(buf, iov->iov_base, size);
PROC_LOCK(p);
}
free(buf, M_TEMP);
return (error);
}
static int
proc_write_regset(struct thread *td, int note, struct iovec *iov)
{
struct regset *regset;
struct proc *p;
void *buf;
size_t size;
int error;
regset = proc_find_regset(td, note);
if (regset == NULL)
return (EINVAL);
/* The length is wrong, return an error */
if (iov->iov_len != regset->size)
return (EINVAL);
if (regset->set == NULL)
return (EINVAL);
size = regset->size;
p = td->td_proc;
/* Drop the proc lock while allocating the temp buffer */
PROC_ASSERT_TRACEREQ(p);
PROC_UNLOCK(p);
buf = malloc(size, M_TEMP, M_WAITOK);
error = copyin(iov->iov_base, buf, size);
PROC_LOCK(p);
if (error == 0) {
if (!regset->set(regset, td, buf, size)) {
error = EINVAL;
}
}
free(buf, M_TEMP);
return (error);
}
#ifdef COMPAT_FREEBSD32
/* For 32 bit binaries, we need to expose the 32 bit regs layouts. */
int
@ -474,6 +596,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
struct dbreg dbreg;
struct fpreg fpreg;
struct reg reg;
struct iovec vec;
char args[sizeof(td->td_sa.args)];
struct ptrace_sc_ret psr;
int ptevents;
@ -504,6 +627,12 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
case PT_GETDBREGS:
bzero(&r.dbreg, sizeof(r.dbreg));
break;
case PT_SETREGSET:
error = copyin(uap->addr, &r.vec, sizeof(r.vec));
break;
case PT_GETREGSET:
error = copyin(uap->addr, &r.vec, sizeof(r.vec));
break;
case PT_SETREGS:
error = copyin(uap->addr, &r.reg, sizeof(r.reg));
break;
@ -558,6 +687,9 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
case PT_GETDBREGS:
error = copyout(&r.dbreg, uap->addr, sizeof(r.dbreg));
break;
case PT_GETREGSET:
error = copyout(&r.vec, uap->addr, sizeof(r.vec));
break;
case PT_GET_EVENT_MASK:
/* NB: The size in uap->data is validated in kern_ptrace(). */
error = copyout(&r.ptevents, uap->addr, uap->data);
@ -1291,6 +1423,18 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
error = PROC_READ(dbregs, td2, addr);
break;
case PT_SETREGSET:
CTR2(KTR_PTRACE, "PT_SETREGSET: tid %d (pid %d)", td2->td_tid,
p->p_pid);
error = proc_write_regset(td2, data, addr);
break;
case PT_GETREGSET:
CTR2(KTR_PTRACE, "PT_GETREGSET: tid %d (pid %d)", td2->td_tid,
p->p_pid);
error = proc_read_regset(td2, data, addr);
break;
case PT_LWPINFO:
if (data <= 0 || data > sizeof(*pl)) {
error = EINVAL;

View File

@ -138,6 +138,8 @@ struct sysentvec elf32_freebsd_sysvec = {
.sv_hwcap2 = &cpu_features2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);

View File

@ -30,12 +30,14 @@
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/elf.h>
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/namei.h>
#include <sys/fcntl.h>
#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/jail.h>
@ -99,6 +101,8 @@ struct sysentvec elf64_freebsd_sysvec_v1 = {
.sv_hwcap2 = &cpu_features2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
struct sysentvec elf64_freebsd_sysvec_v2 = {
@ -141,6 +145,8 @@ struct sysentvec elf64_freebsd_sysvec_v2 = {
.sv_hwcap2 = &cpu_features2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
static boolean_t ppc64_elfv1_header_match(struct image_params *params,

View File

@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/proc.h>
#include <sys/reg.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
@ -102,6 +103,8 @@ struct sysentvec elf64_freebsd_sysvec = {
.sv_machine_arch = riscv_machine_arch,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_regset_begin = SET_BEGIN(__elfN(regset)),
.sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec);

View File

@ -85,6 +85,8 @@
#define PT_VM_TIMESTAMP 40 /* Get VM version (timestamp) */
#define PT_VM_ENTRY 41 /* Get VM map (entry) */
#define PT_GETREGSET 42 /* Get a target register set */
#define PT_SETREGSET 43 /* Set a target register set */
#define PT_FIRSTMACH 64 /* for machine-specific requests */
#include <machine/ptrace.h> /* machine-specific requests, if any */

View File

@ -41,6 +41,25 @@
#include <machine/reg.h>
#ifdef _KERNEL
struct sbuf;
struct regset;
typedef bool (regset_get)(struct regset *, struct thread *, void *,
size_t *);
typedef bool (regset_set)(struct regset *, struct thread *, void *, size_t);
struct regset {
int note;
size_t size;
regset_get *get;
regset_set *set;
};
#if defined(__ELF_WORD_SIZE)
SET_DECLARE(__elfN(regset), struct regset);
#define ELF_REGSET(_regset) DATA_SET(__elfN(regset), _regset)
#endif
int fill_regs(struct thread *, struct reg *);
int set_regs(struct thread *, struct reg *);
int fill_fpregs(struct thread *, struct fpreg *);

View File

@ -160,6 +160,8 @@ struct sysentvec {
struct image_params *imgp);
void (*sv_set_fork_retval)(struct thread *);
/* Only used on x86 */
struct regset **sv_regset_begin;
struct regset **sv_regset_end;
};
#define SV_ILP32 0x000100 /* 32-bit executable. */

View File

@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/cpuset.h>
#include <sys/elf.h>
#include <sys/event.h>
#include <sys/file.h>
#include <sys/time.h>
@ -35,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/procdesc.h>
#define _WANT_MIPS_REGNUM
#include <sys/ptrace.h>
#include <sys/procfs.h>
#include <sys/queue.h>
#include <sys/runq.h>
#include <sys/syscall.h>
@ -3196,6 +3198,64 @@ ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_thread_sigmask, tc)
REQUIRE_EQ(errno, ECHILD);
}
/*
* Verify that PT_GETREGSET returns registers and PT_SETREGSET updates them.
*/
ATF_TC_WITHOUT_HEAD(ptrace__PT_REGSET);
ATF_TC_BODY(ptrace__PT_REGSET, tc)
{
struct prstatus prstatus;
struct iovec vec;
pid_t child, wpid;
int status;
ATF_REQUIRE((child = fork()) != -1);
if (child == 0) {
trace_me();
exit(1);
}
/* The first wait() should report the stop from SIGSTOP. */
wpid = waitpid(child, &status, 0);
REQUIRE_EQ(wpid, child);
ATF_REQUIRE(WIFSTOPPED(status));
REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
/* Check the size is returned when vec.iov_base is NULL */
vec.iov_base = NULL;
vec.iov_len = 0;
ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
-1);
ATF_REQUIRE(vec.iov_len == sizeof(prstatus));
ATF_REQUIRE(vec.iov_base == NULL);
/* Read the registers. */
memset(&prstatus, 0, sizeof(prstatus));
vec.iov_base = &prstatus;
ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
-1);
ATF_REQUIRE(vec.iov_len == sizeof(prstatus));
ATF_REQUIRE(vec.iov_base == &prstatus);
ATF_REQUIRE(prstatus.pr_statussz == sizeof(prstatus));
/* Write the registers back. */
ATF_REQUIRE(ptrace(PT_SETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
-1);
REQUIRE_EQ(ptrace(PT_CONTINUE, child, (caddr_t)1, 0), 0);
/* The second wait() should report the exit status. */
wpid = waitpid(child, &status, 0);
REQUIRE_EQ(wpid, child);
ATF_REQUIRE(WIFEXITED(status));
REQUIRE_EQ(WEXITSTATUS(status), 1);
/* The child should no longer exist. */
wpid = waitpid(child, &status, 0);
REQUIRE_EQ(wpid, -1);
REQUIRE_EQ(errno, ECHILD);
}
static void *
raise_sigstop_thread(void *arg __unused)
{
@ -4302,6 +4362,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, ptrace__killed_with_sigmask);
ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigmask);
ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_thread_sigmask);
ATF_TP_ADD_TC(tp, ptrace__PT_REGSET);
ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop1);
ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop2);
ATF_TP_ADD_TC(tp, ptrace__event_mask_sigkill_discard);