Permalink
Browse files

Clean up ptrace infrastructure slightly.

  • Loading branch information...
1 parent 43b260e commit 1fb6ce8133a1d02569184ff00158e3ff43bbaf0e @nelhage committed Jan 17, 2011
Showing with 112 additions and 46 deletions.
  1. +112 −46 ptrace.c
View
@@ -23,38 +23,117 @@
#define offsetof(a, b) __builtin_offsetof(a,b)
-void attach_child(pid_t pid) {
+enum child_state {
+ ptrace_detached = 0,
+ ptrace_at_syscall,
+ ptrace_after_syscall,
+ ptrace_running,
+ ptrace_stopped,
+ ptrace_exited
+};
+
+struct ptrace_child {
+ pid_t pid;
+ struct user user;
+ enum child_state state;
int status;
+};
- if (ptrace(PTRACE_ATTACH, pid) < 0) {
- perror("Unable to attach");
- exit(1);
- }
+int ptrace_wait(struct ptrace_child *child);
- if (waitpid(pid, NULL, 0) < 0) {
- perror("Waiting on child");
- exit(1);
- }
+int ptrace_attach_child(struct ptrace_child *child, pid_t pid) {
+ int err = 0;
+
+ memset(child, 0, sizeof child);
+ child->pid = pid;
+
+ if ((err = ptrace(PTRACE_ATTACH, pid)) < 0)
+ return err;
- ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD);
- while (1) {
- ptrace(PTRACE_SYSCALL, pid, 0, 0);
- if (waitpid(pid, &status, 0) < 0) {
- perror("Waiting on child");
+ ptrace_wait(child);
+
+ if ((err = ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD)) < 0)
+ goto detach;
+
+ return err;
+
+ detach:
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+ return err;
+}
+
+int ptrace_detach_child(struct ptrace_child *child) {
+ int err;
+ err = ptrace(PTRACE_DETACH, child->pid, 0, 0);
+ if (!err)
+ child->state = ptrace_detached;
+ return err;
+}
+
+int ptrace_wait(struct ptrace_child *child) {
+ int err;
+ if ((err = waitpid(child->pid, &child->status, 0)) < 0)
+ return err;
+ if (WIFEXITED(child->status) || WIFSIGNALED(child->status)) {
+ child->state = ptrace_exited;
+ } else if(WIFSTOPPED(child->status)) {
+ if (WSTOPSIG(child->status) & 0x80) {
+ child->state = (child->state == ptrace_at_syscall) ?
+ ptrace_after_syscall : ptrace_at_syscall;
+ } else {
+ child->state = ptrace_stopped;
}
- if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int ptrace_advance_to_state(struct ptrace_child *child,
+ enum child_state desired) {
+ int err;
+ while(child->state != desired) {
+ switch(desired) {
+ case ptrace_at_syscall:
+ case ptrace_after_syscall:
+ err = ptrace(PTRACE_SYSCALL, child->pid, 0, 0);
+ break;
+ case ptrace_running:
+ return ptrace(PTRACE_CONT, child->pid, 0, 0);
+ case ptrace_stopped:
+ err = kill(child->pid, SIGSTOP);
break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ if (err < 0)
+ return err;
+ if((err = ptrace_wait(child)) < 0)
+ return err;
}
+ return 0;
}
-unsigned long remote_syscall(pid_t pid, unsigned long sysno,
- unsigned long p0, unsigned long p1,
- unsigned long p2, unsigned long p3,
- unsigned long p4, unsigned long p5) {
- int status;
+int ptrace_save_regs(struct ptrace_child *child) {
+ return ptrace(PTRACE_GETREGS, child->pid, 0, &child->user);
+}
+
+int ptrace_restore_regs(struct ptrace_child *child) {
+ return ptrace(PTRACE_SETREGS, child->pid, 0, &child->user);
+}
+
+unsigned long ptrace_remote_syscall(struct ptrace_child *child,
+ unsigned long sysno,
+ unsigned long p0, unsigned long p1,
+ unsigned long p2, unsigned long p3,
+ unsigned long p4, unsigned long p5) {
+ assert(!ptrace_advance_to_state(child, ptrace_at_syscall));
#define setreg(r, v) do { \
- if (ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, regs.r), \
+ if (ptrace(PTRACE_POKEUSER, child->pid, \
+ offsetof(struct user, regs.r), \
(v)) < 0) { \
perror(#r); \
exit(1); \
@@ -68,12 +147,11 @@ unsigned long remote_syscall(pid_t pid, unsigned long sysno,
setreg(syscall_arg3, p3);
setreg(syscall_arg4, p4);
setreg(syscall_arg5, p5);
+#undef setreg
- ptrace(PTRACE_SYSCALL, pid, 0, 0);
- waitpid(pid, &status, 0);
- assert(WIFSTOPPED(status));
+ assert(!ptrace_advance_to_state(child, ptrace_after_syscall));
- return ptrace(PTRACE_PEEKUSER, pid, offsetof(struct user, regs.reg_ax));
+ return ptrace(PTRACE_PEEKUSER, child->pid, offsetof(struct user, regs.reg_ax));
}
void reset_user_struct(struct user *user) {
@@ -82,37 +160,25 @@ void reset_user_struct(struct user *user) {
}
int main(int argc, char **argv) {
+ struct ptrace_child child;
pid_t pid;
- struct user regs;
if (argc < 2) {
printf("Usage: %s pid\n", argv[0]);
return 1;
}
pid = atoi(argv[1]);
- attach_child(pid);
-
- if (ptrace(PTRACE_GETREGS, pid, 0, &regs) < 0) {
- perror("getregs");
- return 1;
- }
-
- printf("mmap = %lx\n", remote_syscall(pid, mmap_syscall, 0,
- 4096, PROT_READ|PROT_WRITE,
- MAP_ANONYMOUS|MAP_PRIVATE, 0, 0));
-
- reset_user_struct(&regs);
+ assert(!ptrace_attach_child(&child, pid));
+ assert(!ptrace_save_regs(&child));
- if (ptrace(PTRACE_SETREGS, pid, 0, &regs) < 0) {
- perror("setregs");
- return 1;
- }
+ printf("mmap = %lx\n", ptrace_remote_syscall(&child, mmap_syscall, 0,
+ 4096, PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, 0, 0));
- if (ptrace(PTRACE_DETACH, pid, 0, 0) < 0) {
- perror("Unable to detach");
- return 1;
- }
+ reset_user_struct(&child.user);
+ assert(!ptrace_restore_regs(&child));
+ assert(!ptrace_detach_child(&child));
return 0;
}

0 comments on commit 1fb6ce8

Please sign in to comment.