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.
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).
19 static const struct timespec bip
= { 0, 1000000000 / 5 }; // 0.2 seconds.
21 static void handler(int sig
)
25 static void install_handler(int sig
, void (*sig_handler
)(int))
28 sa
.sa_handler
= sig_handler
;
29 sigemptyset(&sa
.sa_mask
);
31 sigaction(sig
, &sa
, 0);
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. */
37 static void do_kill(int pid
, int sig
)
45 perror("killer/vfork");
49 // In the child, exec 'kill' in order to send the signal.
53 sprintf(sigbuf
, "-%d", sig
);
54 sprintf(pidbuf
, "%d", pid
);
55 execl("/bin/kill", "kill", sigbuf
, pidbuf
, (char *) NULL
);
56 perror("exec failed");
60 // In the parent, just wait for the child and then check it ran ok.
62 ret
= waitpid(killer
, &status
, 0);
63 while (ret
== -1 && errno
== EINTR
);
66 perror("kill/waitpid");
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
));
78 static void test(int block
, int caughtsig
, int fatalsig
)
84 fprintf(stderr
, "testing: blocking=%d caught=%d fatal=%d... ",
85 block
, caughtsig
, fatalsig
);
93 // In the child, install the signal handler, then wait for the signal to
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.
99 install_handler(caughtsig
, handler
);
108 // In the parent, send the signals.
109 nanosleep(&bip
, 0); // Wait for child to get going.
111 for (i
= 0; i
< 5; i
++) {
112 do_kill(pid
, caughtsig
); // Should be caught.
114 do_kill(pid
, caughtsig
); // Ditto.
115 do_kill(pid
, caughtsig
); // Ditto.
120 do_kill(pid
, fatalsig
); // Should kill it.
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
));
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
));
132 fprintf(stderr
, "PASSED\n");
138 /* Restore default behaviour of SIGHUP when forked from cron. */
139 install_handler(SIGHUP
, SIG_DFL
);
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
);