blob: f05eb6e4ebda2c2dee867950a424732c63e72f73 [file] [log] [blame]
njnf96aa842009-05-01 05:00:34 +00001// 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
sewardjcbdddcf2005-03-10 23:23:45 +000010#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
njnf96aa842009-05-01 05:00:34 +000019static const struct timespec bip = { 0, 1000000000 / 5 }; // 0.2 seconds.
sewardjcbdddcf2005-03-10 23:23:45 +000020
21static void handler(int sig)
22{
23}
24
floriancb301492015-07-31 06:58:16 +000025static 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
sewardjcbdddcf2005-03-10 23:23:45 +000034/* 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. */
37static void do_kill(int pid, int sig)
38{
njnf96aa842009-05-01 05:00:34 +000039 int status;
40 int killer;
41 int ret;
sewardjcbdddcf2005-03-10 23:23:45 +000042
njnf96aa842009-05-01 05:00:34 +000043 killer = vfork();
44 if (killer == -1) {
45 perror("killer/vfork");
46 exit(1);
47 }
sewardjcbdddcf2005-03-10 23:23:45 +000048
njnf96aa842009-05-01 05:00:34 +000049 // 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);
sewardj8eb8bab2015-07-21 14:44:28 +000055 execl("/bin/kill", "kill", sigbuf, pidbuf, (char *) NULL);
njnf96aa842009-05-01 05:00:34 +000056 perror("exec failed");
57 exit(1);
58 }
sewardjcbdddcf2005-03-10 23:23:45 +000059
njnf96aa842009-05-01 05:00:34 +000060 // 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);
sewardjcbdddcf2005-03-10 23:23:45 +000064
njnf96aa842009-05-01 05:00:34 +000065 if (ret != killer) {
66 perror("kill/waitpid");
67 exit(1);
68 }
sewardjcbdddcf2005-03-10 23:23:45 +000069
njnf96aa842009-05-01 05:00:34 +000070 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 }
sewardjcbdddcf2005-03-10 23:23:45 +000076}
77
78static void test(int block, int caughtsig, int fatalsig)
79{
njnf96aa842009-05-01 05:00:34 +000080 int pid;
81 int status;
82 int i;
sewardjcbdddcf2005-03-10 23:23:45 +000083
njnf96aa842009-05-01 05:00:34 +000084 fprintf(stderr, "testing: blocking=%d caught=%d fatal=%d... ",
85 block, caughtsig, fatalsig);
sewardjcbdddcf2005-03-10 23:23:45 +000086
njnf96aa842009-05-01 05:00:34 +000087 pid = fork();
88 if (pid == -1) {
89 perror("fork");
90 exit(1);
91 }
sewardjcbdddcf2005-03-10 23:23:45 +000092
njnf96aa842009-05-01 05:00:34 +000093 // 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) {
floriancb301492015-07-31 06:58:16 +000099 install_handler(caughtsig, handler);
njnf96aa842009-05-01 05:00:34 +0000100 alarm(10);
sewardjcbdddcf2005-03-10 23:23:45 +0000101
njnf96aa842009-05-01 05:00:34 +0000102 for (;;)
103 if (block) {
104 pause();
105 }
106 }
sewardjcbdddcf2005-03-10 23:23:45 +0000107
njnf96aa842009-05-01 05:00:34 +0000108 // In the parent, send the signals.
109 nanosleep(&bip, 0); // Wait for child to get going.
sewardjcbdddcf2005-03-10 23:23:45 +0000110
njnf96aa842009-05-01 05:00:34 +0000111 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 }
sewardjcbdddcf2005-03-10 23:23:45 +0000117
njnf96aa842009-05-01 05:00:34 +0000118 nanosleep(&bip, 0);
sewardjcbdddcf2005-03-10 23:23:45 +0000119
njnf96aa842009-05-01 05:00:34 +0000120 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));
sewardjcbdddcf2005-03-10 23:23:45 +0000125
njnf96aa842009-05-01 05:00:34 +0000126 } 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 }
sewardjcbdddcf2005-03-10 23:23:45 +0000134}
135
136int main()
137{
floriancb301492015-07-31 06:58:16 +0000138 /* Restore default behaviour of SIGHUP when forked from cron. */
139 install_handler(SIGHUP, SIG_DFL);
140
njnf96aa842009-05-01 05:00:34 +0000141 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);
sewardjcbdddcf2005-03-10 23:23:45 +0000149
njnf96aa842009-05-01 05:00:34 +0000150 return 0;
sewardjcbdddcf2005-03-10 23:23:45 +0000151}