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 /* Kill our child, but use a separate kill command. This is so that 26 it's running independently of Valgrind, and so is async with 27 respect to thread scheduling. */ 28 static void do_kill(int pid, int sig) 29 { 30 int status; 31 int killer; 32 int ret; 33 34 killer = vfork(); 35 if (killer == -1) { 36 perror("killer/vfork"); 37 exit(1); 38 } 39 40 // In the child, exec 'kill' in order to send the signal. 41 if (killer == 0) { 42 char sigbuf[20]; 43 char pidbuf[20]; 44 sprintf(sigbuf, "-%d", sig); 45 sprintf(pidbuf, "%d", pid); 46 execl("/bin/kill", "kill", sigbuf, pidbuf, NULL); 47 perror("exec failed"); 48 exit(1); 49 } 50 51 // In the parent, just wait for the child and then check it ran ok. 52 do 53 ret = waitpid(killer, &status, 0); 54 while (ret == -1 && errno == EINTR); 55 56 if (ret != killer) { 57 perror("kill/waitpid"); 58 exit(1); 59 } 60 61 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 62 fprintf(stderr, "kill %d failed status=%s %d\n", killer, 63 WIFEXITED(status) ? "exit" : "signal", 64 WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)); 65 exit(1); 66 } 67 } 68 69 static void test(int block, int caughtsig, int fatalsig) 70 { 71 int pid; 72 int status; 73 int i; 74 75 fprintf(stderr, "testing: blocking=%d caught=%d fatal=%d... ", 76 block, caughtsig, fatalsig); 77 78 pid = fork(); 79 if (pid == -1) { 80 perror("fork"); 81 exit(1); 82 } 83 84 // In the child, install the signal handler, then wait for the signal to 85 // arrive: 86 // - if 'block' is set, wait on a system call; 87 // - otherwise, wait in client code (by spinning). 88 // The alarm() calls is so that if something breaks, we don't get stuck. 89 if (pid == 0) { 90 signal(caughtsig, handler); 91 alarm(10); 92 93 for (;;) 94 if (block) { 95 pause(); 96 } 97 } 98 99 // In the parent, send the signals. 100 nanosleep(&bip, 0); // Wait for child to get going. 101 102 for (i = 0; i < 5; i++) { 103 do_kill(pid, caughtsig); // Should be caught. 104 nanosleep(&bip, 0); 105 do_kill(pid, caughtsig); // Ditto. 106 do_kill(pid, caughtsig); // Ditto. 107 } 108 109 nanosleep(&bip, 0); 110 111 do_kill(pid, fatalsig); // Should kill it. 112 113 // Check that the child behaved as expected when it received the signals. 114 if (waitpid(pid, &status, 0) != pid) { 115 fprintf(stderr, "FAILED: waitpid failed: %s\n", strerror(errno)); 116 117 } else if (!WIFSIGNALED(status) || WTERMSIG(status) != fatalsig) { 118 fprintf(stderr, "FAILED: child exited with unexpected status %s %d\n", 119 WIFEXITED(status) ? "exit" : "signal", 120 WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)); 121 122 } else { 123 fprintf(stderr, "PASSED\n"); 124 } 125 } 126 127 int main() 128 { 129 test(/*non-blocked*/0, /* sync*/SIGSEGV, /* sync*/SIGBUS); 130 test(/*non-blocked*/0, /* sync*/SIGSEGV, /*async*/SIGHUP); 131 test(/*non-blocked*/0, /*async*/SIGUSR1, /* sync*/SIGBUS); 132 test(/*non-blocked*/0, /*async*/SIGUSR1, /*async*/SIGHUP); 133 test(/* blocked*/1, /* sync*/SIGSEGV, /* sync*/SIGBUS); 134 test(/* blocked*/1, /* sync*/SIGSEGV, /*async*/SIGHUP); 135 test(/* blocked*/1, /*async*/SIGUSR1, /* sync*/SIGBUS); 136 test(/* blocked*/1, /*async*/SIGUSR1, /*async*/SIGHUP); 137 138 return 0; 139 } 140