Home | History | Annotate | Download | only in tests
      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 
     21 static void handler(int sig)
     22 {
     23 }
     24 
     25 static 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 
     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)
     38 {
     39    int status;
     40    int killer;
     41    int ret;
     42 
     43    killer = vfork();
     44    if (killer == -1) {
     45       perror("killer/vfork");
     46       exit(1);
     47    }
     48 
     49    // 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);
     55       execl("/bin/kill", "kill", sigbuf, pidbuf, (char *) NULL);
     56       perror("exec failed");
     57       exit(1);
     58    }
     59 
     60    // 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);
     64 
     65    if (ret != killer) {
     66       perror("kill/waitpid");
     67       exit(1);
     68    }
     69 
     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));
     74       exit(1);
     75    }
     76 }
     77 
     78 static void test(int block, int caughtsig, int fatalsig)
     79 {
     80    int pid;
     81    int status;
     82    int i;
     83 
     84    fprintf(stderr, "testing: blocking=%d caught=%d fatal=%d... ",
     85       block, caughtsig, fatalsig);
     86 
     87    pid = fork();
     88    if (pid == -1) {
     89       perror("fork");
     90       exit(1);
     91    }
     92 
     93    // 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) {
     99       install_handler(caughtsig, handler);
    100       alarm(10);
    101 
    102       for (;;)
    103          if (block) {
    104             pause();
    105          }
    106    }
    107 
    108    // In the parent, send the signals.
    109    nanosleep(&bip, 0);           // Wait for child to get going.
    110 
    111    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    }
    117 
    118    nanosleep(&bip, 0);
    119 
    120    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));
    125 
    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));
    130 
    131    } else {
    132       fprintf(stderr, "PASSED\n");
    133    }
    134 }
    135 
    136 int main()
    137 {
    138    /* Restore default behaviour of SIGHUP when forked from cron. */
    139    install_handler(SIGHUP, SIG_DFL);
    140 
    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);
    149 
    150    return 0;
    151 }
    152