예제 #1
0
파일: ipc.c 프로젝트: ctalbert/rr
long read_child_code(pid_t pid, void* addr)
{
	CHECK_ALIGNMENT(addr);

	long tmp;
	tmp = sys_ptrace(PTRACE_PEEKTEXT, pid, addr, 0);
	return tmp;
}
예제 #2
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_setup(pid_t pid)
{
	int flags = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXIT;
	/* First try with seccomp */
	if (ptrace(PTRACE_SETOPTIONS, pid, 0, (void*) (PTRACE_O_TRACESECCOMP | flags)) == -1) {
		/* No seccomp on the system, try without (this has to succeed) */
		sys_ptrace(PTRACE_SETOPTIONS, pid, 0, (void*) flags);
	}
}
예제 #3
0
파일: sys.c 프로젝트: smillaedler/rr
void goto_next_event(struct context *ctx)
{

	if (ctx->child_sig != 0) {
		printf("sending signal: %d\n",ctx->child_sig);
	}
	sys_ptrace(PTRACE_SYSCALL, ctx->child_tid, 0, (void*) ctx->child_sig);
	sys_waitpid(ctx->child_tid, &ctx->status);

	ctx->child_sig = signal_pending(ctx->status);
	if (ctx->child_sig == SIGTRAP) {
		log_err("Caught unexpected SIGTRAP while going to next event");
		emergency_debug(ctx);
	}
	ctx->event = read_child_orig_eax(ctx->child_tid);
}
예제 #4
0
/* Signal handler to help us recover from dying while we are attached to
 * other threads.
 */
