Esempio n. 1
0
/*
 * MPALMOSTSAFE
 */
int
sys_linux_ptrace(struct linux_ptrace_args *uap)
{
    struct thread *td = curthread;
    struct proc *curp = td->td_proc;
    union {
        struct linux_pt_reg	reg;
        struct linux_pt_fpreg	fpreg;
        struct linux_pt_fpxreg	fpxreg;
    } r;
    union {
        struct reg		bsd_reg;
        struct fpreg		bsd_fpreg;
        struct dbreg		bsd_dbreg;
    } u;
    void *addr;
    pid_t pid;
    int error, req;
    struct proc *p = NULL;	/* held process */

    error = 0;

    /* by default, just copy data intact */
    req  = uap->req;
    pid  = (pid_t)uap->pid;
    addr = (void *)uap->addr;

    switch (req) {
    case PTRACE_TRACEME:
    case PTRACE_POKETEXT:
    case PTRACE_POKEDATA:
    case PTRACE_KILL:
        error = kern_ptrace(curp, req, pid, addr, uap->data,
                            &uap->sysmsg_iresult);
        break;
    case PTRACE_PEEKTEXT:
    case PTRACE_PEEKDATA: {
        /* need to preserve return value, use dummy */
        l_int rval = 0;
        error = kern_ptrace(curp, req, pid, addr, 0, &rval);
        if (error == 0) {
            error = copyout(&rval, (caddr_t)uap->data, sizeof(l_int));
        }
        break;
    }
    case PTRACE_DETACH:
        error = kern_ptrace(curp, PT_DETACH, pid, (void *)1,
                            map_signum(uap->data),
                            &uap->sysmsg_iresult);
        break;
    case PTRACE_SINGLESTEP:
    case PTRACE_CONT:
        error = kern_ptrace(curp, req, pid, (void *)1,
                            map_signum(uap->data),
                            &uap->sysmsg_iresult);
        break;
    case PTRACE_ATTACH:
        error = kern_ptrace(curp, PT_ATTACH, pid, addr, uap->data,
                            &uap->sysmsg_iresult);
        break;
    case PTRACE_GETREGS:
        /* Linux is using data where FreeBSD is using addr */
        error = kern_ptrace(curp, PT_GETREGS, pid, &u.bsd_reg, 0,
                            &uap->sysmsg_iresult);
        if (error == 0) {
            map_regs_to_linux(&u.bsd_reg, &r.reg);
            error = copyout(&r.reg, (void *)uap->data,
                            sizeof(r.reg));
        }
        break;
    case PTRACE_SETREGS:
        /* Linux is using data where FreeBSD is using addr */
        error = copyin((caddr_t)uap->data, &r.reg, sizeof(r.reg));
        if (error == 0) {
            map_regs_from_linux(&u.bsd_reg, &r.reg);
            error = kern_ptrace(curp, PT_SETREGS, pid, &u.bsd_reg,
                                0, &uap->sysmsg_iresult);
        }
        break;
    case PTRACE_GETFPREGS:
        /* Linux is using data where FreeBSD is using addr */
        error = kern_ptrace(curp, PT_GETFPREGS, pid, &u.bsd_fpreg,
                            0, &uap->sysmsg_iresult);
        if (error == 0) {
            map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg);
            error = copyout(&r.fpreg, (caddr_t)uap->data,
                            sizeof(r.fpreg));
        }
        break;
    case PTRACE_SETFPREGS:
        /* Linux is using data where FreeBSD is using addr */
        error = copyin((caddr_t)uap->data, &r.fpreg, sizeof(r.fpreg));
        if (error == 0) {
            map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg);
            error = kern_ptrace(curp, PT_SETFPREGS, pid,
                                &u.bsd_fpreg,
                                0, &uap->sysmsg_iresult);
        }
        break;
    case PTRACE_SETFPXREGS:
#ifndef CPU_DISABLE_SSE
        error = copyin((caddr_t)uap->data, &r.fpxreg,
                       sizeof(r.fpxreg));
        if (error)
            break;
