1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
// This tests handling of signals sent from outside the process in the
// following combinations: sync and async signals, caught and uncaught
// signals, and while blocking or not blocking in a syscall. This exercises
// various different paths in Valgrind's signal handling.
//
// It does this by installing signal handlers for one signal S, spawning
// another process P, sending S from P multiple times (all caught), then
// sending another signal from P (not caught).
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
static const struct timespec bip = { 0, 1000000000 / 5 }; // 0.2 seconds.
static void handler(int sig)
{
}
static void install_handler(int sig, void (*sig_handler)(int))
{
struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(sig, &sa, 0);
}
/* Kill our child, but use a separate kill command. This is so that
it's running independently of Valgrind, and so is async with
respect to thread scheduling. */
static void do_kill(int pid, int sig)
{
int status;
int killer;
int ret;
killer = vfork();
if (killer == -1) {
perror("killer/vfork");
exit(1);
}
// In the child, exec 'kill' in order to send the signal.
if (killer == 0) {
char sigbuf[20];
char pidbuf[20];
sprintf(sigbuf, "-%d", sig);
sprintf(pidbuf, "%d", pid);
execl("/bin/kill", "kill", sigbuf, pidbuf, (char *) NULL);
perror("exec failed");
exit(1);
}
// In the parent, just wait for the child and then check it ran ok.
do
ret = waitpid(killer, &status, 0);
while (ret == -1 && errno == EINTR);
if (ret != killer) {
perror("kill/waitpid");
exit(1);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
fprintf(stderr, "kill %d failed status=%s %d\n", killer,
WIFEXITED(status) ? "exit" : "signal",
WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status));
exit(1);
}
}
static void test(int block, int caughtsig, int fatalsig)
{
int pid;
int status;
int i;
fprintf(stderr, "testing: blocking=%d caught=%d fatal=%d... ",
block, caughtsig, fatalsig);
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
// In the child, install the signal handler, then wait for the signal to
// arrive:
// - if 'block' is set, wait on a system call;
// - otherwise, wait in client code (by spinning).
// The alarm() calls is so that if something breaks, we don't get stuck.
if (pid == 0) {
install_handler(caughtsig, handler);
alarm(10);
for (;;)
if (block) {
pause();
}
}
// In the parent, send the signals.
nanosleep(&bip, 0); // Wait for child to get going.
for (i = 0; i < 5; i++) {
do_kill(pid, caughtsig); // Should be caught.
nanosleep(&bip, 0);
do_kill(pid, caughtsig); // Ditto.
do_kill(pid, caughtsig); // Ditto.
}
nanosleep(&bip, 0);
do_kill(pid, fatalsig); // Should kill it.
// Check that the child behaved as expected when it received the signals.
if (waitpid(pid, &status, 0) != pid) {
fprintf(stderr, "FAILED: waitpid failed: %s\n", strerror(errno));
} else if (!WIFSIGNALED(status) || WTERMSIG(status) != fatalsig) {
fprintf(stderr, "FAILED: child exited with unexpected status %s %d\n",
WIFEXITED(status) ? "exit" : "signal",
WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status));
} else {
fprintf(stderr, "PASSED\n");
}
}
int main()
{
/* Restore default behaviour of SIGHUP when forked from cron. */
install_handler(SIGHUP, SIG_DFL);
test(/*non-blocked*/0, /* sync*/SIGSEGV, /* sync*/SIGBUS);
test(/*non-blocked*/0, /* sync*/SIGSEGV, /*async*/SIGHUP);
test(/*non-blocked*/0, /*async*/SIGUSR1, /* sync*/SIGBUS);
test(/*non-blocked*/0, /*async*/SIGUSR1, /*async*/SIGHUP);
test(/* blocked*/1, /* sync*/SIGSEGV, /* sync*/SIGBUS);
test(/* blocked*/1, /* sync*/SIGSEGV, /*async*/SIGHUP);
test(/* blocked*/1, /*async*/SIGUSR1, /* sync*/SIGBUS);
test(/* blocked*/1, /*async*/SIGUSR1, /*async*/SIGHUP);
return 0;
}
|