/* * Eygene Ryabinkin, rea@FreeBSD.org. 2014. * * Tests if signal mask is correctly restored * after signal handler operation. * * We currently test the following mask types: * - empty mask; * - mask with any single bit is set; * - mask with any two bits set; * - inverted counterparts of the above. * * Test for all possible masks will take 2^N tries * where N is total number of signals, so we're not * going to do that (N is currently at least 33, so * it will take a loooong time). */ #include #include #include #include #include #include #include #include #include #include #if 0 #define CHATTY #endif #define SIGMAX SIGLIBRT volatile static int got_signal; /* * Signal handler. */ static void sig_handler(int sig, siginfo_t *info, void *junk) { got_signal = 1; } /* * Tests if we must skip the given signal. */ static int shall_skip(int sig) { return (int)(sig == SIGKILL || sig == SIGSTOP); } /* * Disarms signals to be skipped in the signal mask. */ static void mask_skipped(sigset_t *mask) { sigdelset(mask, SIGKILL); sigdelset(mask, SIGSTOP); } /* * Returns number of signal masks we will test. */ static size_t masks_count(void) { int sig; size_t nsig; for (nsig = 0, sig = 1; sig <= SIGMAX; sig++) if (!shall_skip(sig)) nsig++; assert(nsig > 2); return 2*(nsig + nsig*(nsig - 1)/2 + nsig*(nsig - 1)*(nsig - 2)/6); } /* * Generates signal masks. */ static void masks_generate(sigset_t *masks) { size_t n = 0; int s1, s2, s3; sigset_t *m; assert(masks != NULL); /* Single-bit masks and inversions */ #ifdef CHATTY printf ("Preparing single-bit masks...\n"); #endif for (s1 = 1; s1 <= SIGMAX; s1++) { if (shall_skip(s1)) continue; m = masks + n; sigemptyset(m); sigaddset(m, s1); n++; m++; sigfillset(m); mask_skipped(m); sigdelset(m, s1); n++; } /* Double-bit masks and inversions */ #ifdef CHATTY printf ("Preparing double-bit masks...\n"); #endif for (s1 = 1; s1 <= SIGMAX; s1++) { if (shall_skip(s1)) continue; for (s2 = 1; s2 < s1; s2++) { if (shall_skip(s2)) continue; m = masks + n; sigemptyset(m); sigaddset(m, s1); sigaddset(m, s2); n++; m++; sigfillset(m); mask_skipped(m); sigdelset(m, s1); sigdelset(m, s2); n++; } } /* Triple-bit masks and inversions */ #ifdef CHATTY printf ("Preparing triple-bit masks...\n"); #endif for (s1 = 1; s1 <= SIGMAX; s1++) { if (shall_skip(s1)) continue; for (s2 = 1; s2 < s1; s2++) { if (shall_skip(s2)) continue; for (s3 = 1; s3 < s2; s3++) { if (shall_skip(s3)) continue; m = masks + n; sigemptyset(m); sigaddset(m, s1); sigaddset(m, s2); sigaddset(m, s3); n++; m++; sigfillset(m); mask_skipped(m); sigdelset(m, s1); sigdelset(m, s2); sigdelset(m, s3); n++; } } } assert(n == masks_count()); } /* * Visually dumps signal set. */ static void sigset_dump(FILE *fp, char *prefix, sigset_t *set) { size_t i, n, count; u_int32_t mask; fputs(prefix, fp); for (count = 0, i = 0; i < _SIG_WORDS; i++) { u_int32_t word = set->__bits[i]; for (n = 0, mask = 1; n < 32; n++, mask <<= 1) { fputc(((word & mask) ? 'B' : '~'), fp); count++; if (count % 8 == 0) fputc(' ', fp); } } fputs("\n", fp); } int main(void) { int sig; struct sigaction sa; sigset_t *masks, mask, cur; size_t nmasks, n; pid_t me = getpid(); nmasks = masks_count(); masks = (sigset_t *)calloc(nmasks, sizeof(masks[0])); if (masks == 0) errx(1, "Can't allocate memory."); masks_generate(masks); for (sig = 1; sig <= SIGMAX; sig++) { if (shall_skip(sig)) continue; for (n = 0; n < nmasks; n++) { int i; #ifdef CHATTY if (n % 100 == 0) printf("Signal %d, testing mask %zd\n", sig, n); #endif mask = masks[n]; /* We shouldn't block the signal we will deliver to ourselves */ sigdelset(&mask, sig); if (sigprocmask(SIG_SETMASK, &mask, NULL)) err(1, "sigprocmask([set]) failed"); for (i = 1; i <= SIGMAX; i++) { bzero ((void *)&sa, sizeof(sa)); sa.sa_sigaction = sig_handler; sa.sa_flags = SA_SIGINFO | SA_RESETHAND; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, i); if (sigaction(sig, &sa, NULL)) err(1, "sigaction(sig = %d, mask = %d) failed", sig, i); got_signal = 0; kill(me, sig); while (!got_signal) usleep(10); if (sigprocmask(SIG_SETMASK, NULL, &cur)) err(1, "sigprocmask([get]) failed"); if (!SIGSETEQ(cur, mask)) { printf("Failed for mask %zd:\n", n); sigset_dump(stdout, "Cur: ", &cur); sigset_dump(stdout, "Orig: ", &mask); } } } } return 0; }