public inbox for cygwin-developers@cygwin.com
 help / color / mirror / Atom feed
* Signal delivered while blocked
@ 2021-12-27  4:51 Marco Atzeri
  2021-12-27 23:14 ` David McFarland
  0 siblings, 1 reply; 4+ messages in thread
From: Marco Atzeri @ 2021-12-27  4:51 UTC (permalink / raw)
  To: cygwin-developers

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

Hi Guys,

Some time ago (2017) a Postgres developer reported a signal issue
not present in older version (2013)

https://sourceware.org/pipermail/cygwin/2017-August/234001.html

For what I can see the issue is still present.


Ideas for debugging ?

Marco


[-- Attachment #2: sigprocmask-exclusion.c --]
[-- Type: text/plain, Size: 3190 bytes --]

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

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

* Re: Re: Signal delivered while blocked
  2021-12-27  4:51 Signal delivered while blocked Marco Atzeri
@ 2021-12-27 23:14 ` David McFarland
  2021-12-28  2:46   ` Marco Atzeri
  2021-12-28 13:41   ` Z. Majeed
  0 siblings, 2 replies; 4+ messages in thread
From: David McFarland @ 2021-12-27 23:14 UTC (permalink / raw)
  To: marco.atzeri; +Cc: cygwin-developers


> Hi Guys,
> 
> Some time ago (2017) a Postgres developer reported a signal issue
> not present in older version (2013)
> 
> https://sourceware.org/pipermail/cygwin/2017-August/234001.html
> 
> For what I can see the issue is still present.
> 
> 
> Ideas for debugging ?

I had a look at your program. What seems to be happening is that he
signal thread receives the signal, and uses find_tls to find a thread to
handle it. If this is after a call to sigpermit(), then the mask will
be zero and it will commit to sending the signal to the main thread of
the test program.

This is done without any lock, so the main thread can now handle an
existing signal, call sigforbid() then usleep().

At this point the signal thread can resume, queuing up the next signal,
and the main thread will pick it up when usleep calls
sig_dispatch_pending, even though the main thread signal mask now
disallows it.

The old thread ended with a discussion about whether this is even valid
se of sigprocmask(), which didn't seem to be resolved. Anyone have any
thoughts on that?

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

* Re: Signal delivered while blocked
  2021-12-27 23:14 ` David McFarland
@ 2021-12-28  2:46   ` Marco Atzeri
  2021-12-28 13:41   ` Z. Majeed
  1 sibling, 0 replies; 4+ messages in thread
From: Marco Atzeri @ 2021-12-28  2:46 UTC (permalink / raw)
  To: David McFarland; +Cc: cygwin-developers

On 28.12.2021 00:14, David McFarland wrote:
> 
>> Hi Guys,
>>
>> Some time ago (2017) a Postgres developer reported a signal issue
>> not present in older version (2013)
>>
>> https://sourceware.org/pipermail/cygwin/2017-August/234001.html
>>
>> For what I can see the issue is still present.
>>
>>
>> Ideas for debugging ?
> 
> I had a look at your program. What seems to be happening is that he
> signal thread receives the signal, and uses find_tls to find a thread to
> handle it. If this is after a call to sigpermit(), then the mask will
> be zero and it will commit to sending the signal to the main thread of
> the test program.
> 
> This is done without any lock, so the main thread can now handle an
> existing signal, call sigforbid() then usleep().
> 
> At this point the signal thread can resume, queuing up the next signal,
> and the main thread will pick it up when usleep calls
> sig_dispatch_pending, even though the main thread signal mask now
> disallows it.
> 
> The old thread ended with a discussion about whether this is even valid
> se of sigprocmask(), which didn't seem to be resolved. Anyone have any
> thoughts on that?

Thanks David,

I am not an expert in signal, but it seems the program is working as 
expected on Linux and other platforms and only cygwin is presenting it.
I have not anymore a Linux box so I can not check.

The test case is not mine, it is coming from Noah Misch,
https://www.postgresql.org/message-id/20170811021007.GB3623941%40rfd.leadboat.com
that also reported in 2017 on the Cygwin mailing list


and the discussion was recently revived on the Postgres dev list
https://www.postgresql.org/message-id/20210622064212.GA1367859@rfd.leadboat.com

Regards
Marco

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

* Re: Signal delivered while blocked
  2021-12-27 23:14 ` David McFarland
  2021-12-28  2:46   ` Marco Atzeri
@ 2021-12-28 13:41   ` Z. Majeed
  1 sibling, 0 replies; 4+ messages in thread
From: Z. Majeed @ 2021-12-28 13:41 UTC (permalink / raw)
  To: cygwin-developers

The discussion continued in https://sourceware.org/legacy-ml/cygwin/2017-08/msg00180.html


On Monday, December 27, 2021, 06:15:05 PM EST, David McFarland <corngood@gmail.com> wrote: 

> Hi Guys,
> 
> Some time ago (2017) a Postgres developer reported a signal issue
> not present in older version (2013)
> 
> https://sourceware.org/pipermail/cygwin/2017-August/234001.html
> 
> For what I can see the issue is still present.
> 
> 
> Ideas for debugging ?


I had a look at your program. What seems to be happening is that he
signal thread receives the signal, and uses find_tls to find a thread to
handle it. If this is after a call to sigpermit(), then the mask will
be zero and it will commit to sending the signal to the main thread of
the test program.

This is done without any lock, so the main thread can now handle an
existing signal, call sigforbid() then usleep().

At this point the signal thread can resume, queuing up the next signal,
and the main thread will pick it up when usleep calls
sig_dispatch_pending, even though the main thread signal mask now
disallows it.

The old thread ended with a discussion about whether this is even valid
se of sigprocmask(), which didn't seem to be resolved. Anyone have any
thoughts on that?


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

end of thread, other threads:[~2021-12-28 13:41 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-27  4:51 Signal delivered while blocked Marco Atzeri
2021-12-27 23:14 ` David McFarland
2021-12-28  2:46   ` Marco Atzeri
2021-12-28 13:41   ` Z. Majeed

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).