ptrace(2) modifications:

- add T_GETRANGE/T_SETRANGE to get/set ranges of values
- change EIO error code to EFAULT
- move common-I&D text-to-data translation to umap_local
This commit is contained in:
David van Moolenbroek 2009-12-29 21:32:15 +00:00
parent 8da928d2df
commit e423c86009
5 changed files with 118 additions and 32 deletions

View file

@ -21,6 +21,8 @@
#define T_ATTACH 11 /* attach to a running process */
#define T_DETACH 12 /* detach from a traced process */
#define T_SETOPT 13 /* set trace options */
#define T_GETRANGE 14 /* get range of values */
#define T_SETRANGE 15 /* set range of values */
#define T_READB_INS 100 /* Read a byte from the text segment of an
* untraced process (only for root)
@ -33,6 +35,18 @@
#define TO_TRACEFORK 0x1 /* automatically attach to forked children */
#define TO_ALTEXEC 0x2 /* send SIGSTOP on successful exec() */
/* Trace spaces. */
#define TS_INS 0 /* text space */
#define TS_DATA 1 /* data space */
/* Trance range structure. */
struct ptrace_range {
int pr_space; /* space in traced process */
long pr_addr; /* address in traced process */
void *pr_ptr; /* buffer in caller process */
size_t pr_size; /* size of range, in bytes */
};
/* Function Prototypes. */
#ifndef _ANSI_H
#include <ansi.h>

View file

@ -397,6 +397,8 @@ vir_bytes bytes; /* # of bytes to be copied */
if (seg != T)
seg = (vc < rp->p_memmap[D].mem_vir + rp->p_memmap[D].mem_len ? D : S);
else if (rp->p_memmap[T].mem_len == 0) /* common I&D? */
seg = D; /* ptrace needs this */
if ((vir_addr>>CLICK_SHIFT) >= rp->p_memmap[seg].mem_vir +
rp->p_memmap[seg].mem_len) return( (phys_bytes) 0 );

View file

