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 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 19 static const struct timespec bip = { 0, 1000000000 / 5 }; // 0.2 seconds. 20 21 static void handler(int sig) 22 { 23 } 24 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 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 { 39 int status; 40 int killer; 41 int ret; 42 43 killer = vfork(); 44 if (killer == -1) { 45 perror("killer/vfork"); 46 exit(1); 47 } 48 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); 55 execl("/bin/kill", "kill", sigbuf, pidbuf, (char *) NULL); 56 perror("exec failed"); 57 exit(1); 58 } 59 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); 64 65 if (ret != killer) { 66 perror("kill/waitpid"); 67 exit(1); 68 } 69 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 } 76 } 77 78 static void test(int block, int caughtsig, int fatalsig) 79 { 80 int pid; 81 int status; 82 int i; 83 84 fprintf(stderr, "testing: blocking=%d caught=%d fatal=%d... ", 85 block, caughtsig, fatalsig); 86 87 pid = fork(); 88 if (pid == -1) { 89 perror("fork"); 90 exit(1); 91 } 92 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) { 99 install_handler(caughtsig, handler); 100 alarm(10); 101 102 for (;;) 103 if (block) { 104 pause(); 105 } 106 } 107 108 // In the parent, send the signals. 109 nanosleep(&bip, 0); // Wait for child to get going. 110 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 } 117 118 nanosleep(&bip, 0); 119 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)); 125 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 } 134 } 135 136 int main() 137 { 138 /* Restore default behaviour of SIGHUP when forked from cron. */ 139 install_handler(SIGHUP, SIG_DFL); 140 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); 149 150 return 0; 151 } 152