• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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. */
do_kill(int pid,int sig)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 
test(int block,int caughtsig,int fatalsig)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 
main()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