#endif
    /* FALL THROUGH */
    case PTRACE_GETFPXREGS: {
#ifndef CPU_DISABLE_SSE
        struct proc *p;
        struct lwp *lp;

        if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) {
            static int once = 0;
            if (!once) {
                kprintf("linux: savexmm != linux_pt_fpxreg\n");
                once = 1;
            }
            error = EIO;
            break;
        }

        if ((p = pfind(uap->pid)) == NULL) {
            error = ESRCH;
            break;
        }

        if (!PRISON_CHECK(curp->p_ucred, p->p_ucred)) {
            error = ESRCH;
            goto fail;
        }

        /* System processes can't be debugged. */
        if ((p->p_flag & P_SYSTEM) != 0) {
            error = EINVAL;
            goto fail;
        }

        /* not being traced... */
        if ((p->p_flag & P_TRACED) == 0) {
            error = EPERM;
            goto fail;
        }

        /* not being traced by YOU */
        if (p->p_pptr != curp) {
            error = EBUSY;
            goto fail;
        }

        /* not currently stopped */
        if ((p->p_flag & (P_TRACED|P_WAITED)) == 0) {
            error = EBUSY;
            goto fail;
        }

        /* XXX lwp */
        lp = FIRST_LWP_IN_PROC(p);

        if (req == PTRACE_GETFPXREGS) {
            LWPHOLD(lp);
            error = linux_proc_read_fpxregs(lp, &r.fpxreg);
            LWPRELE(lp);
            if (error == 0)
                error = copyout(&r.fpxreg, (caddr_t)uap->data,
                                sizeof(r.fpxreg));
        } else {
            /* clear dangerous bits exactly as Linux does*/
            r.fpxreg.mxcsr &= 0xffbf;
            LWPHOLD(lp);
            error = linux_proc_write_fpxregs(lp, &r.fpxreg);
            LWPRELE(lp);
        }
        break;

fail:
#else
        error = EIO;
