On Mon, 2007-11-12 at 16:30 -0800, Roland McGrath wrote: > [...] > If you do the artificial test using a long sleep in arch_ptrace_stop, > then you can probably produce this by hand with gdb. Have the process > doing raise(SIGCHLD) or some other harmless signal. The traced > process will stop to report the signal to gdb, and then gdb will sit > at the prompt before resuming it (given "handle SIGFOO stop" if not default). > If your sleep is long enough, it won't be hard to get your SIGKILL in there. > Then when gdb is sitting, the traced process may still be sitting too. > But it should have gone away instantly from SIGKILL. I found it extremely difficult to trigger the race condition without the articifial test - arch_ptrace_stop() only sleeps if the user page is not present, but in the common case the register stack backing store will have been quite recently accessed by the process. It should be possible to create a large file, flush the page cache, put the RSE into lazy mode, flush it and map the register stack from that file, so that no memory accesses to the backing store are done before ptrace_stop(), but for the time being I placed an msleep(100) after arch_ptrace_stop(). Anyway, I produced a test case which succeeds when the call to sigkill_pending() is in and fails when it's commented out. I'm attaching it here (the kernel patch to follow). Regards, Petr Tesarik
#include <stdio.h> #include <signal.h> #include <unistd.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <time.h> #define TEST_OK 0 #define TEST_FAILED 1 #define TEST_ABORTED 2 #define INTERACTIVE 1 static int run_debugger(pid_t pid) { struct timespec tm; int stat; int res; printf("Running debugger on pid %ld\n", (long) pid); if (waitpid(pid, &stat, 0) == -1) { perror("waitpid attach"); goto error_out; } if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) { perror("PTRACE_CONT"); goto error_out; } tm.tv_sec = 0; tm.tv_nsec = 10000; nanosleep(&tm, NULL); if (kill(pid, SIGKILL) == -1) { perror("kill SIGKILL"); goto error_out; } #if INTERACTIVE puts("Sent SIGKILL. Press Enter to continue."); getchar(); #endif if (waitpid(pid, &stat, 0) == -1) { perror("waitpid exit"); res = TEST_FAILED; } else if (WIFSIGNALED(stat)) { if (WTERMSIG(stat) != SIGKILL) { fprintf(stderr, "Child terminated by signal %d\n", WTERMSIG(stat)); res = TEST_FAILED; } } else if (WIFSTOPPED(stat)) { fprintf(stderr, "Child notified us about signal %d - FAILED\n", WSTOPSIG(stat)); res = TEST_FAILED; } else if (!WIFEXITED(stat)) { fprintf(stderr, "Child terminated abnormally, stat=0x%x\n", stat); res = TEST_FAILED; } else { fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(stat)); res = TEST_FAILED; if (res < WEXITSTATUS(stat)) res = WEXITSTATUS(stat); } return res; error_out: ptrace(PTRACE_KILL, pid, 0, 0); return TEST_ABORTED; } static int run_child() { /* Attach ourselves */ if (ptrace(PTRACE_TRACEME, 0, 0, 0) == -1) { perror("TRACE_ME"); return TEST_ABORTED; } for (;;) raise(SIGCHLD); } int main() { pid_t pid; if ((pid = fork())) return run_debugger(pid); else return run_child(); }
![]() |