@ -16,8 +16,6 @@
/*==========================================================================*
* do_trace *
*==========================================================================*/
#define TR_VLSIZE ((vir_bytes) sizeof(long))
PUBLIC int do_trace(m_ptr)
register message *m_ptr;
{
@ -28,16 +26,22 @@ register message *m_ptr;
* T_GETINS return value from instruction space
* T_GETDATA return value from data space
* T_GETUSER return value from user process table
* T_SETINS set value from instruction space
* T_SETDATA set value from data space
* T_SETINS set value in instruction space
* T_SETDATA set value in data space
* T_SETUSER set value in user process table
* T_RESUME resume execution
* T_EXIT exit
* T_STEP set trace bit
* T_SYSCALL trace system call
* T_ATTACH attach to an existing process
* T_DETACH detach from a traced process
* T_SETOPT set trace options
* T_GETRANGE get range of values
* T_SETRANGE set range of values
*
* The T_OK and T_EXIT commands are handled completely by the process manager,
* all others come here.
* The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled completely by
* the process manager. T_GETRANGE and T_SETRANGE use sys_vircopy(). All others
* come here.
*/
register struct proc *rp;
@ -91,12 +95,9 @@ register message *m_ptr;
return(OK);
case T_GETINS: /* return value from instruction space */
if (rp->p_memmap[T].mem_len != 0) {
COPYFROMPROC(T, tr_addr, (vir_bytes) &tr_data, sizeof(long));
m_ptr->CTL_DATA = tr_data;
break;
}
/* Text space is actually data space - fall through. */
case T_GETDATA: /* return value from data space */
COPYFROMPROC(D, tr_addr, (vir_bytes) &tr_data, sizeof(long));
@ -104,7 +105,7 @@ register message *m_ptr;
break;
case T_GETUSER: /* return value from process table */
if ((tr_addr & (sizeof(long) - 1)) != 0) return(EIO);
if ((tr_addr & (sizeof(long) - 1)) != 0) return(EFAULT);
if (tr_addr <= sizeof(struct proc) - sizeof(long)) {
m_ptr->CTL_DATA = *(long *) ((char *) rp + (int) tr_addr);
@ -117,18 +118,15 @@ register message *m_ptr;
i = sizeof(long) - 1;
tr_addr -= (sizeof(struct proc) + i) & ~i;
if (tr_addr > sizeof(struct priv) - sizeof(long)) return(EIO);
if (tr_addr > sizeof(struct priv) - sizeof(long)) return(EFAULT);
m_ptr->CTL_DATA = *(long *) ((char *) rp->p_priv + (int) tr_addr);
break;
case T_SETINS: /* set value in instruction space */
if (rp->p_memmap[T].mem_len != 0) {
COPYTOPROC(T, tr_addr, (vir_bytes) &tr_data, sizeof(long));
m_ptr->CTL_DATA = 0;
break;
}
/* Text space is actually data space - fall through. */
case T_SETDATA: /* set value in data space */
COPYTOPROC(D, tr_addr, (vir_bytes) &tr_data, sizeof(long));
@ -138,7 +136,7 @@ register message *m_ptr;
case T_SETUSER: /* set value in process table */
if ((tr_addr & (sizeof(reg_t) - 1)) != 0 ||
tr_addr > sizeof(struct stackframe_s) - sizeof(reg_t))
return(EIO);
return(EFAULT);
i = (int) tr_addr;
#if (_MINIX_CHIP == _CHIP_INTEL)
/* Altering segment registers might crash the kernel when it
@ -153,7 +151,7 @@ register message *m_ptr;
i == (int) &((struct proc *) 0)->p_reg.fs ||
#endif
i == (int) &((struct proc *) 0)->p_reg.ss)
return(EIO);
return(EFAULT);
#endif
if (i == (int) &((struct proc *) 0)->p_reg.psw)
/* only selected bits are changeable */
@ -185,12 +183,13 @@ register message *m_ptr;
break;
case T_READB_INS: /* get value from instruction space */
COPYFROMPROC(rp->p_memmap[T].mem_len > 0 ? T : D, tr_addr, (vir_bytes) &ub, 1);
COPYFROMPROC(T, tr_addr, (vir_bytes) &ub, 1);
m_ptr->CTL_DATA = ub;
break;
case T_WRITEB_INS: /* set value in instruction space */
COPYTOPROC(rp->p_memmap[T].mem_len > 0 ? T : D,tr_addr, (vir_bytes) &tr_data, 1);
ub = (unsigned char) (tr_data & 0xff);
COPYTOPROC(T, tr_addr, (vir_bytes) &ub, 1);
m_ptr->CTL_DATA = 0;
break;

View file

@ -94,6 +94,16 @@ interpreted as an additional signal to pass to the process.
.B T_SETOPT
Set the given process's trace options to the bit combination of flags given
in the \fIdata\fP argument.
.TP
.B T_GETRANGE
Get a range of values from the address space of the traced process. The
\fIaddr\fP argument must be a pointer to a fully initialized
\fBstruct ptrace_range\fP structure.
.TP
.B T_SETRANGE
Set a range of values in the address space of the traced process. The
\fIaddr\fP argument must be a pointer to a fully initialized
\fBstruct ptrace_range\fP structure.
.PP
The following option flags are currently supported for \fBT_SETOPT\fP:
.TP 2
@ -108,22 +118,56 @@ Send \fBSIGSTOP\fP instead of \fBSIGTRAP\fP upon a successful
.BR execve (2).
This allows the tracer to disambiguate between this case and other traps.
.PP
All addresses specified for the \fBT_GET\fP* and \fBT_SET\fP* requests must be
The \fBT_GETRANGE\fP and \fBT_SETRANGE\fP calls use the following structure:
.PP
.RS
.nf
.ft B
.ta +4n +8n
struct ptrace_range {
int pr_space;
long pr_addr;
size_t pr_size;
void *pr_ptr;
};
.RE
.PP
The \fBpr_space\fP field specifies the address space from which to retrieve
or set the values. It must be set to either of the following values:
.PP
.TP 10
.B TS_INS
Text space.
.TP
.B TS_DATA
Data space.
.PP
The \fBpr_addr\fP field specifies the start address of the target area in the
traced process. The \fBpr_size\fP field specifies the number of bytes to
retrieve or set, and must be between 1 and LONG_MAX. The \fBpr_ptr\fP field
must point to a buffer in the calling process that is used to store the
retrieved values (for \fBT_GETRANGE\fP) or contains the values to set (for
\fBT_SETRANGE\fP).
.PP
All addresses specified for the \fBT_GETINS\fP, \fBT_GETDATA\fP,
\fBT_GETUSER\fP requests and their \fBT_SET\fP* counterparts must be
aligned on \fBlong\fP boundary. Similarly, only \fBlong\fP sized values can be
retrieved and set at a time.
.SH "RETURN VALUE"
All but the \fBT_GET\fP* requests return 0 upon successful completion.
All but the \fBT_GETINS\fP, \fBT_GETDATA\fP, \fBT_GETUSER\fP requests return 0
upon successful completion.
Otherwise, a value of -1 is returned and \fIerrno\fP is set to indicate the
error.
.PP
The \fBT_GET\fP* requests return the resulting data. Here, -1 is a legitimate
return value. To distinguish between this and an error, clear \fIerrno\fP
The \fBT_GETINS\fP, \fBT_GETDATA\fP, \fBT_GETUSER\fP requests return the
resulting data. Here, -1 is a legitimate return value.
To distinguish between this and an error, clear \fIerrno\fP
before the \fBptrace\fP call, and check whether it is zero afterwards.
.SH ERRORS
The functions will fail if any of the following errors occur:
.TP 10
.B EINVAL
Invalid request or signal given.
Invalid request, signal, space, or length given.
.TP 10
.B ESRCH
The given process is not found, exiting, or not traced by the caller.
@ -131,8 +175,8 @@ The given process is not found, exiting, or not traced by the caller.
.B EBUSY
The given process is not stopped, or already being traced.
.TP 10
.B EIO
The given address is out of range or not properly aligned.
.B EFAULT
The given address is invalid, inaccessible, or not properly aligned.
.TP 10
.B EPERM
Attaching is denied, because the caller equals the given process,

View file

@ -18,6 +18,8 @@
* T_ATTACH attach to an existing process
* T_DETACH detach from a traced process
* T_SETOPT set trace options
* T_GETRANGE get range of values
* T_SETRANGE set range of values
*
* The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled here, and the
* T_RESUME, T_STEP, T_SYSCALL, and T_DETACH commands are partially handled
@ -38,7 +40,8 @@
PUBLIC int do_trace()
{
register struct mproc *child;
int i, r, req;
struct ptrace_range pr;
int i, r, seg, req;
req = m_in.request;
@ -155,6 +158,30 @@ PUBLIC int do_trace()
mp->mp_reply.reply_trace = 0;
return(OK);
case T_GETRANGE:
case T_SETRANGE: /* get/set range of values */
r = sys_datacopy(who_e, (vir_bytes) m_in.PMTRACE_ADDR,
SELF, (vir_bytes) &pr, (phys_bytes) sizeof(pr));
if (r != OK) return(r);
if (pr.pr_space != TS_INS && pr.pr_space != TS_DATA) return(EINVAL);
if (pr.pr_size == 0 || pr.pr_size > LONG_MAX) return(EINVAL);
seg = (pr.pr_space == TS_INS) ? T : D;
if (req == T_GETRANGE)
r = sys_vircopy(child->mp_endpoint, seg, (vir_bytes) pr.pr_addr,
who_e, D, (vir_bytes) pr.pr_ptr,
(phys_bytes) pr.pr_size);
else
r = sys_vircopy(who_e, D, (vir_bytes) pr.pr_ptr,
child->mp_endpoint, seg, (vir_bytes) pr.pr_addr,
(phys_bytes) pr.pr_size);
if (r != OK) return(r);
mp->mp_reply.reply_trace = 0;
return(OK);
case T_DETACH: /* detach from traced process */
if (m_in.data < 0 || m_in.data >= _NSIG) return(EINVAL);