static void SignalHandler(int signum, siginfo_t *si, void *data) {
  if (sig_pids != NULL) {
    if (signum == SIGABRT) {
      while (sig_num_threads-- > 0) {
        /* Not sure if sched_yield is really necessary here, but it does not */
        /* hurt, and it might be necessary for the same reasons that we have */
        /* to do so in sys_ptrace_detach().                                  */
        sys_sched_yield();
        sys_ptrace(PTRACE_KILL, sig_pids[sig_num_threads], 0, 0);
      }
    } else if (sig_num_threads > 0) {
      ResumeAllProcessThreads(sig_num_threads, (int *)sig_pids);
    }
  }
  sig_pids = NULL;
  if (sig_marker >= 0)
    NO_INTR(sys_close(sig_marker));
  sig_marker = -1;
  if (sig_proc >= 0)
    NO_INTR(sys_close(sig_proc));
  sig_proc = -1;

  sys__exit(signum == SIGABRT ? 1 : 2);
}
예제 #5
0
static void ListerThread(struct ListerParams *args) {
  int                found_parent = 0;
  pid_t              clone_pid  = sys_gettid(), ppid = sys_getppid();
  char               proc_self_task[80], marker_name[48], *marker_path;
  const char         *proc_paths[3];
  const char *const  *proc_path = proc_paths;
  int                proc = -1, marker = -1, num_threads = 0;
  int                max_threads = 0, sig;
  struct kernel_stat marker_sb, proc_sb;
  stack_t            altstack;

  /* Create "marker" that we can use to detect threads sharing the same
   * address space and the same file handles. By setting the FD_CLOEXEC flag
   * we minimize the risk of misidentifying child processes as threads;
   * and since there is still a race condition,  we will filter those out
   * later, anyway.
   */
  if ((marker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0 ||
      sys_fcntl(marker, F_SETFD, FD_CLOEXEC) < 0) {
  failure:
    args->result = -1;
    args->err    = errno;
    if (marker >= 0)
      NO_INTR(sys_close(marker));
    sig_marker = marker = -1;
    if (proc >= 0)
      NO_INTR(sys_close(proc));
    sig_proc = proc = -1;
    sys__exit(1);
  }

  /* Compute search paths for finding thread directories in /proc            */
  local_itoa(strrchr(strcpy(proc_self_task, "/proc/"), '\000'), ppid);
  strcpy(marker_name, proc_self_task);
  marker_path = marker_name + strlen(marker_name);
  strcat(proc_self_task, "/task/");
  proc_paths[0] = proc_self_task; /* /proc/$$/task/                          */
  proc_paths[1] = "/proc/";       /* /proc/                                  */
  proc_paths[2] = NULL;

  /* Compute path for marker socket in /proc                                 */
  local_itoa(strcpy(marker_path, "/fd/") + 4, marker);
  if (sys_stat(marker_name, &marker_sb) < 0) {
    goto failure;
  }

  /* Catch signals on an alternate pre-allocated stack. This way, we can
   * safely execute the signal handler even if we ran out of memory.
   */
  memset(&altstack, 0, sizeof(altstack));
  altstack.ss_sp    = args->altstack_mem;
  altstack.ss_flags = 0;
  altstack.ss_size  = ALT_STACKSIZE;
  sys_sigaltstack(&altstack, (const stack_t *)NULL);

  /* Some kernels forget to wake up traced processes, when the
   * tracer dies.  So, intercept synchronous signals and make sure
   * that we wake up our tracees before dying. It is the caller's
   * responsibility to ensure that asynchronous signals do not
   * interfere with this function.
   */
  sig_marker = marker;
  sig_proc   = -1;
  for (sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) {
    struct kernel_sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction_ = SignalHandler;
    sys_sigfillset(&sa.sa_mask);
    sa.sa_flags      = SA_ONSTACK|SA_SIGINFO|SA_RESETHAND;
    sys_sigaction(sync_signals[sig], &sa, (struct kernel_sigaction *)NULL);
  }
  
  /* Read process directories in /proc/...                                   */
  for (;;) {
    /* Some kernels know about threads, and hide them in "/proc"
     * (although they are still there, if you know the process
     * id). Threads are moved into a separate "task" directory. We
     * check there first, and then fall back on the older naming
     * convention if necessary.
     */
    if ((sig_proc = proc = c_open(*proc_path, O_RDONLY|O_DIRECTORY, 0)) < 0) {
      if (*++proc_path != NULL)
        continue;
      goto failure;
    }
    if (sys_fstat(proc, &proc_sb) < 0)
      goto failure;
    
    /* Since we are suspending threads, we cannot call any libc
     * functions that might acquire locks. Most notably, we cannot
     * call malloc(). So, we have to allocate memory on the stack,
     * instead. Since we do not know how much memory we need, we
     * make a best guess. And if we guessed incorrectly we retry on
     * a second iteration (by jumping to "detach_threads").
     *
     * Unless the number of threads is increasing very rapidly, we
     * should never need to do so, though, as our guestimate is very
     * conservative.
     */
    if (max_threads < proc_sb.st_nlink + 100)
      max_threads = proc_sb.st_nlink + 100;
    
    /* scope */ {
      pid_t pids[max_threads];
      int   added_entries = 0;
      sig_num_threads     = num_threads;
      sig_pids            = pids;
      for (;;) {
        struct kernel_dirent *entry;
        char buf[4096];
        ssize_t nbytes = sys_getdents(proc, (struct kernel_dirent *)buf,
                                      sizeof(buf));
        if (nbytes < 0)
          goto failure;
        else if (nbytes == 0) {
          if (added_entries) {
            /* Need to keep iterating over "/proc" in multiple
             * passes until we no longer find any more threads. This
             * algorithm eventually completes, when all threads have
             * been suspended.
             */
            added_entries = 0;
            sys_lseek(proc, 0, SEEK_SET);
            continue;
          }
          break;
        }
        for (entry = (struct kernel_dirent *)buf;
             entry < (struct kernel_dirent *)&buf[nbytes];
             entry = (struct kernel_dirent *)((char *)entry+entry->d_reclen)) {
          if (entry->d_ino != 0) {
            const char *ptr = entry->d_name;
            pid_t pid;
            
            /* Some kernels hide threads by preceding the pid with a '.'     */
            if (*ptr == '.')
              ptr++;
            
            /* If the directory is not numeric, it cannot be a
             * process/thread
             */
            if (*ptr < '0' || *ptr > '9')
              continue;
            pid = local_atoi(ptr);

            /* Attach (and suspend) all threads                              */
            if (pid && pid != clone_pid) {
              struct kernel_stat tmp_sb;
              char fname[entry->d_reclen + 48];
              strcat(strcat(strcpy(fname, "/proc/"),
                            entry->d_name), marker_path);
              
              /* Check if the marker is identical to the one we created      */
              if (sys_stat(fname, &tmp_sb) >= 0 &&
                  marker_sb.st_ino == tmp_sb.st_ino) {
                long i, j;

                /* Found one of our threads, make sure it is no duplicate    */
                for (i = 0; i < num_threads; i++) {
                  /* Linear search is slow, but should not matter much for
                   * the typically small number of threads.
                   */
                  if (pids[i] == pid) {
                    /* Found a duplicate; most likely on second pass         */
                    goto next_entry;
                  }
                }
                
                /* Check whether data structure needs growing                */
                if (num_threads >= max_threads) {
                  /* Back to square one, this time with more memory          */
                  NO_INTR(sys_close(proc));
                  goto detach_threads;
                }

                /* Attaching to thread suspends it                           */
                pids[num_threads++] = pid;
                sig_num_threads     = num_threads;
                if (sys_ptrace(PTRACE_ATTACH, pid, (void *)0,
                               (void *)0) < 0) {
                  /* If operation failed, ignore thread. Maybe it
                   * just died?  There might also be a race
                   * condition with a concurrent core dumper or
                   * with a debugger. In that case, we will just
                   * make a best effort, rather than failing
                   * entirely.
                   */
                  num_threads--;
                  sig_num_threads = num_threads;
                  goto next_entry;
                }
                while (sys_waitpid(pid, (int *)0, __WALL) < 0) {
                  if (errno != EINTR) {
                    sys_ptrace_detach(pid);
                    num_threads--;
                    sig_num_threads = num_threads;
                    goto next_entry;
                  }
                }
                
                if (sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i++ != j ||
                    sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i   != j) {
                  /* Address spaces are distinct, even though both
                   * processes show the "marker". This is probably
                   * a forked child process rather than a thread.
                   */
                  sys_ptrace_detach(pid);
                  num_threads--;
                  sig_num_threads = num_threads;
                } else {
                  found_parent |= pid == ppid;
                  added_entries++;
                }
              }
            }
          }
        next_entry:;
        }
      }
      NO_INTR(sys_close(proc));
      sig_proc = proc = -1;

      /* If we failed to find any threads, try looking somewhere else in
       * /proc. Maybe, threads are reported differently on this system.
       */
      if (num_threads > 1 || !*++proc_path) {
        NO_INTR(sys_close(marker));
        sig_marker = marker = -1;

        /* If we never found the parent process, something is very wrong.
         * Most likely, we are running in debugger. Any attempt to operate
         * on the threads would be very incomplete. Let's just report an
         * error to the caller.
         */
        if (!found_parent) {
          ResumeAllProcessThreads(num_threads, pids);
          sys__exit(3);
        }

        /* Now we are ready to call the callback,
         * which takes care of resuming the threads for us.
         */
        args->result = args->callback(args->parameter, num_threads,
                                      pids, args->ap);
        args->err = errno;

        /* Callback should have resumed threads, but better safe than sorry  */
        if (ResumeAllProcessThreads(num_threads, pids)) {
          /* Callback forgot to resume at least one thread, report error     */
          args->err    = EINVAL;
          args->result = -1;
        }

        sys__exit(0);
      }
    detach_threads:
      /* Resume all threads prior to retrying the operation                  */
      ResumeAllProcessThreads(num_threads, pids);
      sig_pids = NULL;
      num_threads = 0;
      sig_num_threads = num_threads;
      max_threads += 100;
    }
  }
}
예제 #6
0
파일: sys.c 프로젝트: smillaedler/rr
long sys_ptrace_peektext_word(pid_t pid, void* addr)
{
	return sys_ptrace(PTRACE_PEEKTEXT, pid, addr, 0);
}
예제 #7
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_syscall_sig(pid_t pid, int sig)
{
	sys_ptrace(PTRACE_SYSCALL, pid, 0, (void*) sig);
}
예제 #8
0
파일: ipc.c 프로젝트: sanyaade-mobiledev/rr
long int read_child_eip(pid_t child_id)
{
	struct user_regs_struct regs;
	sys_ptrace(PTRACE_GETREGS, child_id, NULL, &regs);
	return regs.eip;
}
예제 #9
0
파일: ipc.c 프로젝트: sanyaade-mobiledev/rr
static void write_child_data_word(pid_t pid, void *addr, void *data)
{
	CHECK_ALIGNMENT(addr);
	sys_ptrace(PTRACE_POKEDATA, pid, addr, data);
}
예제 #10
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_singlestep(pid_t pid)
{
	sys_ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
}
예제 #11
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_getsiginfo(pid_t pid, siginfo_t* sig)
{
	sys_ptrace(PTRACE_GETSIGINFO, pid, 0, sig);
}
예제 #12
0
파일: sys.c 프로젝트: smillaedler/rr
unsigned long sys_ptrace_getmsg(pid_t pid)
{
	unsigned long tmp;
	sys_ptrace(PTRACE_GETEVENTMSG, pid, 0, &tmp);
	return tmp;
}
예제 #13
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_sysemu_sig(pid_t pid, int sig)
{
	sys_ptrace(PTRACE_SYSEMU, pid, 0, (void*)sig);
}
예제 #14
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_sysemu(pid_t pid)
{
	sys_ptrace(PTRACE_SYSEMU, pid, 0, 0);
}
예제 #15
0
파일: ipc.c 프로젝트: sanyaade-mobiledev/rr
void write_child_registers(pid_t pid, struct user_regs_struct *regs)
{
	sys_ptrace(PTRACE_SETREGS, pid, NULL, regs);
}
예제 #16
0
파일: ipc.c 프로젝트: sanyaade-mobiledev/rr
void write_child_code(pid_t pid, void* addr, long code)
{
	CHECK_ALIGNMENT(addr);

	sys_ptrace(PTRACE_POKETEXT, pid, addr, (void*) code);
}
예제 #17
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_singlestep_sig(pid_t pid, int sig)
{
	sys_ptrace(PTRACE_SINGLESTEP, pid, 0, (void*) sig);
}
예제 #18
0
파일: ipc.c 프로젝트: sanyaade-mobiledev/rr
long int get_ret_syscall(int child_id)
{
	struct user_regs_struct regs;
	sys_ptrace(PTRACE_GETREGS, child_id, NULL, &regs);
	return regs.eax;
}
예제 #19
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_traceme()
{
	sys_ptrace(PTRACE_TRACEME, 0, 0, 0);
}
예제 #20
0
파일: ipc.c 프로젝트: sanyaade-mobiledev/rr
long int read_child_orig_eax(int child_id)
{
	struct user_regs_struct regs;
	sys_ptrace(PTRACE_GETREGS, child_id, NULL, &regs);
	return regs.orig_eax;
}
예제 #21
0
파일: sys.c 프로젝트: smillaedler/rr
void sys_ptrace_syscall(pid_t pid)
{
	sys_ptrace(PTRACE_SYSCALL, pid, 0, 0);
}