njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 1 | // This tests handling of signals sent from outside the process in the |
| 2 | // following combinations: sync and async signals, caught and uncaught |
| 3 | // signals, and while blocking or not blocking in a syscall. This exercises |
| 4 | // various different paths in Valgrind's signal handling. |
| 5 | // |
| 6 | // It does this by installing signal handlers for one signal S, spawning |
| 7 | // another process P, sending S from P multiple times (all caught), then |
| 8 | // sending another signal from P (not caught). |
| 9 | |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 10 | #include <signal.h> |
| 11 | #include <unistd.h> |
| 12 | #include <sys/wait.h> |
| 13 | #include <stdio.h> |
| 14 | #include <stdlib.h> |
| 15 | #include <string.h> |
| 16 | #include <errno.h> |
| 17 | #include <time.h> |
| 18 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 19 | static const struct timespec bip = { 0, 1000000000 / 5 }; // 0.2 seconds. |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 20 | |
| 21 | static void handler(int sig) |
| 22 | { |
| 23 | } |
| 24 | |
florian | cb30149 | 2015-07-31 06:58:16 +0000 | [diff] [blame] | 25 | static void install_handler(int sig, void (*sig_handler)(int)) |
| 26 | { |
| 27 | struct sigaction sa; |
| 28 | sa.sa_handler = sig_handler; |
| 29 | sigemptyset(&sa.sa_mask); |
| 30 | sa.sa_flags = 0; |
| 31 | sigaction(sig, &sa, 0); |
| 32 | } |
| 33 | |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 34 | /* Kill our child, but use a separate kill command. This is so that |
| 35 | it's running independently of Valgrind, and so is async with |
| 36 | respect to thread scheduling. */ |
| 37 | static void do_kill(int pid, int sig) |
| 38 | { |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 39 | int status; |
| 40 | int killer; |
| 41 | int ret; |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 42 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 43 | killer = vfork(); |
| 44 | if (killer == -1) { |
| 45 | perror("killer/vfork"); |
| 46 | exit(1); |
| 47 | } |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 48 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 49 | // In the child, exec 'kill' in order to send the signal. |
| 50 | if (killer == 0) { |
| 51 | char sigbuf[20]; |
| 52 | char pidbuf[20]; |
| 53 | sprintf(sigbuf, "-%d", sig); |
| 54 | sprintf(pidbuf, "%d", pid); |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 55 | execl("/bin/kill", "kill", sigbuf, pidbuf, (char *) NULL); |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 56 | perror("exec failed"); |
| 57 | exit(1); |
| 58 | } |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 59 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 60 | // In the parent, just wait for the child and then check it ran ok. |
| 61 | do |
| 62 | ret = waitpid(killer, &status, 0); |
| 63 | while (ret == -1 && errno == EINTR); |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 64 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 65 | if (ret != killer) { |
| 66 | perror("kill/waitpid"); |
| 67 | exit(1); |
| 68 | } |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 69 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 70 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
| 71 | fprintf(stderr, "kill %d failed status=%s %d\n", killer, |
| 72 | WIFEXITED(status) ? "exit" : "signal", |
| 73 | WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)); |
| 74 | exit(1); |
| 75 | } |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | static void test(int block, int caughtsig, int fatalsig) |
| 79 | { |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 80 | int pid; |
| 81 | int status; |
| 82 | int i; |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 83 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 84 | fprintf(stderr, "testing: blocking=%d caught=%d fatal=%d... ", |
| 85 | block, caughtsig, fatalsig); |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 86 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 87 | pid = fork(); |
| 88 | if (pid == -1) { |
| 89 | perror("fork"); |
| 90 | exit(1); |
| 91 | } |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 92 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 93 | // In the child, install the signal handler, then wait for the signal to |
| 94 | // arrive: |
| 95 | // - if 'block' is set, wait on a system call; |
| 96 | // - otherwise, wait in client code (by spinning). |
| 97 | // The alarm() calls is so that if something breaks, we don't get stuck. |
| 98 | if (pid == 0) { |
florian | cb30149 | 2015-07-31 06:58:16 +0000 | [diff] [blame] | 99 | install_handler(caughtsig, handler); |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 100 | alarm(10); |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 101 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 102 | for (;;) |
| 103 | if (block) { |
| 104 | pause(); |
| 105 | } |
| 106 | } |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 107 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 108 | // In the parent, send the signals. |
| 109 | nanosleep(&bip, 0); // Wait for child to get going. |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 110 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 111 | for (i = 0; i < 5; i++) { |
| 112 | do_kill(pid, caughtsig); // Should be caught. |
| 113 | nanosleep(&bip, 0); |
| 114 | do_kill(pid, caughtsig); // Ditto. |
| 115 | do_kill(pid, caughtsig); // Ditto. |
| 116 | } |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 117 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 118 | nanosleep(&bip, 0); |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 119 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 120 | do_kill(pid, fatalsig); // Should kill it. |
| 121 | |
| 122 | // Check that the child behaved as expected when it received the signals. |
| 123 | if (waitpid(pid, &status, 0) != pid) { |
| 124 | fprintf(stderr, "FAILED: waitpid failed: %s\n", strerror(errno)); |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 125 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 126 | } else if (!WIFSIGNALED(status) || WTERMSIG(status) != fatalsig) { |
| 127 | fprintf(stderr, "FAILED: child exited with unexpected status %s %d\n", |
| 128 | WIFEXITED(status) ? "exit" : "signal", |
| 129 | WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)); |
| 130 | |
| 131 | } else { |
| 132 | fprintf(stderr, "PASSED\n"); |
| 133 | } |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | int main() |
| 137 | { |
florian | cb30149 | 2015-07-31 06:58:16 +0000 | [diff] [blame] | 138 | /* Restore default behaviour of SIGHUP when forked from cron. */ |
| 139 | install_handler(SIGHUP, SIG_DFL); |
| 140 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 141 | test(/*non-blocked*/0, /* sync*/SIGSEGV, /* sync*/SIGBUS); |
| 142 | test(/*non-blocked*/0, /* sync*/SIGSEGV, /*async*/SIGHUP); |
| 143 | test(/*non-blocked*/0, /*async*/SIGUSR1, /* sync*/SIGBUS); |
| 144 | test(/*non-blocked*/0, /*async*/SIGUSR1, /*async*/SIGHUP); |
| 145 | test(/* blocked*/1, /* sync*/SIGSEGV, /* sync*/SIGBUS); |
| 146 | test(/* blocked*/1, /* sync*/SIGSEGV, /*async*/SIGHUP); |
| 147 | test(/* blocked*/1, /*async*/SIGUSR1, /* sync*/SIGBUS); |
| 148 | test(/* blocked*/1, /*async*/SIGUSR1, /*async*/SIGHUP); |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 149 | |
njn | f96aa84 | 2009-05-01 05:00:34 +0000 | [diff] [blame] | 150 | return 0; |
sewardj | cbdddcf | 2005-03-10 23:23:45 +0000 | [diff] [blame] | 151 | } |