From e423c86009e18f2844ec1fe0c6876da29c29c36c Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Tue, 29 Dec 2009 21:32:15 +0000 Subject: [PATCH] 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 --- include/sys/ptrace.h | 14 ++++++++++ kernel/arch/i386/memory.c | 2 ++ kernel/system/do_trace.c | 47 ++++++++++++++++--------------- man/man2/ptrace.2 | 58 ++++++++++++++++++++++++++++++++++----- servers/pm/trace.c | 29 +++++++++++++++++++- 5 files changed, 118 insertions(+), 32 deletions(-) diff --git a/include/sys/ptrace.h b/include/sys/ptrace.h index e9694dd9e..4c8475374 100644 --- a/include/sys/ptrace.h +++ b/include/sys/ptrace.h @@ -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 diff --git a/kernel/arch/i386/memory.c b/kernel/arch/i386/memory.c index 53c2ce4c7..330cb9338 100644 --- a/kernel/arch/i386/memory.c +++ b/kernel/arch/i386/memory.c @@ -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 ); diff --git a/kernel/system/do_trace.c b/kernel/system/do_trace.c index 96910f209..e785183b9 100644 --- a/kernel/system/do_trace.c +++ b/kernel/system/do_trace.c @@ -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. */ + COPYFROMPROC(T, tr_addr, (vir_bytes) &tr_data, sizeof(long)); + m_ptr->CTL_DATA = tr_data; + break; 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. */ + COPYTOPROC(T, tr_addr, (vir_bytes) &tr_data, sizeof(long)); + m_ptr->CTL_DATA = 0; + break; 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; diff --git a/man/man2/ptrace.2 b/man/man2/ptrace.2 index b4b3766c3..05f085c89 100644 --- a/man/man2/ptrace.2 +++ b/man/man2/ptrace.2 @@ -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, diff --git a/servers/pm/trace.c b/servers/pm/trace.c index 8b833bd75..d706c9b98 100644 --- a/servers/pm/trace.c +++ b/servers/pm/trace.c @@ -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); -- 2.44.0