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 /* 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. */
     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 
     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 
    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