diff options
author | peter <peter@FreeBSD.org> | 1996-01-24 18:29:00 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1996-01-24 18:29:00 +0000 |
commit | 372aa68f42770d9c66509d34f90c593eadc9069c (patch) | |
tree | 5ae2be9fdd5205120a370f7e6721742906ca7f19 /sys/kern/sys_process.c | |
parent | 021011c8f8c5a4913dd07d06c30889167dcfa85a (diff) | |
download | FreeBSD-src-372aa68f42770d9c66509d34f90c593eadc9069c.zip FreeBSD-src-372aa68f42770d9c66509d34f90c593eadc9069c.tar.gz |
Major fixes for ptrace()...
PT_ATTACH/PT_DETACH implemented now and fully operational.
PT_{GET|SET}{REGS|FPREFS} implemented now, using code shared with procfs
PT_{READ|WRITE}_{I|D} now uses code shared with procfs
ptrace opcodes now fully permission checked, including ownerships.
doing an operation to the u-area on a swapped process should no longer
panic.
running gdb as root works for me now, where it didn't before.
general cleanup..
Note, that this has some tightening of permissions/access checks etc.
Some of these may be going too far.. In particular, the "owner" of the
traced process is enforced. The process that created or attached to
the traced process is now the only one that can "do" things to it.
Diffstat (limited to 'sys/kern/sys_process.c')
-rw-r--r-- | sys/kern/sys_process.c | 325 |
1 files changed, 226 insertions, 99 deletions
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index cf004dc..c26e3cd 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -28,7 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: sys_process.c,v 1.19 1995/12/17 06:59:36 bde Exp $ + * $Id: sys_process.c,v 1.20 1996/01/19 03:58:04 dyson Exp $ */ #include <sys/param.h> @@ -54,7 +54,10 @@ #include <vm/vm_extern.h> #include <sys/user.h> +#include <miscfs/procfs/procfs.h> +/* use the equivalent procfs code */ +#if 0 static int pread (struct proc *procp, unsigned int addr, unsigned int *retval) { int rv; @@ -193,6 +196,7 @@ pwrite (struct proc *procp, unsigned int addr, unsigned int datum) { VM_PROT_READ|VM_PROT_EXECUTE, 0); return rv; } +#endif /* * Process debugging system call. @@ -213,62 +217,159 @@ ptrace(curp, uap, retval) int *retval; { struct proc *p; + struct iovec iov; + struct uio uio; int error = 0; + int write; + int s; - *retval = 0; - if (uap->req == PT_TRACE_ME) { - curp->p_flag |= P_TRACED; - return 0; - } - if ((p = pfind(uap->pid)) == NULL) { - return ESRCH; + if (uap->req == PT_TRACE_ME) + p = curp; + else { + if ((p = pfind(uap->pid)) == NULL) + return ESRCH; } -#ifdef PT_ATTACH - if (uap->req != PT_ATTACH && ( - (p->p_flag & P_TRACED) == 0 || - (p->p_tptr && curp != p->p_tptr) || - (!p->p_tptr && curp != p->p_pptr))) + /* + * Permissions check + */ + switch (uap->req) { + case PT_TRACE_ME: + /* Always legal. */ + break; + + case PT_ATTACH: + /* Self */ + if (p->p_pid == curp->p_pid) + return EINVAL; + + /* Already traced */ + if (p->p_flag & P_TRACED) + return EBUSY; + + /* not owned by you, has done setuid (unless you're root) */ + if ((p->p_cred->p_ruid != curp->p_cred->p_ruid) || + (p->p_flag & P_SUGID)) { + if (error = suser(curp->p_ucred, &curp->p_acflag)) + return error; + } + + /* OK */ + break; - return ESRCH; + case PT_READ_I: + case PT_READ_D: + case PT_READ_U: + case PT_WRITE_I: + case PT_WRITE_D: + case PT_WRITE_U: + case PT_CONTINUE: + case PT_KILL: + case PT_STEP: + case PT_DETACH: +#ifdef PT_GETREGS + case PT_GETREGS: +#endif +#ifdef PT_SETREGS + case PT_SETREGS: +#endif +#ifdef PT_GETFPREGS + case PT_GETFPREGS: #endif -#ifdef PT_ATTACH - if (uap->req != PT_ATTACH) { +#ifdef PT_SETFPREGS + case PT_SETFPREGS: #endif + /* not being traced... */ if ((p->p_flag & P_TRACED) == 0) return EPERM; + + /* not being traced by YOU */ + if (p->p_pptr != curp) + return EBUSY; + + /* not currently stopped */ if (p->p_stat != SSTOP || (p->p_flag & P_WAITED) == 0) return EBUSY; -#ifdef PT_ATTACH + + /* OK */ + break; + + default: + return EINVAL; } + +#ifdef FIX_SSTEP + /* + * Single step fixup ala procfs + */ + FIX_SSTEP(p); #endif + /* - * XXX The PT_ATTACH code is completely broken. It will - * be obsoleted by a /proc filesystem, so is it worth it - * to fix it? (Answer, probably. So that'll be next, - * I guess.) + * Actually do the requests */ - switch (uap->req) { -#ifdef PT_ATTACH - case PT_ATTACH: - if (curp->p_ucred->cr_uid != 0 && ( - curp->p_ucred->cr_uid != p->p_ucred->cr_uid || - curp->p_ucred->cr_uid != p->p_cred->p_svuid)) - return EACCES; + write = 0; + *retval = 0; - p->p_tptr = curp; + switch (uap->req) { + case PT_TRACE_ME: + /* set my trace flag and "owner" so it can read/write me */ p->p_flag |= P_TRACED; - psignal(p, SIGSTOP); + p->p_oppid = p->p_pptr->p_pid; return 0; + case PT_ATTACH: + /* security check done above */ + p->p_flag |= P_TRACED; + p->p_oppid = p->p_pptr->p_pid; + if (p->p_pptr != curp) + proc_reparent(p, curp); + uap->data = SIGSTOP; + goto sendsig; /* in PT_CONTINUE below */ + + case PT_STEP: + case PT_CONTINUE: case PT_DETACH: if ((unsigned)uap->data >= NSIG) return EINVAL; - p->p_flag &= ~P_TRACED; - p->p_tptr = NULL; - psignal(p->p_pptr, SIGCHLD); - wakeup((caddr_t)p->p_pptr); + + PHOLD(p); + + if (uap->req == PT_STEP) { + if ((error = ptrace_single_step (p))) { + PRELE(p); + return error; + } + } + + if (uap->addr != (caddr_t)1) { + fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); + if ((error = ptrace_set_pc (p, (u_int)uap->addr))) { + PRELE(p); + return error; + } + } + PRELE(p); + + if (uap->req == PT_DETACH) { + /* reset process parent */ + if (p->p_oppid != p->p_pptr->p_pid) { + struct proc *pp; + + pp = pfind(p->p_oppid); + proc_reparent(p, pp ? pp : initproc); + } + + p->p_flag &= ~(P_TRACED | P_WAITED); + p->p_oppid = 0; + + /* should we send SIGCHLD? */ + + } + + sendsig: + /* deliver or queue signal */ s = splhigh(); if (p->p_stat == SSTOP) { p->p_xstat = uap->data; @@ -279,83 +380,109 @@ ptrace(curp, uap, retval) splx(s); return 0; -# ifdef PT_INHERIT - case PT_INHERIT: - if ((p->p_flag & P_TRACED) == 0) - return ESRCH; - return 0; -# endif /* PT_INHERIT */ -#endif /* PT_ATTACH */ - - case PT_READ_I: - case PT_READ_D: - if ((error = pread (p, (unsigned int)uap->addr, retval))) - return error; - return 0; case PT_WRITE_I: case PT_WRITE_D: - if ((error = pwrite (p, (unsigned int)uap->addr, - (unsigned int)uap->data))) - return error; - return 0; - case PT_STEP: - if ((error = ptrace_single_step (p))) - return error; + write = 1; /* fallthrough */ - case PT_CONTINUE: - /* - * Continue at addr uap->addr with signal - * uap->data; if uap->addr is 1, then we just - * let the chips fall where they may. - * - * The only check I'll make right now is for - * uap->data to be larger than NSIG; if so, we return - * EINVAL. - */ - if (uap->data >= NSIG) - return EINVAL; - - if (uap->addr != (caddr_t)1) { - fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); - if ((error = ptrace_set_pc (p, (u_int)uap->addr))) - return error; - } - - p->p_xstat = uap->data; + case PT_READ_I: + case PT_READ_D: + /* write = 0 set above */ + iov.iov_base = write ? (caddr_t)&uap->data : (caddr_t)retval; + iov.iov_len = sizeof(int); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = (off_t)(u_long)uap->addr; + uio.uio_resid = sizeof(int); + uio.uio_segflg = UIO_SYSSPACE; /* ie: the uap */ + uio.uio_rw = write ? UIO_WRITE : UIO_READ; + uio.uio_procp = p; + return(procfs_domem(curp, p, NULL, &uio)); -/* if (p->p_stat == SSTOP) */ - setrunnable (p); - return 0; case PT_READ_U: if ((u_int)uap->addr > (UPAGES * NBPG - sizeof(int))) { return EFAULT; } - p->p_addr->u_kproc.kp_proc = *p; - fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); - *retval = *(int*)((u_int)p->p_addr + (u_int)uap->addr); - return 0; + error = 0; + PHOLD(p); /* user had damn well better be incore! */ + if (p->p_flag & P_INMEM) { + p->p_addr->u_kproc.kp_proc = *p; + fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); + *retval = *(int*)((u_int)p->p_addr + (u_int)uap->addr); + } else { + *retval = 0; + error = EFAULT; + } + PRELE(p); + return error; + case PT_WRITE_U: - p->p_addr->u_kproc.kp_proc = *p; - fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); - return ptrace_write_u(p, (vm_offset_t)uap->addr, uap->data); + PHOLD(p); /* user had damn well better be incore! */ + if (p->p_flag & P_INMEM) { + p->p_addr->u_kproc.kp_proc = *p; + fill_eproc (p, &p->p_addr->u_kproc.kp_eproc); + error = ptrace_write_u(p, (vm_offset_t)uap->addr, uap->data); + } else { + error = EFAULT; + } + PRELE(p); + return error; + case PT_KILL: - p->p_xstat = SIGKILL; - setrunnable(p); - return 0; + uap->data = SIGKILL; + goto sendsig; /* in PT_CONTINUE above */ + +#ifdef PT_SETREGS + case PT_SETREGS: + write = 1; + /* fallthrough */ +#endif /* PT_SETREGS */ #ifdef PT_GETREGS case PT_GETREGS: - /* - * copyout the registers into addr. There's no - * size constraint!!! *GRRR* - */ - return ptrace_getregs(p, uap->addr); - case PT_SETREGS: - /* - * copyin the registers from addr. Again, no - * size constraint!!! *GRRRR* - */ - return ptrace_setregs (p, uap->addr); -#endif /* PT_GETREGS */ + /* write = 0 above */ +#endif /* PT_SETREGS */ +#if defined(PT_SETREGS) || defined(PT_GETREGS) + if (!procfs_validregs(p)) /* no P_SYSTEM procs please */ + return EINVAL; + else { + iov.iov_base = uap->addr; + iov.iov_len = sizeof(struct reg); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = 0; + uio.uio_resid = sizeof(struct reg); + uio.uio_segflg = UIO_USERSPACE; + uio.uio_rw = write ? UIO_WRITE : UIO_READ; + uio.uio_procp = curp; + return (procfs_doregs(curp, p, NULL, &uio)); + } +#endif /* defined(PT_SETREGS) || defined(PT_GETREGS) */ + +#ifdef PT_SETFPREGS + case PT_SETFPREGS: + write = 1; + /* fallthrough */ +#endif /* PT_SETFPREGS */ +#ifdef PT_GETFPREGS + case PT_GETFPREGS: + /* write = 0 above */ +#endif /* PT_SETFPREGS */ +#if defined(PT_SETFPREGS) || defined(PT_GETFPREGS) + if (!procfs_validfpregs(p)) /* no P_SYSTEM procs please */ + return EINVAL; + else { + iov.iov_base = uap->addr; + iov.iov_len = sizeof(struct fpreg); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = 0; + uio.uio_resid = sizeof(struct fpreg); + uio.uio_segflg = UIO_USERSPACE; + uio.uio_rw = write ? UIO_WRITE : UIO_READ; + uio.uio_procp = curp; + return (procfs_dofpregs(curp, p, NULL, &uio)); + } +#endif /* defined(PT_SETFPREGS) || defined(PT_GETFPREGS) */ + default: break; } |