Split up ptrace() into a wrapper that does the copying to and from

user space and a kern_ptrace() implementation. Use the kern_*()
version in the Linux emulation code to remove more stack gap uses.

Approved by:	des
This commit is contained in:
iedowse 2002-09-05 01:02:50 +00:00
parent d049443a42
commit 0fc3eadf20
3 changed files with 142 additions and 163 deletions

View File

@ -34,6 +34,7 @@
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/ptrace.h>
#include <sys/syscallsubr.h>
#include <sys/sysproto.h>
#include <sys/user.h>
@ -246,119 +247,92 @@ linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)
int
linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
{
struct ptrace_args bsd_args;
int error;
caddr_t sg;
union {
struct linux_pt_reg reg;
struct linux_pt_fpreg fpreg;
struct linux_pt_fpxreg fpxreg;
} r;
sg = stackgap_init();
union {
struct reg bsd_reg;
struct fpreg bsd_fpreg;
struct dbreg bsd_dbreg;
} u;
void *addr;
pid_t pid;
int error, req;
error = 0;
/* by default, just copy data intact */
bsd_args.req = uap->req;
bsd_args.pid = (pid_t)uap->pid;
bsd_args.addr = (caddr_t)uap->addr;
bsd_args.data = uap->data;
req = uap->req;
pid = (pid_t)uap->pid;
addr = (void *)uap->addr;
switch (uap->req) {
switch (req) {
case PTRACE_TRACEME:
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
case PTRACE_KILL:
error = ptrace(td, &bsd_args);
error = kern_ptrace(td, req, pid, addr, uap->data);
break;
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA: {
/* need to preserve return value */
int rval = td->td_retval[0];
bsd_args.data = 0;
error = ptrace(td, &bsd_args);
error = kern_ptrace(td, req, pid, addr, 0);
if (error == 0)
error = copyout(td->td_retval,
(caddr_t)uap->data, sizeof(l_int));
error = copyout(td->td_retval, (caddr_t)uap->data,
sizeof(l_int));
td->td_retval[0] = rval;
break;
}
case PTRACE_DETACH:
bsd_args.req = PT_DETACH;
/* fall through */
error = kern_ptrace(td, PT_DETACH, pid, (void *)1,
map_signum(uap->data));
break;
case PTRACE_SINGLESTEP:
case PTRACE_CONT:
bsd_args.data = map_signum(uap->data);
bsd_args.addr = (caddr_t)1;
error = ptrace(td, &bsd_args);
error = kern_ptrace(td, req, pid, (void *)1,
map_signum(uap->data));
break;
case PTRACE_ATTACH:
bsd_args.req = PT_ATTACH;
error = ptrace(td, &bsd_args);
error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
break;
case PTRACE_GETREGS: {
struct reg *bsd_r;
bsd_r = (struct reg*)stackgap_alloc(&sg, sizeof(*bsd_r));
case PTRACE_GETREGS:
/* Linux is using data where FreeBSD is using addr */
bsd_args.req = PT_GETREGS;
bsd_args.addr = (caddr_t)bsd_r;
bsd_args.data = 0;
error = ptrace(td, &bsd_args);
error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
if (error == 0) {
map_regs_to_linux(bsd_r, &r.reg);
map_regs_to_linux(&u.bsd_reg, &r.reg);
error = copyout(&r.reg, (caddr_t)uap->data,
sizeof(r.reg));
}
break;
}
case PTRACE_SETREGS: {
struct reg *bsd_r;
bsd_r = (struct reg*)stackgap_alloc(&sg, sizeof(*bsd_r));
case PTRACE_SETREGS:
/* Linux is using data where FreeBSD is using addr */
bsd_args.req = PT_SETREGS;
bsd_args.addr = (caddr_t)bsd_r;
bsd_args.data = 0;
error = copyin((caddr_t)uap->data, &r.reg, sizeof(r.reg));
if (error == 0) {
map_regs_from_linux(bsd_r, &r.reg);
error = ptrace(td, &bsd_args);
map_regs_from_linux(&u.bsd_reg, &r.reg);
error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
}
break;
}
case PTRACE_GETFPREGS: {
struct fpreg *bsd_r;
bsd_r = (struct fpreg*)stackgap_alloc(&sg, sizeof(*bsd_r));
case PTRACE_GETFPREGS:
/* Linux is using data where FreeBSD is using addr */
bsd_args.req = PT_GETFPREGS;
bsd_args.addr = (caddr_t)bsd_r;
bsd_args.data = 0;
error = ptrace(td, &bsd_args);
error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0);
if (error == 0) {
map_fpregs_to_linux(bsd_r, &r.fpreg);
map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg);
error = copyout(&r.fpreg, (caddr_t)uap->data,
sizeof(r.fpreg));
}
break;
}
case PTRACE_SETFPREGS: {
struct fpreg *bsd_r;
bsd_r = (struct fpreg*)stackgap_alloc(&sg, sizeof(*bsd_r));
case PTRACE_SETFPREGS:
/* Linux is using data where FreeBSD is using addr */
bsd_args.req = PT_SETFPREGS;
bsd_args.addr = (caddr_t)bsd_r;
bsd_args.data = 0;
error = copyin((caddr_t)uap->data, &r.fpreg, sizeof(r.fpreg));
if (error == 0) {
map_fpregs_from_linux(bsd_r, &r.fpreg);
error = ptrace(td, &bsd_args);
map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg);
error = kern_ptrace(td, PT_SETFPREGS, pid,
&u.bsd_fpreg, 0);
}
break;
}
case PTRACE_SETFPXREGS:
#ifdef CPU_ENABLE_SSA
error = copyin((caddr_t)uap->data, &r.fpxreg,
@ -415,7 +389,7 @@ linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
}
td2 = FIRST_THREAD_IN_PROC(p);
if (uap->req == PTRACE_GETFPXREGS) {
if (req == PTRACE_GETFPXREGS) {
_PHOLD(p);
error = linux_proc_read_fpxregs(td2, &r.fpxreg);
_PRELE(p);
@ -453,20 +427,12 @@ linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
* as necessary.
*/
if (uap->addr < sizeof(struct linux_pt_reg)) {
struct reg *bsd_r;
bsd_r = (struct reg*)stackgap_alloc(&sg,
sizeof(*bsd_r));
bsd_args.req = PT_GETREGS;
bsd_args.addr = (caddr_t)bsd_r;
bsd_args.data = 0;
error = ptrace(td, &bsd_args);
error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
if (error != 0)
break;
map_regs_to_linux(bsd_r, &r.reg);
if (uap->req == PTRACE_PEEKUSR) {
map_regs_to_linux(&u.bsd_reg, &r.reg);
if (req == PTRACE_PEEKUSR) {
error = copyout((char *)&r.reg + uap->addr,
(caddr_t)uap->data, sizeof(l_int));
break;
@ -475,11 +441,8 @@ linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
*(l_int *)((char *)&r.reg + uap->addr) =
(l_int)uap->data;
map_regs_from_linux(bsd_r, &r.reg);
bsd_args.req = PT_SETREGS;
bsd_args.addr = (caddr_t)bsd_r;
bsd_args.data = 0;
error = ptrace(td, &bsd_args);
map_regs_from_linux(&u.bsd_reg, &r.reg);
error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
}
/*
@ -487,29 +450,23 @@ linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
*/
if (uap->addr >= LINUX_DBREG_OFFSET &&
uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) {
struct dbreg *bsd_r;
bsd_r = (struct dbreg*)stackgap_alloc(&sg,
sizeof(*bsd_r));
bsd_args.req = PT_GETDBREGS;
bsd_args.addr = (caddr_t)bsd_r;
bsd_args.data = 0;
error = ptrace(td, &bsd_args);
error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg,
0);
if (error != 0)
break;
uap->addr -= LINUX_DBREG_OFFSET;
if (uap->req == PTRACE_PEEKUSR) {
error = copyout((char *)bsd_r + uap->addr,
(caddr_t)uap->data, sizeof(l_int));
if (req == PTRACE_PEEKUSR) {
error = copyout((char *)&u.bsd_dbreg +
uap->addr, (caddr_t)uap->data,
sizeof(l_int));
break;
}
*(l_int *)((char *)bsd_r + uap->addr) = uap->data;
bsd_args.req = PT_SETDBREGS;
bsd_args.addr = (caddr_t)bsd_r;
bsd_args.data = 0;
error = ptrace(td, &bsd_args);
*(l_int *)((char *)&u.bsd_dbreg + uap->addr) =
uap->data;
error = kern_ptrace(td, PT_SETDBREGS, pid,
&u.bsd_dbreg, 0);
}
break;

View File

@ -35,6 +35,7 @@
#include <sys/systm.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/syscallsubr.h>
#include <sys/sysproto.h>
#include <sys/proc.h>
#include <sys/vnode.h>
@ -324,8 +325,6 @@ struct ptrace_args {
int
ptrace(struct thread *td, struct ptrace_args *uap)
{
struct iovec iov;
struct uio uio;
/*
* XXX this obfuscation is to reduce stack usage, but the register
* structs may be too large to put on the stack anyway.
@ -336,18 +335,70 @@ ptrace(struct thread *td, struct ptrace_args *uap)
struct fpreg fpreg;
struct reg reg;
} r;
void *addr;
int error = 0;
addr = &r;
switch (uap->req) {
case PT_GETREGS:
case PT_GETFPREGS:
case PT_GETDBREGS:
break;
case PT_SETREGS:
error = copyin(uap->addr, &r.reg, sizeof r.reg);
break;
case PT_SETFPREGS:
error = copyin(uap->addr, &r.fpreg, sizeof r.fpreg);
break;
case PT_SETDBREGS:
error = copyin(uap->addr, &r.dbreg, sizeof r.dbreg);
break;
case PT_IO:
error = copyin(uap->addr, &r.piod, sizeof r.piod);
break;
default:
addr = uap->addr;
}
if (error)
return (error);
error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data);
if (error)
return (error);
switch (uap->req) {
case PT_IO:
(void)copyout(&r.piod, uap->addr, sizeof r.piod);
break;
case PT_GETREGS:
error = copyout(&r.reg, uap->addr, sizeof r.reg);
break;
case PT_GETFPREGS:
error = copyout(&r.fpreg, uap->addr, sizeof r.fpreg);
break;
case PT_GETDBREGS:
error = copyout(&r.dbreg, uap->addr, sizeof r.dbreg);
break;
}
return (error);
}
int
kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
{
struct iovec iov;
struct uio uio;
struct proc *curp, *p, *pp;
struct thread *td2;
struct ptrace_io_desc *piod;
int error, write, tmp;
int proctree_locked = 0;
curp = td->td_proc;
/*
* Do copyin() early before getting locks and lock proctree before
* locking the process.
*/
switch (uap->req) {
/* Lock proctree before locking the process. */
switch (req) {
case PT_TRACE_ME:
case PT_ATTACH:
case PT_STEP:
@ -356,37 +407,16 @@ ptrace(struct thread *td, struct ptrace_args *uap)
sx_xlock(&proctree_lock);
proctree_locked = 1;
break;
#ifdef PT_SETREGS
case PT_SETREGS:
error = copyin(uap->addr, &r.reg, sizeof r.reg);
if (error)
return (error);
break;
#endif /* PT_SETREGS */
#ifdef PT_SETFPREGS
case PT_SETFPREGS:
error = copyin(uap->addr, &r.fpreg, sizeof r.fpreg);
if (error)
return (error);
break;
#endif /* PT_SETFPREGS */
#ifdef PT_SETDBREGS
case PT_SETDBREGS:
error = copyin(uap->addr, &r.dbreg, sizeof r.dbreg);
if (error)
return (error);
break;
#endif /* PT_SETDBREGS */
default:
break;
}
write = 0;
if (uap->req == PT_TRACE_ME) {
if (req == PT_TRACE_ME) {
p = td->td_proc;
PROC_LOCK(p);
} else {
if ((p = pfind(uap->pid)) == NULL) {
if ((p = pfind(pid)) == NULL) {
if (proctree_locked)
sx_xunlock(&proctree_lock);
return (ESRCH);
@ -409,7 +439,7 @@ ptrace(struct thread *td, struct ptrace_args *uap)
/*
* Permissions check
*/
switch (uap->req) {
switch (req) {
case PT_TRACE_ME:
/* Always legal. */
break;
@ -496,7 +526,7 @@ ptrace(struct thread *td, struct ptrace_args *uap)
td->td_retval[0] = 0;
switch (uap->req) {
switch (req) {
case PT_TRACE_ME:
/* set my trace flag and "owner" so it can read/write me */
p->p_flag |= P_TRACED;
@ -511,21 +541,21 @@ ptrace(struct thread *td, struct ptrace_args *uap)
p->p_oppid = p->p_pptr->p_pid;
if (p->p_pptr != td->td_proc)
proc_reparent(p, td->td_proc);
uap->data = SIGSTOP;
data = SIGSTOP;
goto sendsig; /* in PT_CONTINUE below */
case PT_STEP:
case PT_CONTINUE:
case PT_DETACH:
/* XXX uap->data is used even in the PT_STEP case. */
if (uap->req != PT_STEP && (unsigned)uap->data > _SIG_MAXSIG) {
/* XXX data is used even in the PT_STEP case. */
if (req != PT_STEP && (unsigned)data > _SIG_MAXSIG) {
error = EINVAL;
goto fail;
}
_PHOLD(p);
if (uap->req == PT_STEP) {
if (req == PT_STEP) {
error = ptrace_single_step(td2);
if (error) {
_PRELE(p);
@ -533,10 +563,9 @@ ptrace(struct thread *td, struct ptrace_args *uap)
}
}
if (uap->addr != (caddr_t)1) {
if (addr != (void *)1) {
fill_kinfo_proc(p, &p->p_uarea->u_kproc);
error = ptrace_set_pc(td2,
(u_long)(uintfptr_t)uap->addr);
error = ptrace_set_pc(td2, (u_long)(uintfptr_t)addr);
if (error) {
_PRELE(p);
goto fail;
@ -544,7 +573,7 @@ ptrace(struct thread *td, struct ptrace_args *uap)
}
_PRELE(p);
if (uap->req == PT_DETACH) {
if (req == PT_DETACH) {
/* reset process parent */
if (p->p_oppid != p->p_pptr->p_pid) {
struct proc *pp;
@ -569,14 +598,14 @@ ptrace(struct thread *td, struct ptrace_args *uap)
sx_xunlock(&proctree_lock);
/* deliver or queue signal */
if (P_SHOULDSTOP(p)) {
p->p_xstat = uap->data;
p->p_xstat = data;
mtx_lock_spin(&sched_lock);
p->p_flag &= ~(P_STOPPED_TRACE|P_STOPPED_SGNL);
setrunnable(td2); /* XXXKSE */
/* Need foreach kse in proc, ... make_kse_queued(). */
mtx_unlock_spin(&sched_lock);
} else if (uap->data)
psignal(p, uap->data);
} else if (data)
psignal(p, data);
PROC_UNLOCK(p);
return (0);
@ -590,11 +619,11 @@ ptrace(struct thread *td, struct ptrace_args *uap)
PROC_UNLOCK(p);
tmp = 0;
/* write = 0 set above */
iov.iov_base = write ? (caddr_t)&uap->data : (caddr_t)&tmp;
iov.iov_base = write ? (caddr_t)&data : (caddr_t)&tmp;
iov.iov_len = sizeof(int);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = (off_t)(uintptr_t)uap->addr;
uio.uio_offset = (off_t)(uintptr_t)addr;
uio.uio_resid = sizeof(int);
uio.uio_segflg = UIO_SYSSPACE; /* i.e.: the uap */
uio.uio_rw = write ? UIO_WRITE : UIO_READ;
@ -618,18 +647,16 @@ ptrace(struct thread *td, struct ptrace_args *uap)
return (error);
case PT_IO:
error = copyin(uap->addr, &r.piod, sizeof r.piod);
if (error)
return (error);
iov.iov_base = r.piod.piod_addr;
iov.iov_len = r.piod.piod_len;
piod = addr;
iov.iov_base = piod->piod_addr;
iov.iov_len = piod->piod_len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = (off_t)(uintptr_t)r.piod.piod_offs;
uio.uio_resid = r.piod.piod_len;
uio.uio_offset = (off_t)(uintptr_t)piod->piod_offs;
uio.uio_resid = piod->piod_len;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_td = td;
switch (r.piod.piod_op) {
switch (piod->piod_op) {
case PIOD_READ_D:
case PIOD_READ_I:
uio.uio_rw = UIO_READ;
@ -642,60 +669,53 @@ ptrace(struct thread *td, struct ptrace_args *uap)
return (EINVAL);
}
error = proc_rwmem(p, &uio);
r.piod.piod_len -= uio.uio_resid;
(void)copyout(&r.piod, uap->addr, sizeof r.piod);
piod->piod_len -= uio.uio_resid;
return (error);
case PT_KILL:
uap->data = SIGKILL;
data = SIGKILL;
goto sendsig; /* in PT_CONTINUE above */
case PT_SETREGS:
_PHOLD(p);
error = proc_write_regs(td2, &r.reg);
error = proc_write_regs(td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
case PT_GETREGS:
_PHOLD(p);
error = proc_read_regs(td2, &r.reg);
error = proc_read_regs(td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
if (error == 0)
error = copyout(&r.reg, uap->addr, sizeof r.reg);
return (error);
case PT_SETFPREGS:
_PHOLD(p);
error = proc_write_fpregs(td2, &r.fpreg);
error = proc_write_fpregs(td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
case PT_GETFPREGS:
_PHOLD(p);
error = proc_read_fpregs(td2, &r.fpreg);
error = proc_read_fpregs(td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
if (error == 0)
error = copyout(&r.fpreg, uap->addr, sizeof r.fpreg);
return (error);
case PT_SETDBREGS:
_PHOLD(p);
error = proc_write_dbregs(td2, &r.dbreg);
error = proc_write_dbregs(td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
case PT_GETDBREGS:
_PHOLD(p);
error = proc_read_dbregs(td2, &r.dbreg);
error = proc_read_dbregs(td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
if (error == 0)
error = copyout(&r.dbreg, uap->addr, sizeof r.dbreg);
return (error);
default:

View File

@ -57,6 +57,8 @@ int kern_mknod(struct thread *td, char *path, enum uio_seg pathseg,
int mode, int dev);
int kern_open(struct thread *td, char *path, enum uio_seg pathseg,
int flags, int mode);
int kern_ptrace(struct thread *td, int req, pid_t pid, void *addr,
int data);
int kern_readlink(struct thread *td, char *path, enum uio_seg pathseg,
char *buf, enum uio_seg bufseg, int count);
int kern_rename(struct thread *td, char *from, char *to,