diff options
| author | Dmitry V. Levin <ldv@altlinux.org> | 2011-08-16 18:57:29 +0000 |
|---|---|---|
| committer | Dmitry V. Levin <ldv@altlinux.org> | 2011-08-17 09:59:14 +0000 |
| commit | 4e39e26fe247270bac77db141e4c7080c04da6b9 (patch) | |
| tree | 1bfde42538f3fe8297ba9c03e5ed23f983f4742e | |
| parent | 4ec9626092913b8596812d422750141477f19e63 (diff) | |
| download | strace-ldv/PTRACE_SETOPTIONS-tests.tar.gz | |
Fix PTRACE_SETOPTIONS testsldv/PTRACE_SETOPTIONS-tests
* strace.c [LINUX] (kill_save_errno): New function.
(test_ptrace_setoptions_followfork): Change return type to void.
Fix and harden error handling. Use kill_save_errno() to avoid errno
clobbering. Treat EIO from ptrace() the same way as EINVAL.
(test_ptrace_setoptions_for_all): Use kill_save_errno() to avoid errno
clobbering. Treat EIO from ptrace() the same way as EINVAL.
(main): Update use of test_ptrace_setoptions_followfork().
| -rw-r--r-- | strace.c | 159 |
1 files changed, 100 insertions, 59 deletions
@@ -716,12 +716,20 @@ startup_child(char **argv) } #ifdef LINUX +static void kill_save_errno(pid_t pid, int sig) +{ + int saved_errno = errno; + + (void) kill(pid, sig); + errno = saved_errno; +} + /* * Test whether the kernel support PTRACE_O_TRACECLONE et al options. * First fork a new child, call ptrace with PTRACE_SETOPTIONS on it, * and then see which options are supported by the kernel. */ -static int +static void test_ptrace_setoptions_followfork(void) { int pid, expected_grandchild = 0, found_grandchild = 0; @@ -730,59 +738,93 @@ test_ptrace_setoptions_followfork(void) PTRACE_O_TRACEVFORK; if ((pid = fork()) < 0) - return -1; + perror_msg_and_die("fork"); else if (pid == 0) { - if (ptrace(PTRACE_TRACEME, 0, (char *)1, 0) < 0) - _exit(1); - kill(getpid(), SIGSTOP); - _exit(fork() < 0); + pid = getpid(); + if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) + perror_msg_and_die("%s: PTRACE_TRACEME doesn't work", + __func__); + kill(pid, SIGSTOP); + if (fork() < 0) + perror_msg_and_die("fork"); + _exit(0); } while (1) { int status, tracee_pid; + errno = 0; tracee_pid = wait(&status); - if (tracee_pid == -1) { + if (tracee_pid <= 0) { if (errno == EINTR) continue; else if (errno == ECHILD) break; - perror("test_ptrace_setoptions_followfork"); - return -1; + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("%s: unexpected wait result %d", + __func__, tracee_pid); + } + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + if (tracee_pid != pid) + kill_save_errno(pid, SIGKILL); + error_msg_and_die("%s: unexpected exit status %u", + __func__, WEXITSTATUS(status)); + } + continue; + } + if (WIFSIGNALED(status)) { + if (tracee_pid != pid) + kill_save_errno(pid, SIGKILL); + error_msg_and_die("%s: unexpected signal %u", + __func__, WTERMSIG(status)); + } + if (!WIFSTOPPED(status)) { + if (tracee_pid != pid) + kill_save_errno(tracee_pid, SIGKILL); + kill(pid, SIGKILL); + error_msg_and_die("%s: unexpected wait status %x", + __func__, status); } if (tracee_pid != pid) { found_grandchild = tracee_pid; - if (ptrace(PTRACE_CONT, tracee_pid, 0, 0) < 0 && - errno != ESRCH) - kill(tracee_pid, SIGKILL); - } - else if (WIFSTOPPED(status)) { - switch (WSTOPSIG(status)) { - case SIGSTOP: - if (ptrace(PTRACE_SETOPTIONS, pid, - NULL, test_options) < 0) { - kill(pid, SIGKILL); - return -1; - } - break; - case SIGTRAP: - if (status >> 16 == PTRACE_EVENT_FORK) { - long msg = 0; + if (ptrace(PTRACE_CONT, tracee_pid, 0, 0) < 0) { + kill_save_errno(tracee_pid, SIGKILL); + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("PTRACE_CONT doesn't work"); + } + continue; + } + switch (WSTOPSIG(status)) { + case SIGSTOP: + if (ptrace(PTRACE_SETOPTIONS, pid, 0, test_options) < 0 + && errno != EINVAL && errno != EIO) + perror_msg("PTRACE_SETOPTIONS"); + break; + case SIGTRAP: + if (status >> 16 == PTRACE_EVENT_FORK) { + long msg = 0; - if (ptrace(PTRACE_GETEVENTMSG, pid, - NULL, (long) &msg) == 0) - expected_grandchild = msg; - } - break; + if (ptrace(PTRACE_GETEVENTMSG, pid, + NULL, (long) &msg) == 0) + expected_grandchild = msg; } - if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0 && - errno != ESRCH) - kill(pid, SIGKILL); + break; + } + if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) { + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("PTRACE_SYSCALL doesn't work"); } } - if (expected_grandchild && expected_grandchild == found_grandchild) + if (expected_grandchild && expected_grandchild == found_grandchild) { ptrace_setoptions |= test_options; - return 0; + if (debug) + fprintf(stderr, "ptrace_setoptions = %#x\n", + ptrace_setoptions); + return; + } + error_msg("Test for PTRACE_O_TRACECLONE failed, " + "giving up using this feature."); } /* @@ -802,7 +844,8 @@ test_ptrace_setoptions_followfork(void) static void test_ptrace_setoptions_for_all(void) { - const unsigned int test_options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC; + const unsigned int test_options = PTRACE_O_TRACESYSGOOD | + PTRACE_O_TRACEEXEC; int pid; int it_worked = 0; @@ -814,7 +857,8 @@ test_ptrace_setoptions_for_all(void) pid = getpid(); if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0) /* Note: exits with exitcode 1 */ - perror_msg_and_die("%s: PTRACE_TRACEME doesn't work", __func__); + perror_msg_and_die("%s: PTRACE_TRACEME doesn't work", + __func__); kill(pid, SIGSTOP); _exit(0); /* parent should see entry into this syscall */ } @@ -827,18 +871,24 @@ test_ptrace_setoptions_for_all(void) if (tracee_pid <= 0) { if (errno == EINTR) continue; - kill(pid, SIGKILL); - perror_msg_and_die("%s: unexpected wait result %d", __func__, tracee_pid); + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("%s: unexpected wait result %d", + __func__, tracee_pid); } if (WIFEXITED(status)) { if (WEXITSTATUS(status) == 0) break; - /* PTRACE_TRACEME failed in child. This is fatal. */ - exit(1); + error_msg_and_die("%s: unexpected exit status %u", + __func__, WEXITSTATUS(status)); + } + if (WIFSIGNALED(status)) { + error_msg_and_die("%s: unexpected signal %u", + __func__, WTERMSIG(status)); } if (!WIFSTOPPED(status)) { kill(pid, SIGKILL); - error_msg_and_die("%s: unexpected wait status %x", __func__, status); + error_msg_and_die("%s: unexpected wait status %x", + __func__, status); } if (WSTOPSIG(status) == SIGSTOP) { /* @@ -847,15 +897,15 @@ test_ptrace_setoptions_for_all(void) * and thus will decide to not use the option. * IOW: the outcome of the test will be correct. */ - if (ptrace(PTRACE_SETOPTIONS, pid, 0L, test_options) < 0) - if (errno != EINVAL) - perror_msg("PTRACE_SETOPTIONS"); + if (ptrace(PTRACE_SETOPTIONS, pid, 0L, test_options) < 0 + && errno != EINVAL && errno != EIO) + perror_msg("PTRACE_SETOPTIONS"); } if (WSTOPSIG(status) == (SIGTRAP | 0x80)) { it_worked = 1; } if (ptrace(PTRACE_SYSCALL, pid, 0L, 0L) < 0) { - kill(pid, SIGKILL); + kill_save_errno(pid, SIGKILL); perror_msg_and_die("PTRACE_SYSCALL doesn't work"); } } @@ -869,8 +919,8 @@ test_ptrace_setoptions_for_all(void) return; } - fprintf(stderr, - "Test for PTRACE_O_TRACESYSGOOD failed, giving up using this feature.\n"); + error_msg("Test for PTRACE_O_TRACESYSGOOD failed, " + "giving up using this feature."); } #endif @@ -1060,17 +1110,8 @@ main(int argc, char *argv[]) } #ifdef LINUX - if (followfork) { - if (test_ptrace_setoptions_followfork() < 0) { - fprintf(stderr, - "Test for options supported by PTRACE_SETOPTIONS " - "failed, giving up using this feature.\n"); - ptrace_setoptions = 0; - } - if (debug) - fprintf(stderr, "ptrace_setoptions = %#x\n", - ptrace_setoptions); - } + if (followfork) + test_ptrace_setoptions_followfork(); test_ptrace_setoptions_for_all(); #endif |
