コード例 #1
0
int
procfs_dofpregs(struct proc *curp, struct lwp *lp, struct pfsnode *pfs,
		struct uio *uio)
{
	struct proc *p = lp->lwp_proc;
	int error;
	struct fpreg r;

	/* Can't trace a process that's currently exec'ing. */ 
	if ((p->p_flags & P_INEXEC) != 0)
		return EAGAIN;
	if (!CHECKIO(curp, p) || p_trespass(curp->p_ucred, p->p_ucred))
		return EPERM;

	LWPHOLD(lp);
	error = procfs_read_fpregs(lp, &r);
	if (error == 0)
		error = uiomove_frombuf(&r, sizeof(r), uio);
	if (error == 0 && uio->uio_rw == UIO_WRITE) {
		if (lp->lwp_stat != LSSTOP)
			error = EBUSY;
		else
			error = procfs_write_fpregs(lp, &r);
	}
	LWPRELE(lp);
	uio->uio_offset = 0;

	return (error);
}
コード例 #2
0
/*
 * Kill all LWPs except the current one.  Do not try to signal
 * LWPs which have exited on their own or have already been
 * signaled.
 */
static void
killlwps(struct lwp *lp)
{
	struct proc *p = lp->lwp_proc;
	struct lwp *tlp;

	/*
	 * Kill the remaining LWPs.  We must send the signal before setting
	 * LWP_WEXIT.  The setting of WEXIT is optional but helps reduce
	 * races.  tlp must be held across the call as it might block and
	 * allow the target lwp to rip itself out from under our loop.
	 */
	FOREACH_LWP_IN_PROC(tlp, p) {
		LWPHOLD(tlp);
		if ((tlp->lwp_flag & LWP_WEXIT) == 0) {
			lwpsignal(p, tlp, SIGKILL);
			tlp->lwp_flag |= LWP_WEXIT;
		}
		LWPRELE(tlp);
	}
コード例 #3
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);
}
コード例 #4
0
static int
procfs_control(struct proc *curp, struct lwp *lp, int op)
{
	struct proc *p = lp->lwp_proc;
	int error;

	ASSERT_LWKT_TOKEN_HELD(&p->p_token);
	ASSERT_LWKT_TOKEN_HELD(&proc_token);

	/* Can't trace a process that's currently exec'ing. */ 
	if ((p->p_flags & P_INEXEC) != 0)
		return EAGAIN;
	/*
	 * Authorization check: rely on normal debugging protection, except
	 * allow processes to disengage debugging on a process onto which
	 * they have previously attached, but no longer have permission to
	 * debug.
	 */
	if (op != PROCFS_CTL_DETACH) {
		if (securelevel > 0 && p->p_pid == 1)
			return (EPERM);

		if (!CHECKIO(curp, p) || p_trespass(curp->p_ucred, p->p_ucred))
			return (EPERM);
	}

	/*
	 * Attach - attaches the target process for debugging
	 * by the calling process.
	 */
	if (op == PROCFS_CTL_ATTACH) {
		/* check whether already being traced */
		if (p->p_flags & P_TRACED)
			return (EBUSY);

		/* can't trace yourself! */
		if (p->p_pid == curp->p_pid)
			return (EINVAL);

		/*
		 * Go ahead and set the trace flag.
		 * Save the old parent (it's reset in
		 *   _DETACH, and also in kern_exit.c:wait4()
		 * Reparent the process so that the tracing
		 *   proc gets to see all the action.
		 * Stop the target.
		 */
		p->p_flags |= P_TRACED;
		faultin(p);
		p->p_xstat = 0;		/* XXX ? */
		if (p->p_pptr != curp) {
			p->p_oppid = p->p_pptr->p_pid;
			proc_reparent(p, curp);
		}
		proc_stop(p);
		return (0);
	}

	/*
	 * Target process must be stopped, owned by (curp) and
	 * be set up for tracing (P_TRACED flag set).
	 * Allow DETACH to take place at any time for sanity.
	 * Allow WAIT any time, of course.
	 */
	switch (op) {
	case PROCFS_CTL_DETACH:
	case PROCFS_CTL_WAIT:
		break;

	default:
		if (!TRACE_WAIT_P(curp, p))
			return (EBUSY);
	}


#ifdef FIX_SSTEP
	/*
	 * do single-step fixup if needed
	 */
	FIX_SSTEP(lp);
#endif

	/*
	 * Don't deliver any signal by default.
	 * To continue with a signal, just send
	 * the signal name to the ctl file
	 */
	p->p_xstat = 0;

	switch (op) {
	/*
	 * Detach.  Cleans up the target process, reparent it if possible
	 * and set it running once more.
	 */
	case PROCFS_CTL_DETACH:
		/* if not being traced, then this is a painless no-op */
		if ((p->p_flags & P_TRACED) == 0)
			return (0);

		/* not being traced any more */
		p->p_flags &= ~P_TRACED;

		/* remove pending SIGTRAP, else the process will die */
		spin_lock(&lp->lwp_spin);
		lwp_delsig(lp, SIGTRAP);
		spin_unlock(&lp->lwp_spin);

		/* give process back to original parent */
		if (p->p_oppid != p->p_pptr->p_pid) {
			struct proc *pp;

			pp = pfs_pfind(p->p_oppid);
			if (pp) {
				proc_reparent(p, pp);
				pfs_pdone(pp);
			}
		}

		p->p_oppid = 0;
		p->p_flags &= ~P_WAITED;	/* XXX ? */
		wakeup((caddr_t) curp);		/* XXX for CTL_WAIT below ? */

		break;

	/*
	 * Step.  Let the target process execute a single instruction.
	 */
	case PROCFS_CTL_STEP:
		LWPHOLD(lp);
		error = procfs_sstep(lp);
		LWPRELE(lp);
		if (error)
			return (error);
		break;

	/*
	 * Run.  Let the target process continue running until a breakpoint
	 * or some other trap.
	 */
	case PROCFS_CTL_RUN:
		break;

	/*
	 * Wait for the target process to stop.
	 * If the target is not being traced then just wait
	 * to enter
	 */
	case PROCFS_CTL_WAIT:
		error = 0;
		if (p->p_flags & P_TRACED) {
			while (error == 0 &&
					p->p_stat != SSTOP &&
					(p->p_flags & P_TRACED) &&
					(p->p_pptr == curp)) {
				error = tsleep((caddr_t) p,
						PCATCH, "procfsx", 0);
			}
			if (error == 0 && !TRACE_WAIT_P(curp, p))
				error = EBUSY;
		} else {
			while (error == 0 && p->p_stat != SSTOP) {
				error = tsleep((caddr_t) p,
						PCATCH, "procfs", 0);
			}
		}
		return (error);

	default:
		panic("procfs_control");
	}

	/*
	 * If the process is in a stopped state, make it runnable again.
	 * Do not set LWP_MP_BREAKTSLEEP - that is, do not break a tsleep
	 * that might be in progress.
	 */
	if (p->p_stat == SSTOP)
		proc_unstop(p);
	return (0);
}