#endif
        break;
    }
    case PTRACE_PEEKUSR:
    case PTRACE_POKEUSR: {
        error = EIO;

        /* check addr for alignment */
        if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1))
            break;
        /*
         * Allow linux programs to access register values in
         * user struct. We simulate this through PT_GET/SETREGS
         * as necessary.
         */
        if (uap->addr < sizeof(struct linux_pt_reg)) {
            error = kern_ptrace(curp, PT_GETREGS, pid, &u.bsd_reg,
                                0, &uap->sysmsg_iresult);
            if (error != 0)
                break;

            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;
            }

            *(l_int *)((char *)&r.reg + uap->addr) =
                (l_int)uap->data;

            map_regs_from_linux(&u.bsd_reg, &r.reg);
            error = kern_ptrace(curp, PT_SETREGS, pid, &u.bsd_reg,
                                0, &uap->sysmsg_iresult);
        }

        /*
         * Simulate debug registers access
         */
        if (uap->addr >= LINUX_DBREG_OFFSET &&
                uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) {
            error = kern_ptrace(curp, PT_GETDBREGS, pid,
                                &u.bsd_dbreg,
                                0, &uap->sysmsg_iresult);
            if (error != 0)
                break;

            uap->addr -= LINUX_DBREG_OFFSET;
            if (req == PTRACE_PEEKUSR) {
                error = copyout((char *)&u.bsd_dbreg +
                                uap->addr, (caddr_t)uap->data,
                                sizeof(l_int));
                break;
            }

            *(l_int *)((char *)&u.bsd_dbreg + uap->addr) =
                uap->data;
            error = kern_ptrace(curp, PT_SETDBREGS, pid,
                                &u.bsd_dbreg,
                                0, &uap->sysmsg_iresult);
        }

        break;
    }
    case PTRACE_SYSCALL:
    /* fall through */
    default:
        kprintf("linux: ptrace(%u, ...) not implemented\n",
                (unsigned int)uap->req);
        error = EINVAL;
        break;
    }

    /*
     * Release held proces (if any) before returning.
     */
    if (p)
        PRELE(p);
    return (error);
}
Esempio n. 2
0
int
linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
{
	union {
		struct linux_pt_reg	reg;
		struct linux_pt_fpreg	fpreg;
		struct linux_pt_fpxreg	fpxreg;
	} r;
	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 */
	req  = uap->req;
	pid  = (pid_t)uap->pid;
	addr = (void *)uap->addr;

	switch (req) {
	case PTRACE_TRACEME:
	case PTRACE_POKETEXT:
	case PTRACE_POKEDATA:
	case PTRACE_KILL:
		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];
		error = kern_ptrace(td, req, pid, addr, 0);
		if (error == 0)
			error = copyout(td->td_retval, (void *)uap->data,
			    sizeof(l_int));
		td->td_retval[0] = rval;
		break;
	}
	case PTRACE_DETACH:
		error = kern_ptrace(td, PT_DETACH, pid, (void *)1,
		     map_signum(uap->data));
		break;
	case PTRACE_SINGLESTEP:
	case PTRACE_CONT:
		error = kern_ptrace(td, req, pid, (void *)1,
		     map_signum(uap->data));
		break;
	case PTRACE_ATTACH:
		error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
		break;
	case PTRACE_GETREGS:
		/* Linux is using data where FreeBSD is using addr */
		error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
		if (error == 0) {
			map_regs_to_linux(&u.bsd_reg, &r.reg);
			error = copyout(&r.reg, (void *)uap->data,
			    sizeof(r.reg));
		}
		break;
	case PTRACE_SETREGS:
		/* Linux is using data where FreeBSD is using addr */
		error = copyin((void *)uap->data, &r.reg, sizeof(r.reg));
		if (error == 0) {
			map_regs_from_linux(&u.bsd_reg, &r.reg);
			error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
		}
		break;
	case PTRACE_GETFPREGS:
		/* Linux is using data where FreeBSD is using addr */
		error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0);
		if (error == 0) {
			map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg);
			error = copyout(&r.fpreg, (void *)uap->data,
			    sizeof(r.fpreg));
		}
		break;
	case PTRACE_SETFPREGS:
		/* Linux is using data where FreeBSD is using addr */
		error = copyin((void *)uap->data, &r.fpreg, sizeof(r.fpreg));
		if (error == 0) {
			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_SSE
		error = copyin((void *)uap->data, &r.fpxreg, sizeof(r.fpxreg));
		if (error)
			break;
#endif
		/* FALL THROUGH */
	case PTRACE_GETFPXREGS: {
#ifdef CPU_ENABLE_SSE
		struct proc *p;
		struct thread *td2;

		if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) {
			static int once = 0;
			if (!once) {
				printf("linux: savexmm != linux_pt_fpxreg\n");
				once = 1;
			}
			error = EIO;
			break;
		}

		if ((p = pfind(uap->pid)) == NULL) {
			error = ESRCH;
			break;
		}

		/* Exiting processes can't be debugged. */
		if ((p->p_flag & P_WEXIT) != 0) {
			error = ESRCH;
			goto fail;
		}

		if ((error = p_candebug(td, p)) != 0)
			goto fail;

		/* System processes can't be debugged. */
		if ((p->p_flag & P_SYSTEM) != 0) {
			error = EINVAL;
			goto fail;
		}

		/* not being traced... */
		if ((p->p_flag & P_TRACED) == 0) {
			error = EPERM;
			goto fail;
		}

		/* not being traced by YOU */
		if (p->p_pptr != td->td_proc) {
			error = EBUSY;
			goto fail;
		}

		/* not currently stopped */
		if (!P_SHOULDSTOP(p) || (p->p_flag & P_WAITED) == 0) {
			error = EBUSY;
			goto fail;
		}

		if (req == PTRACE_GETFPXREGS) {
			_PHOLD(p);	/* may block */
			td2 = FIRST_THREAD_IN_PROC(p);
			error = linux_proc_read_fpxregs(td2, &r.fpxreg);
			_PRELE(p);
			PROC_UNLOCK(p);
			if (error == 0)
				error = copyout(&r.fpxreg, (void *)uap->data,
				    sizeof(r.fpxreg));
		} else {
			/* clear dangerous bits exactly as Linux does*/
			r.fpxreg.mxcsr &= 0xffbf;
			_PHOLD(p);	/* may block */
			td2 = FIRST_THREAD_IN_PROC(p);
			error = linux_proc_write_fpxregs(td2, &r.fpxreg);
			_PRELE(p);
			PROC_UNLOCK(p);
		}
		break;

	fail:
		PROC_UNLOCK(p);
#else
		error = EIO;
#endif
		break;
	}
	case PTRACE_PEEKUSR:
	case PTRACE_POKEUSR: {
		error = EIO;

		/* check addr for alignment */
		if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1))
			break;
		/*
		 * Allow linux programs to access register values in
		 * user struct. We simulate this through PT_GET/SETREGS
		 * as necessary.
		 */
		if (uap->addr < sizeof(struct linux_pt_reg)) {
			error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
			if (error != 0)
				break;

			map_regs_to_linux(&u.bsd_reg, &r.reg);
			if (req == PTRACE_PEEKUSR) {
				error = copyout((char *)&r.reg + uap->addr,
				    (void *)uap->data, sizeof(l_int));
				break;
			}

			*(l_int *)((char *)&r.reg + uap->addr) =
			    (l_int)uap->data;

			map_regs_from_linux(&u.bsd_reg, &r.reg);
			error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
		}

		/*
		 * Simulate debug registers access
		 */
		if (uap->addr >= LINUX_DBREG_OFFSET &&
		    uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) {
			error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg,
			    0);
			if (error != 0)
				break;

			uap->addr -= LINUX_DBREG_OFFSET;
			if (req == PTRACE_PEEKUSR) {
				error = copyout((char *)&u.bsd_dbreg +
				    uap->addr, (void *)uap->data,
				    sizeof(l_int));
				break;
			}

			*(l_int *)((char *)&u.bsd_dbreg + uap->addr) =
			     uap->data;
			error = kern_ptrace(td, PT_SETDBREGS, pid,
			    &u.bsd_dbreg, 0);
		}

		break;
	}
	case PTRACE_SYSCALL:
		/* fall through */
	default:
		printf("linux: ptrace(%u, ...) not implemented\n",
		    (unsigned int)uap->req);
		error = EINVAL;
		break;
	}

	return (error);
}