/* * Demonstrate improper delivery of a blocked signal. * * This program prints "ERROR: already forbidden" and aborts within one * second on this configuration (uname -srvm): * CYGWIN_NT-10.0 2.7.0(0.306/5/3) 2017-02-12 13:18 x86_64 * * It runs indefinitely (>600s) without trouble on these configurations: * CYGWIN_NT-6.0 1.7.27(0.271/5/3) 2013-12-09 11:57 i686 * Linux 3.10.0-514.16.1.el7.x86_64 #1 SMP Wed Apr 12 15:04:24 UTC 2017 x86_64 [CentOS 7] * AIX 7100-03-02-1412 * SunOS 5.10 Generic_147147-26 sun4u */ #include #include #include #include #include #include static sigset_t block, unblock; static sig_atomic_t sigblocked = 0; /* Have I blocked signals? */ static char *stack_base; static sigjmp_buf jmp; static void sigforbid(void) { const char errmsg[] = "ERROR: already forbidden\n"; if (sigprocmask(SIG_SETMASK, &block, NULL) != 0) perror("sigprocmask"); if (sigblocked == 1) { write(2, errmsg, sizeof(errmsg) - 1); abort(); } sigblocked = 1; } static void sigpermit(void) { const char errmsg[] = "ERROR: already permitted\n"; if (sigblocked == 0) { write(2, errmsg, sizeof(errmsg) - 1); abort(); } sigblocked = 0; if (sigprocmask(SIG_SETMASK, &unblock, NULL) != 0) perror("sigprocmask"); } /* * Start fresh in main() when the stack gets too deep. This is not essential * to the test, but it allows for long runs without huge RLIMIT_STACK. */ static void clear_stack_if_needed(void) { char stack_position; ptrdiff_t stack_usage; stack_usage = stack_base - &stack_position; if (stack_usage < 0) stack_usage = -stack_usage; if (stack_usage > 1024 * 1024) { puts("releasing excess stack"); sigblocked = 1; siglongjmp(jmp, 1); } } static void handler(int arg) { sigforbid(); clear_stack_if_needed(); /* wait for extra signal to arrive, after 1-2ms */ usleep(5000); sigpermit(); } int main(int argc, char **argv) { char stack_position; /* initial signal mask setup */ sigemptyset(&unblock); sigfillset(&block); sigdelset(&block, SIGTRAP); sigdelset(&block, SIGABRT); sigdelset(&block, SIGILL); sigdelset(&block, SIGFPE); sigdelset(&block, SIGSEGV); sigdelset(&block, SIGBUS); sigdelset(&block, SIGSYS); sigdelset(&block, SIGCONT); sigforbid(); /* Register signal handlers. Problem somehow requires two signals. */ { struct sigaction act, oact; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGUSR1, &act, &oact) != 0) perror("sigaction"); if (sigaction(SIGCHLD, &act, &oact) != 0) perror("sigaction"); } /* start a child to inundate me with signals */ { pid_t pid, ppid; pid = fork(); switch (pid) { case -1: perror("fork"); return 1; case 0: ppid = getppid(); printf("pid=%d inundating pid=%d with SIGUSR1 and SIGCHLD\n", getpid(), ppid); while (kill(ppid, random() % 2 ? SIGUSR1 : SIGCHLD) == 0) ; puts("child done"); return 0; } } /* loop forever while we receive signals */ stack_base = &stack_position; sigsetjmp(jmp, 1); for (;;) { sigpermit(); usleep(1000); sigforbid(); } }