// gcc -std=c11 -D_POSIX_SOURCE -o sigprocmask-exclusion sigprocmask-exclusion.c // gcc -std=c11 -D_DEFAULT_SOURCE -o sigprocmask-exclusion sigprocmask-exclusion.c // if defined than the original signal mask is restored(SIG_SETMASK); otherwise the signal is removed from the mask (SIG_UNBLOCK) //#define option1 /* ... cfm 3 kopie van origineel gewijzigd: - forbid, permit verwijderd (forbid en permit opgenomen in main() ) - handlers: toevoeging en verwijdering van het andere signal - options: 1. either restore original mask (using SIG_SETMASK) or 2. removal signal from mask (using SIG_UNBLOCK) - 1st option fails after a long while, 2nd option fails mostly fails immediately - sigaction: het andere signal wordt niet verhinderd this test case fails on Cygwin; immediately when the 2nd option is used, after some time when the 1st option is used */ /* * 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 char *stack_base; static sigjmp_buf jmp; static int cnt_usr1; static int cnt_chld = 0; static int cnt_int = 0; static volatile sig_atomic_t i = 0; static volatile sig_atomic_t j = 0; static int r = 0; static ptrdiff_t stack_usage_prev = 0; /* * 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) { puts("NEGATIVE"); stack_usage = -stack_usage; } #if 0 if (stack_usage != stack_usage_prev) { printf("stack_usage: %d\n", stack_usage); stack_usage_prev = stack_usage; } #endif //if (stack_usage > 1024 * 1024) if (stack_usage > 5 * 1024) // 5 => 5120, 6 => 6144 { //sigprocmask(SIG_SETMASK, &block, NULL); puts("releasing excess stack"); //abort(); // if SA_NODEFER has been specified siglongjmp(jmp, 1); } } static void handler(int arg) { const char errmsg[] = "ERROR: handler: No 2 running\n"; const char errmsg2[] = "ERROR: handler: nesting self!\n"; sigset_t set, oset; r= sigemptyset(&set); r = sigaddset(&set, SIGCHLD); if (r != 0) puts("retvalH1"); if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) // voeg toe perror("sigprocmask"); cnt_usr1++; #if defined(__CYGWIN__) { sigset_t oset; sigemptyset(&oset); int rv = 0; if (sigprocmask(SIG_BLOCK, NULL, &oset) != 0) // fetch current mask/ verify perror("sigprocmask"); if ( (rv = sigismember(&oset, SIGUSR1)) == -1) perror("sigismember"); if (rv == 0) puts ("handlerB: USR1 missing"); if ( (rv = sigismember(&oset, SIGCHLD)) == -1) perror("sigismember"); if (rv == 0) puts ("handlerB: CHLD missing"); } #endif //puts("H "); if (i == 1) { write(2, errmsg2, sizeof(errmsg2) - 1); printf("cnt_usr1 = %d, cnt_chld = %d, cnt_int = %d\n", cnt_usr1, cnt_chld, cnt_int); // not safe //abort(); } if (j == 1) { write(2, errmsg, sizeof(errmsg) - 1); // whoa, not shown ... ????? printf("cnt_usr1 = %d, cnt_chld = %d, cnt_int = %d\n", cnt_usr1, cnt_chld, cnt_int); // not safe //abort(); } i = 1; clear_stack_if_needed(); usleep(5000); i = 0; //puts("H-e "); #if defined(__CYGWIN__) { sigset_t oset; sigemptyset(&oset); int rv = 0; if (sigprocmask(SIG_BLOCK, NULL, &oset) != 0) // fetch current mask/ verify perror("sigprocmask"); if ( (rv = sigismember(&oset, SIGUSR1)) == -1) perror("sigismember"); if (rv == 0) puts ("handlerE: USR1 missing"); if ( (rv = sigismember(&oset, SIGCHLD)) == -1) perror("sigismember"); if (rv == 0) puts ("handlerE: CHLD missing"); } #endif #if defined(option1) if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) // herstel perror("sigprocmask"); #else r = sigdelset(&set, SIGCHLD); if (r != 0) puts("retvalH1b"); if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) // verwijder perror("sigprocmask"); // Cygwin: aborts, almost immediately #endif } static void handler2(int arg) { const char errmsg[] = "ERROR: handler2: No 1 running\n"; const char errmsg2[] = "ERROR: handler2: nesting self!\n"; sigset_t set, oset; r = sigemptyset(&set); r = sigaddset(&set, SIGUSR1); if (r != 0) puts("retvalH2"); if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) // voeg toe perror("sigprocmask"); cnt_chld++; #if defined(__CYGWIN__) { sigset_t oset; sigemptyset(&oset); int rv = 0; if (sigprocmask(SIG_BLOCK, NULL, &oset) != 0) // fetch current mask/ verify perror("sigprocmask"); if ( (rv = sigismember(&oset, SIGUSR1)) == -1) perror("sigismember"); if (rv == 0) puts ("handler2B: USR1 missing"); if ( (rv = sigismember(&oset, SIGCHLD)) == -1) perror("sigismember"); if (rv == 0) puts ("handler2B: CHLD missing"); } #endif //puts(" H2"); if (j == 1) { write(2, errmsg2, sizeof(errmsg2) - 1); printf("cnt_usr1 = %d, cnt_chld = %d, cnt_int = %d\n", cnt_usr1, cnt_chld, cnt_int); // not safe //abort(); } if (i == 1) { write(2, errmsg, sizeof(errmsg) - 1); printf("cnt_usr1 = %d, cnt_chld = %d, cnt_int = %d\n", cnt_usr1, cnt_chld, cnt_int); // not safe //abort(); } j = 1; clear_stack_if_needed(); usleep(5000); j = 0; //puts(" H2-e"); #if defined(__CYGWIN__) { sigset_t oset; sigemptyset(&oset); int rv = 0; if (sigprocmask(SIG_BLOCK, NULL, &oset) != 0) // fetch current mask/ verify perror("sigprocmask"); if ( (rv = sigismember(&oset, SIGUSR1)) == -1) perror("sigismember"); if (rv == 0) puts ("handler2E: USR1 missing"); if ( (rv = sigismember(&oset, SIGCHLD)) == -1) perror("sigismember"); if (rv == 0) puts ("handler2E: CHLD missing"); } #endif #if defined(option1) if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) // herstel perror("sigprocmask"); #else r = sigdelset(&set, SIGUSR1); if (r != 0) puts("retvalH2b"); if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) // verwijder perror("sigprocmask"); #endif } #if 1 static void handler3(int arg) // note: exhibits racing between bash and child { cnt_int++; printf("cnt_usr1 = %d, cnt_chld = %d, cnt_int = %d\n", cnt_usr1, cnt_chld, cnt_int); } #endif int main(int argc, char **argv) { char stack_position; /* initial signal mask setup */ r = sigemptyset(&unblock); r = sigfillset(&block); r = sigdelset(&block, SIGTRAP); r = sigdelset(&block, SIGABRT); r = sigdelset(&block, SIGILL); r = sigdelset(&block, SIGFPE); r = sigdelset(&block, SIGSEGV); r = sigdelset(&block, SIGBUS); r = sigdelset(&block, SIGSYS); r = sigdelset(&block, SIGCONT); if (r != 0) puts("retval0"); if (sigprocmask(SIG_SETMASK, &block, NULL) != 0) // forbid perror("sigprocmask"); /* Register signal handlers. Problem somehow requires two signals. */ { struct sigaction act, oact; // the sa_mask is only applied during the execution of the handler act.sa_handler = handler; r = sigemptyset(&act.sa_mask); // however SIGUSR is auto added to mask during execution of the handler, is it not? // do it in the handler //r = sigaddset(&act.sa_mask, SIGCHLD); // added if (r != 0) puts("retval1"); act.sa_flags = 0; //act.sa_flags = SA_NODEFER; // be stupid if (sigaction(SIGUSR1, &act, &oact) != 0) perror("sigaction"); struct sigaction act2, oact2; act2.sa_handler = handler2; r = sigemptyset(&act2.sa_mask); //r = sigaddset(&act2.sa_mask, SIGUSR1); // added if (r != 0) puts("retval2"); act2.sa_flags = 0; //act2.sa_flags = SA_NODEFER; // be stupid if (sigaction(SIGCHLD, &act2, &oact2) != 0) perror("sigaction"); #if 1 struct sigaction act3, oact3; act3.sa_handler = handler3; r = sigfillset(&act3.sa_mask); r = sigdelset(&act3.sa_mask, SIGINT); // remove if (r != 0) puts("retval3"); act3.sa_flags = SA_RESETHAND; if (sigaction(SIGINT, &act3, &oact3) != 0) perror("sigaction"); #endif } /* 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); #if 0 while (kill(ppid, random() % 2 ? SIGUSR1 : SIGCHLD) == 0) ; #else volatile int i = 0; //volatile int i = 1; int xcnt_usr1 = 0; int xcnt_chld = 0; while (1) { i = i ? 0 : 1; if (1) { if (kill(ppid, SIGCHLD) != 0) break; xcnt_chld++; if (kill(ppid, SIGUSR1) != 0) break; // LOST! WHY? xcnt_usr1++; if (kill(ppid, SIGUSR1) != 0) break; // LOST! WHY? xcnt_usr1++; if (kill(ppid, SIGCHLD) != 0) break; xcnt_chld++; } else { if (kill(ppid, SIGUSR1) != 0) break; // LOST! WHY? xcnt_usr1++; if (kill(ppid, SIGCHLD) != 0) break; xcnt_chld++; if (kill(ppid, SIGCHLD) != 0) break; xcnt_chld++; if (kill(ppid, SIGUSR1) != 0) break; // LOST! WHY? xcnt_usr1++; } #if defined(__linux__) usleep(10000); // if disabled, either cnt_usr1 stays zero ????? // enabked: 2 : 1, disabled: 10.000 : 1 #endif #if defined(__CYGWIN__) //usleep(10000); // if disabled ... // using SIG_SETMASK (restore mask, saved at the start of the handler): ... abort, but after a long while (it seems) // using SIG_UNBLOCK: abort (almost immediately) ... Sigh! #endif } #endif puts("child done"); printf("xcnt_usr1 = %d, xcnt_chld = %d\n", xcnt_usr1, xcnt_chld); return 0; } } /* loop forever while we receive signals */ stack_base = &stack_position; sigsetjmp(jmp, 1); for (;;) { puts("-"); if (sigprocmask(SIG_SETMASK, &unblock, NULL) != 0) // permit perror("sigprocmask"); puts("--"); usleep(1000); puts("---"); if (sigprocmask(SIG_SETMASK, &block, NULL) != 0) // forbid perror("sigprocmask"); puts("----"); } }