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