int netbsd32_ptrace(struct lwp *l, const struct netbsd32_ptrace_args *uap, register_t *retval) { /* { syscallarg(int) req; syscallarg(pid_t) pid; syscallarg(netbsd32_voidp *) addr; syscallarg(int) data; } */ return do_ptrace(&netbsd32_ptm, l, SCARG(uap, req), SCARG(uap, pid), SCARG_P32(uap, addr), SCARG(uap, data), retval); }
static int dt_proc_create_thread(dtrace_hdl_t *dtp, dt_proc_t *dpr, uint_t stop) { dt_proc_control_data_t data; sigset_t nset, oset; pthread_attr_t a; int err; #if defined(linux) /***********************************************/ /* Kernel bug -- a parent which attaches to */ /* a proc -- cannot share with a child */ /* thread. So we have to detach and */ /* reattach in the child thread if we are */ /* to work properly. */ /***********************************************/ if (stop == DT_PROC_STOP_GRAB) { do_ptrace(__func__, PTRACE_DETACH, dpr->dpr_pid, 0, 0); } #endif (void) pthread_mutex_lock(&dpr->dpr_lock); dpr->dpr_stop |= stop; /* set bit for initial rendezvous */ (void) pthread_attr_init(&a); (void) pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); (void) sigfillset(&nset); (void) sigdelset(&nset, SIGABRT); /* unblocked for assert() */ #if defined(sun) (void) sigdelset(&nset, SIGCANCEL); /* see dt_proc_destroy() */ #else (void) sigdelset(&nset, SIGUSR1); /* see dt_proc_destroy() */ #endif data.dpcd_hdl = dtp; data.dpcd_proc = dpr; (void) pthread_sigmask(SIG_SETMASK, &nset, &oset); err = pthread_create(&dpr->dpr_tid, &a, dt_proc_control, &data); (void) pthread_sigmask(SIG_SETMASK, &oset, NULL); do_ptrace(__func__, PTRACE_DETACH, dpr->dpr_pid, 0, 0); /* * If the control thread was created, then wait on dpr_cv for either * dpr_done to be set (the victim died or the control thread failed) * or DT_PROC_STOP_IDLE to be set, indicating that the victim is now * stopped by /proc and the control thread is at the rendezvous event. * On success, we return with the process and control thread stopped: * the caller can then apply dt_proc_continue() to resume both. */ if (err == 0) { //printf("0..waiting for dt_proc_control....dpr_done=%d stop=%d !stop=%d\n", dpr->dpr_done, dpr->dpr_stop, !(dpr->dpr_stop & DT_PROC_STOP_IDLE)); while (!dpr->dpr_done && !(dpr->dpr_stop & DT_PROC_STOP_IDLE)) { //printf("1..waiting for dt_proc_control....dpr_done=%d stop=%d\n", dpr->dpr_done, dpr->dpr_stop); (void) pthread_cond_wait(&dpr->dpr_cv, &dpr->dpr_lock); } //printf("2..waiting for dt_proc_control....dpr_done=%d stop=%d\n", dpr->dpr_done, dpr->dpr_stop); /* * If dpr_done is set, the control thread aborted before it * reached the rendezvous event. This is either due to PS_LOST * or PS_UNDEAD (i.e. the process died). We try to provide a * small amount of useful information to help figure it out. */ if (dpr->dpr_done) { const psinfo_t *prp = Ppsinfo(dpr->dpr_proc); int stat = prp ? prp->pr_wstat : 0; int pid = dpr->dpr_pid; if (Pstate(dpr->dpr_proc) == PS_LOST) { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process exec'd " "set-id or unobservable program\n", pid); } else if (WIFSIGNALED(stat)) { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process died " "from signal %d\n", pid, WTERMSIG(stat)); } else { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to control pid %d: process exited " "with status %d\n", pid, WEXITSTATUS(stat)); } err = ESRCH; /* cause grab() or create() to fail */ } } else { (void) dt_proc_error(dpr->dpr_hdl, dpr, "failed to create control thread for process-id %d: %s\n", (int)dpr->dpr_pid, strerror(err)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); (void) pthread_attr_destroy(&a); return (err); }
/* * Main loop for all victim process control threads. We initialize all the * appropriate /proc control mechanisms, and then enter a loop waiting for * the process to stop on an event or die. We process any events by calling * appropriate subroutines, and exit when the victim dies or we lose control. * * The control thread synchronizes the use of dpr_proc with other libdtrace * threads using dpr_lock. We hold the lock for all of our operations except * waiting while the process is running: this is accomplished by writing a * PCWSTOP directive directly to the underlying /proc/<pid>/ctl file. If the * libdtrace client wishes to exit or abort our wait, SIGCANCEL can be used. */ static void * dt_proc_control(void *arg) { dt_proc_control_data_t *datap = arg; dtrace_hdl_t *dtp = datap->dpcd_hdl; dt_proc_t *dpr = datap->dpcd_proc; dt_proc_hash_t *dph = dpr->dpr_hdl->dt_procs; struct ps_prochandle *P = dpr->dpr_proc; #if defined(sun) int pfd = Pctlfd(P); const long wstop = PCWSTOP; #endif int notify = B_FALSE; /* * We disable the POSIX thread cancellation mechanism so that the * client program using libdtrace can't accidentally cancel our thread. * dt_proc_destroy() uses SIGCANCEL explicitly to simply poke us out * of PCWSTOP with EINTR, at which point we will see dpr_quit and exit. */ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); dpr->dpr_pid = proc_getpid(P); int pid = dpr->dpr_pid; /* * Set up the corresponding process for tracing by libdtrace. We want * to be able to catch breakpoints and efficiently single-step over * them, and we need to enable librtld_db to watch libdl activity. */ do_ptrace(__func__, PTRACE_ATTACH, dpr->dpr_pid, 0, 0); (void) pthread_mutex_lock(&dpr->dpr_lock); (void) Punsetflags(P, PR_ASYNC); /* require synchronous mode */ (void) Psetflags(P, PR_BPTADJ); /* always adjust eip on x86 */ (void) Punsetflags(P, PR_FORK); /* do not inherit on fork */ (void) Pfault(P, FLTBPT, B_TRUE); /* always trace breakpoints */ (void) Pfault(P, FLTTRACE, B_TRUE); /* always trace single-step */ /* * We must trace exit from exec() system calls so that if the exec is * successful, we can reset our breakpoints and re-initialize libproc. */ (void) Psysexit(P, SYS_exec, B_TRUE); (void) Psysexit(P, SYS_execve, B_TRUE); /* * We must trace entry and exit for fork() system calls in order to * disable our breakpoints temporarily during the fork. We do not set * the PR_FORK flag, so if fork succeeds the child begins executing and * does not inherit any other tracing behaviors or a control thread. */ (void) Psysentry(P, SYS_vfork, B_TRUE); (void) Psysexit(P, SYS_vfork, B_TRUE); (void) Psysentry(P, SYS_fork1, B_TRUE); (void) Psysexit(P, SYS_fork1, B_TRUE); (void) Psysentry(P, SYS_forkall, B_TRUE); (void) Psysexit(P, SYS_forkall, B_TRUE); (void) Psysentry(P, SYS_forksys, B_TRUE); (void) Psysexit(P, SYS_forksys, B_TRUE); Psync(P); /* enable all /proc changes */ dt_proc_attach(dpr, B_FALSE); /* enable rtld breakpoints */ /* * If PR_KLC is set, we created the process; otherwise we grabbed it. * Check for an appropriate stop request and wait for dt_proc_continue. */ dpr->dpr_stop |= DT_PROC_STOP_CREATE; if (Pstatus(P)->pr_flags & PR_KLC) dt_proc_stop(dpr, DT_PROC_STOP_CREATE); else dt_proc_stop(dpr, DT_PROC_STOP_GRAB); if (Psetrun(P, 0, 0) == -1) { dt_dprintf("pid %d: failed to set running: %s\n", (int)dpr->dpr_pid, strerror(errno)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); /* * Wait for the process corresponding to this control thread to stop, * process the event, and then set it running again. We want to sleep * with dpr_lock *unheld* so that other parts of libdtrace can use the * ps_prochandle in the meantime (e.g. ustack()). To do this, we write * a PCWSTOP directive directly to the underlying /proc/<pid>/ctl file. * Once the process stops, we wake up, grab dpr_lock, and then call * Pwait() (which will return immediately) and do our processing. */ //printf("%s: waiting to quit\n", __func__); while (!dpr->dpr_quit) { const lwpstatus_t *psp; #if defined(sun) if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR) continue; /* check dpr_quit and continue waiting */ #else /* Wait for the process to report status. */ proc_wait(P); #endif (void) pthread_mutex_lock(&dpr->dpr_lock); pwait_locked: if (Pstopstatus(P, PCNULL, 0) == -1 && errno == EINTR) { //printf("%s stopstatus (loop) pr_pid pid=%d\n", __func__, Pstatus(dpr->dpr_proc)->pr_pid); (void) pthread_mutex_unlock(&dpr->dpr_lock); continue; /* check dpr_quit and continue waiting */ } switch (Pstate(P)) { case PS_STOP: psp = &Pstatus(P)->pr_lwp; dt_dprintf("pid %d: proc stopped showing %d/%d\n", pid, psp->pr_why, psp->pr_what); #if defined(sun) /* * If the process stops showing PR_REQUESTED, then the * DTrace stop() action was applied to it or another * debugging utility (e.g. pstop(1)) asked it to stop. * In either case, the user's intention is for the * process to remain stopped until another external * mechanism (e.g. prun(1)) is applied. So instead of * setting the process running ourself, we wait for * someone else to do so. Once that happens, we return * to our normal loop waiting for an event of interest. */ if (psp->pr_why == PR_REQUESTED) { dt_proc_waitrun(dpr); (void) pthread_mutex_unlock(&dpr->dpr_lock); continue; } /* * If the process stops showing one of the events that * we are tracing, perform the appropriate response. * Note that we ignore PR_SUSPENDED, PR_CHECKPOINT, and * PR_JOBCONTROL by design: if one of these conditions * occurs, we will fall through to Psetrun() but the * process will remain stopped in the kernel by the * corresponding mechanism (e.g. job control stop). */ if (psp->pr_why == PR_FAULTED && psp->pr_what == FLTBPT) dt_proc_bpmatch(dtp, dpr); else if (psp->pr_why == PR_SYSENTRY && IS_SYS_FORK(psp->pr_what)) dt_proc_bpdisable(dpr); else if (psp->pr_why == PR_SYSEXIT && IS_SYS_FORK(psp->pr_what)) dt_proc_bpenable(dpr); else if (psp->pr_why == PR_SYSEXIT && IS_SYS_EXEC(psp->pr_what)) dt_proc_attach(dpr, B_TRUE); #endif //printf("In PS_STOP dpr_stop=%x\n", dpr->dpr_stop); break; case PS_LOST: //printf("in PS_LOST\n"); if (Preopen(P) == 0) goto pwait_locked; dt_dprintf("pid %d: proc lost: %s\n", pid, strerror(errno)); dpr->dpr_quit = B_TRUE; notify = B_TRUE; break; case PS_UNDEAD: case PS_DEAD: dt_dprintf("pid %d: proc died\n", pid); dpr->dpr_quit = B_TRUE; notify = B_TRUE; break; } if (Pstate(P) != PS_UNDEAD && Psetrun(P, 0, 0) == -1) { dt_dprintf("pid %d: failed to set running: %s\n", (int)dpr->dpr_pid, strerror(errno)); } (void) pthread_mutex_unlock(&dpr->dpr_lock); } /* * If the control thread detected PS_UNDEAD or PS_LOST, then enqueue * the dt_proc_t structure on the dt_proc_hash_t notification list. */ if (notify) dt_proc_notify(dtp, dph, dpr, NULL); /* * Destroy and remove any remaining breakpoints, set dpr_done and clear * dpr_tid to indicate the control thread has exited, and notify any * waiting thread in dt_proc_destroy() that we have succesfully exited. */ (void) pthread_mutex_lock(&dpr->dpr_lock); dt_proc_bpdestroy(dpr, B_TRUE); dpr->dpr_done = B_TRUE; dpr->dpr_tid = 0; (void) pthread_cond_broadcast(&dpr->dpr_cv); (void) pthread_mutex_unlock(&dpr->dpr_lock); return (NULL); }
static long do_pokeuser(long offset, long val) { return do_ptrace(PTRACE_POKEUSER, (void *)offset, (void *)val); }
static long do_peekuser(long offset) { return do_ptrace(PTRACE_PEEKUSER, (void *)offset, NULL); }
static int trace_sysnum_regs(void *vregs) { trace_regs *regs = vregs; do_ptrace(PTRACE_GETREGS, regs, NULL); return regs->u_regs[U_REG_G1] ? : SB_SYS_EXECVE; }