diff options
-rw-r--r-- | sys/i386/linux/linux_ptrace.c | 147 | ||||
-rw-r--r-- | sys/kern/sys_process.c | 156 | ||||
-rw-r--r-- | sys/sys/syscallsubr.h | 2 |
3 files changed, 142 insertions, 163 deletions
diff --git a/sys/i386/linux/linux_ptrace.c b/sys/i386/linux/linux_ptrace.c index a19dcc7..f5e47f9 100644 --- a/sys/i386/linux/linux_ptrace.c +++ b/sys/i386/linux/linux_ptrace.c @@ -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; diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 0ed7c08..69c0d6f 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -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: diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 49a3017..aa434c2 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -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, |