On Sun, 21 Nov 2004, Linus Torvalds wrote:
Ok, how about this patch?
It does basically two things:
it makes the x86 version of ptrace be a lot more careful about the TF bit in eflags, and in particular it never touches it _unless_ the tracer has explicitly asked for it (ie we set TF only when doing a PTRACE_SINGESTEP, and we clear it only when it has been set by us, not if it has been set by the program itself).
This patch also cleans up the codepaths by doing all the common stuff in set_singlestep()/clear_singlestep().
It clarifies signal handling, and makes it clear that we always push the full eflags onto the signal stack, _except_ if the TF bit was set by an external ptrace user, in which case we hide it so that the tracee doesn't see it when it looks at its stack contents.
It also adds a few comments, and makes it clear that the signal handler itself is always set up with TF _clear_. But if we were single-stepped into it, we will have notified the debugger, so the debugger obviously can (and often will) decide to continue single-stepping.
Looks like a nice cleanup. What does the test program below print for you?
- Davide
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <linux/user.h> #include <linux/unistd.h>
int main(int ac, char **av) { int i, status, res; long start, end; pid_t cpid, pid; struct user_regs_struct ur; struct sigaction sa;
sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_DFL; sigaction(SIGCHLD, &sa, NULL);
printf("nargs=%d\n", ac); if (ac == 1) goto tracer;
printf("arg=%s\n", av[1]); loop: __asm__ volatile ("int $0x80" : "=a" (res) : "0" (__NR_getpid)); goto loop; endloop: exit(0);
tracer: if ((cpid = fork()) != 0) goto parent;
printf("child=%d\n", getpid()); ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl(av[0], av[0], "child", NULL);
exit(0);
parent: start = (long) &&loop; end = (long) &&endloop;
printf("pchild=%d\n", cpid);
for (;;) { pid = wait(&status); if (pid != cpid) continue; res = WSTOPSIG(status); if (ptrace(PTRACE_GETREGS, pid, NULL, &ur)) { printf("[%d] error: ptrace(PTRACE_GETREGS, %d)\n", pid, pid); return 1; }
if (ptrace(PTRACE_SINGLESTEP, pid, NULL, res != SIGTRAP ? res: 0)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; }
if (ur.eip >= start && ur.eip <= end) break; }
for (i = 0; i < 15; i++) { printf("waiting ...\n"); pid = wait(&status); printf("done: pid=%d status=%d\n", pid, status); if (pid != cpid) continue; res = WSTOPSIG(status); printf("sig=%d\n", res); if (ptrace(PTRACE_GETREGS, pid, NULL, &ur)) { printf("[%d] error: ptrace(PTRACE_GETREGS, %d)\n", pid, pid); return 1; }
printf("EIP=0x%08x\n", ur.eip);
if (ptrace(PTRACE_SINGLESTEP, pid, NULL, res != SIGTRAP ? res: 0)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; } }
if (ptrace(PTRACE_CONT, cpid, NULL, SIGKILL)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; }
return 0; }