public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
* REVISITED: Signal delivered while blocked
@ 2017-08-16 21:22 Houder
  2017-08-18 16:20 ` Corinna Vinschen
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Houder @ 2017-08-16 21:22 UTC (permalink / raw)
  To: cygwin

[-- Attachment #1: Type: text/plain, Size: 4168 bytes --]

Hi,

Please read this post first:

     https://cygwin.com/ml/cygwin/2017-08/msg00048.html
     ( Signal delivered while blocked -- by Noah Misch, August 4th 2017 )

This post is not intended to "hijack" the post by Noah Misch; this post 
only
ships an alternative (i.e. revised) testcase for the one by provided by 
Noah
Misch.

The alternative testcase in fact consists of 2 testcases (2 files):

  1. sigprocmask-exclusion4.c
  2. sigprocmask-exclusion5.c

The 1st testcase uses sigaction() (i.c. sa_mask) in order to keep both 
signal
handlers "out of each other's hair".

The 2nd testcase achieves this by protecting the "vital part" of each of 
the
signal handlers using sigprocmask() ...

Using sigprocmask() and SIG_BLOCK (1st argument), the other signal is 
added
to the signal mask at the start of the "vital part" of the signal 
handler.

To restore the signal mask at the end of the "vital part" of the 
handler, it
is possible to choose between the following options:

  a. using sigprocmask() and SIG_SETMASK in order to reinstate the mask 
as it
     was at the beginning of the "vital part" of the handler

  b. using sigprocmask() and SIG_UNBLOCK to remove the other signal from 
the
     signal mask

Both testcases confirm that Cygwin _sometimes_ delivers a signal, 
although
the signal mask specifies (verified by the test case) it should not.

Typical runs:

64-@@ ./sigprocmask-exclusion4
-
pid=2728 inundating pid=3504 with SIGUSR1 and SIGCHLD
--
ERROR: handler2: No 1 running
cnt_usr1 = 424, cnt_chld = 305, cnt_int = 0
ERROR: handler2: No 1 running
cnt_usr1 = 1212, cnt_chld = 1262, cnt_int = 0
ERROR: handler: No 2 running
cnt_usr1 = 4341, cnt_chld = 3827, cnt_int = 0
ERROR: handler: No 2 running
cnt_usr1 = 5289, cnt_chld = 4171, cnt_int = 0
ERROR: handler: No 2 running
cnt_usr1 = 5499, cnt_chld = 4354, cnt_int = 0
ERROR: handler: No 2 running
cnt_usr1 = 6769, cnt_chld = 6295, cnt_int = 0
ERROR: handler2: nesting self!
cnt_usr1 = 10463, cnt_chld = 9243, cnt_int = 0
ERROR: handler2: nesting self!
cnt_usr1 = 12390, cnt_chld = 10869, cnt_int = 0
ERROR: handler: No 2 running
cnt_usr1 = 14046, cnt_chld = 14771, cnt_int = 0
ERROR: handler2: No 1 running
cnt_usr1 = 16111, cnt_chld = 15785, cnt_int = 0
cnt_usr1 = 16959, cnt_chld = 16790, cnt_int = 1
child done
xcnt_usr1 = 13295798, xcnt_chld = 13295797

64-@@

64-@@ ./sigprocmask-exclusion5
-
--
pid=4132 inundating pid=5976 with SIGUSR1 and SIGCHLD
ERROR: handler: No 2 running
cnt_usr1 = 1, cnt_chld = 2, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 228, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 1444, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 1585, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 1830, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 2588, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 2719, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 2795, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 3332, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 3340, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 3353, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 4130, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 4136, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 5256, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 6548, cnt_chld = 6, cnt_int = 0
ERROR: handler: nesting self!
cnt_usr1 = 8292, cnt_chld = 6, cnt_int = 0
cnt_usr1 = 11223, cnt_chld = 6, cnt_int = 1
child done
xcnt_usr1 = 4149717, xcnt_chld = 4149717

64-@@

Both runs were terminated by SIGINT (^C).

When running for an extended period (hours), the run-away stack did not 
show
up (Linux).

Environment:

64-@@ uname -a
CYGWIN_NT-6.1 Seven 2.9.0(0.316/5/3)  x86_64 Cygwin
64-@@ gcc --version
gcc (GCC) 5.4.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is 
NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR 
PURPOSE

Regards,

Henri

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: sigprocmask-exclusion4.c --]
[-- Type: text/x-c; name=sigprocmask-exclusion4.c, Size: 8685 bytes --]

// gcc -std=c11 -D_POSIX_SOURCE -o sigprocmask-exclusion sigprocmask-exclusion.c
// gcc -std=c11 -D_DEFAULT_SOURCE -o sigprocmask-exclusion sigprocmask-exclusion.c

/* ... cfm 2
 kopie van origineel
 gewijzigd:
  - forbid, permit verwijderd (forbid en permit opgenomen in main() )
  - handlers: geen bewerking van het signal mask
  - sigaction: het andere signal wordt verhinderd
 this test case fails on Cygwin; period!
 */

/*
 * 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 <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

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 > 4 * 1024)  // 4 = 4096, 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";
	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);
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
}
static void
handler2(int arg)
{
	const char errmsg[] = "ERROR: handler2: No 1 running\n";
	const char errmsg2[] = "ERROR: handler2: nesting self!\n";
	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 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?
	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, cnt_chld stays zero ?????
    // enabled: 2 : 1, disabled: 100.000 : 1
#endif
#if defined(__CYGWIN__)
    //usleep(10000); // if disabled, it will fail (abort) after some time (... nesting?)
    // enabled: 10 : 1, disabled: 1000 : 1
#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("----");
	}
}

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: sigprocmask-exclusion5.c --]
[-- Type: text/x-c; name=sigprocmask-exclusion5.c, Size: 10252 bytes --]

// 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 <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

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("----");
	}
}

[-- Attachment #4: Type: text/plain, Size: 219 bytes --]


--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: REVISITED: Signal delivered while blocked
  2017-08-16 21:22 REVISITED: Signal delivered while blocked Houder
@ 2017-08-18 16:20 ` Corinna Vinschen
  2017-08-20 13:20   ` Houder
  2017-08-20 12:59 ` Houder
  2017-08-20 15:44 ` Kaz Kylheku
  2 siblings, 1 reply; 5+ messages in thread
From: Corinna Vinschen @ 2017-08-18 16:20 UTC (permalink / raw)
  To: cygwin

[-- Attachment #1: Type: text/plain, Size: 1398 bytes --]

On Aug 16 23:22, Houder wrote:
> Hi,
> 
> Please read this post first:
> 
>     https://cygwin.com/ml/cygwin/2017-08/msg00048.html
>     ( Signal delivered while blocked -- by Noah Misch, August 4th 2017 )
> 
> This post is not intended to "hijack" the post by Noah Misch; this post only
> ships an alternative (i.e. revised) testcase for the one by provided by Noah
> Misch.
> 
> The alternative testcase in fact consists of 2 testcases (2 files):
> 
>  1. sigprocmask-exclusion4.c
>  2. sigprocmask-exclusion5.c

Thanks for the testcases.  This is still a pretty tricky problem and a
few hours of debugging haven't shown anything conclusive.  The signal
code was my former co-maintainer's domain, so I'm not as fluent in
debugging it.

ATM it looks like a race inside of the Cygwin DLL to me.  The checks if
a signal should be handled and the *creation* of the call to the signal
handler (but *not* the actual call to the signal handler) may occur
before the signal is blocked, while the call then occurs after the
blocking.

Anyway, I'm not sure I have enough time to fully immerse into that
problem any time soon.  I'd be not too unhappy if somebody would try to
debug this in the Cygwin DLL, too...


Corinna

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: REVISITED: Signal delivered while blocked
  2017-08-16 21:22 REVISITED: Signal delivered while blocked Houder
  2017-08-18 16:20 ` Corinna Vinschen
