summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/i386/linux/linux_ptrace.c147
-rw-r--r--sys/kern/sys_process.c156
-rw-r--r--sys/sys/syscallsubr.h2
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,
OpenPOWER on IntegriCloud