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
19static const struct timespec bip = { 0, 1000000000 / 5 };   // 0.2 seconds.
20
21static 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. */
28static 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
69static 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
127int 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