@ 2017-08-20 12:59 ` Houder
  2017-08-20 15:44 ` Kaz Kylheku
  2 siblings, 0 replies; 5+ messages in thread
From: Houder @ 2017-08-20 12:59 UTC (permalink / raw)
  To: cygwin

On Wed, 16 Aug 2017 23:22:31, Houder wrote:
> 
> Hi,
> 
> Please read this post first:
> 
>      https://cygwin.com/ml/cygwin/2017-08/msg00048.html
>      ( Signal delivered while blocked -- by Noah Misch, August 4th 2017 )
> 
> This post is not intended to "hijack" the post by Noah Misch; this post 
> only ships an alternative (i.e. revised) testcase for the one by provided
> by Noah Misch.
> 
> The alternative testcase in fact consists of 2 testcases (2 files):
> 
>   1. sigprocmask-exclusion4.c
>   2. sigprocmask-exclusion5.c

Only for those interested ...

I made a minor modification to the 2nd testcase (sigprocmask-exclusion5.c) (in
order to get it theoretically correct).

I modified the last part (bottom) of each handler. See below.

On request, I will upload the files again.

Regards,

Henri

-----
handler(): // old

#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
}

handler(): // new

#if defined(option1)
	if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) // herstel
		perror("sigprocmask");
#else
	{
		int rv = 0;
		if ( (rv = sigismember(&oset, SIGCHLD)) == -1)
			perror("sigismember");
		// if the other signal was enabled at the beginning, enable it again
		if (rv == 0) {
			if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) // verwijder
				perror("sigprocmask");
		}
	}
#endif
}

-----
handler2(): // old

#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
}

handler2(): // new

#if defined(option1)
	if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) // herstel
		perror("sigprocmask");
