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 
handler(int sig)21 static void handler(int sig)
22 {
23 }
24 
install_handler(int sig,void (* sig_handler)(int))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. */
do_kill(int pid,int sig)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 
test(int block,int caughtsig,int fatalsig)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 
main()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