#else
	{
		int rv = 0;
		if ( (rv = sigismember(&oset, SIGUSR1)) == -1)
			perror("sigismember");
		// if the other signal was enabled at the beginning, enable it again
		if (rv == 0) {
			if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) // verwijder
				perror("sigprocmask");
		}
	}
#endif
}

=====


--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: REVISITED: Signal delivered while blocked
  2017-08-18 16:20 ` Corinna Vinschen
@ 2017-08-20 13:20   ` Houder
  0 siblings, 0 replies; 5+ messages in thread
From: Houder @ 2017-08-20 13:20 UTC (permalink / raw)
  To: cygwin

On Fri, 18 Aug 2017 18:20:40, Corinna Vinschen wrote:
> On Aug 16 23:22, Houder wrote:
[snip]

> > The alternative testcase in fact consists of 2 testcases (2 files):
> >
> >  1. sigprocmask-exclusion4.c
> >  2. sigprocmask-exclusion5.c

> Thanks for the testcases.  This is still a pretty tricky problem and a

Providing a proper testcase was my sole purpose (intention?). That is,
to help you (i.e. to prevent you from waisting your precious time).

Though Noah believes that my modification of his testcase is uncalled
for.

Consequently, it is up your discretion :-)

> few hours of debugging haven't shown anything conclusive.  The signal
> code was my former co-maintainer's domain, so I'm not as fluent in
> debugging it.

Got it, "Advantage Christopher Faylor, disadvantage Corinna Vinschen"!

Perhaps Christopher would be willing to lend you a hand here ... for old
times' sake ... or as an once-only challenge (to proof to himself, he can
still do it :-))

> ATM it looks like a race inside of the Cygwin DLL to me.  The checks if
> a signal should be handled and the *creation* of the call to the signal
> handler (but *not* the actual call to the signal handler) may occur
> before the signal is blocked, while the call then occurs after the
> blocking.

My impression as well (however, in my case it was based on the results of
the testcases).

.. uhm, in "U/Linux land" the delivery of a signal is "immediate", once
the process returns to the kernel ... (i.e. the stack of the process is
adjusted with a call to the signal handler before it will continue).

.. signal handler A is not allowed to continue (and block signal B) if
the kernel has decided that signal handler B should run.

That is, it is what I remember from old times ...

> Anyway, I'm not sure I have enough time to fully immerse into that
> problem any time soon.  I'd be not too unhappy if somebody would try to
> debug this in the Cygwin DLL, too...

I hear you. However I am not capable to help you here ... Perhaps somebody
else can ... a much younger person than I am.

Regards,

Henri

=====


--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: REVISITED: Signal delivered while blocked
  2017-08-16 21:22 REVISITED: Signal delivered while blocked Houder
  2017-08-18 16:20 ` Corinna Vinschen
  2017-08-20 12:59 ` Houder
@ 2017-08-20 15:44 ` Kaz Kylheku
  2 siblings, 0 replies; 5+ messages in thread
From: Kaz Kylheku @ 2017-08-20 15:44 UTC (permalink / raw)
  To: Houder; +Cc: Cygwin

> Both testcases confirm that Cygwin _sometimes_ delivers a signal, 
> although
> the signal mask specifies (verified by the test case) it should not.

Hence, your fixes are not relevant to the issue.

However, they also demonstrate that the signal nesting (at least,
self-esting: the same signal being handled more than once)
is also not relevant to the issue.

Eliminating unlimited nesting can be regarded as an improvement
to the test case, since it doesn't have to worry about hitting
a stack depth limit.

The original test case is simple in that it uses precisely one known
signal mask for blocking the signals, and another one for unblocking
them. So it it is easy to see that the signal went off when it
shouldn't have.

This global clobbering of the signal mask leads to the nesting
because, for instance, when the SIGCHLD signal is running,
before it has reached the  sigforbid() call, the SIGUSR1 signal
handler can go off. That handler will finish by calling sigpermit()
which will unblock not only SIGUSR1 but also SIGCHLD. So control
returns to the SIGCHLD handler, and SIGCHLD is now unblocked,
opening the door to nesting of that signal. Another window for this
to happen is after the SIGCHLD has itself called sigpermit(),
as the last step before returning.

You're not correct in insinuating that POSIX forbids such a 
manipulation.

Such a manipulation is merely a bad idea; it is not good application
practice for a signal handler to clobber the mask bits belonging to
other signals.

"We wouldn't do this in a real program" isn't the same
as "POSIX doesn't define it".

It's perfectly okay for API test programs to deviate from best 
practices.
If that is necessary to make a problem easier to reproduce, or
to simplify the test case, then it should be done that way.


--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-08-20 15:44 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-16 21:22 REVISITED: Signal delivered while blocked Houder
2017-08-18 16:20 ` Corinna Vinschen
2017-08-20 13:20   ` Houder
2017-08-20 12:59 ` Houder
2017-08-20 15:44 ` Kaz Kylheku

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).