NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/40041: 4.0 libpthread may permernantly mask process level signal during fast signal delivery.



>Number:         40041
>Category:       lib
>Synopsis:       4.0 libpthread may permernantly mask process level signal 
>during fast signal delivery.
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Nov 26 20:25:03 +0000 2008
>Originator:     Chris Zhang
>Release:        NetBSD 4.0
>Organization:
SeaMicro Inc.
>Environment:
NetBSD caladan 4.0 NetBSD 4.0 (GENERIC.MP) #0: Sun Dec 16 00:47:41 PST 2007  
builds@wb34:/home/builds/ab/netbsd-4-0-RELEASE/i386/200712160005Z-obj/home/builds/ab/netbsd-4-0-RELEASE/src/sys/arch/i386/compile/GENERIC.MP
 i386
>Description:
I know SA based libpthread is discontinued in -current, but the bug might still 
be relevant in case any more new releases against 4.0 (4.0.2?).

If we use libpthread, and start a fast timer (e.g. 10ms timer), the libpthread 
will eventually mask out SIGALRM forever. This should also apply to other 
signals (when delivered faster than the sig handler can handle).

In pthread_sigmask/pthread_sig.c, it checks for any pending signals and call 
pthread__kill_self to handle them. If another SIGALRM happens in the middle of 
user signal handler (through sa_sigaction), pthread__signal may mask the 
process signal delivery because no thread is available to handle it. 
pthread__kill_self, when done, doesn't check and unblock the process wide 
SIGALRM. Hence, SIGALRM is permanently blocked.

pthread_sigmask is invoked by pthread__kill whenever a signal is delivered to 
the thread from kernel.

The problem is platform independent, it's tested under x86 and ppc.

--- Chris ---
>How-To-Repeat:
/* 
 * Start a 10ms timer, in the SIGALRM handler, sleep for 1 sec,
 * you will see the signal handler is only invoked twice.
 * Afterwards "ps -O sigmask" shows SIGALRM masked.
 * The first time is called from kernel
 * The second time is triggered by libpthread (through sig pending list)
 * process wide signal is permanently disabled after the second
 * signal delivery.
 */

#include <sys/time.h>
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void *
t1_thread(void *arg)
{
        /* doesn't contribute to our test, just a dummy task */
        while(1) {
                sleep(1);
        }
        return NULL;
}

void alrm_hdlr(int sig)
{
        /* sleep longer than timer reloading */
        printf("TIMER RUNNING\n");
        sleep(1);
}

int
main(void)
{
        struct itimerval v, ov;
        sigset_t set;
        struct sigaction sact;
        pthread_t t1;

        sigemptyset(&set);
        sigaddset(&set, SIGALRM);

        pthread_sigmask(SIG_BLOCK, &set, NULL);

        v.it_interval.tv_sec  = 0;
        v.it_interval.tv_usec = 10000;  /* 10 ms */

        v.it_value.tv_sec  = 0;
        v.it_value.tv_usec = 10000;     /* 10 ms */
        sact.sa_handler = alrm_hdlr;
        sact.sa_flags = SA_RESTART;
        sigemptyset(&sact.sa_mask);
   
        pthread_create(&t1, NULL, t1_thread, NULL);
        pthread_sigmask(SIG_UNBLOCK, &set, NULL);
        sigaction(SIGALRM, &sact, NULL);
        setitimer(ITIMER_REAL, &v, &ov);
        while (1) {
                sleep(1);
        }
}
>Fix:
The following patch seems to make the problem go away.


--- pthread_sig.c       2008-11-26 12:17:51.000000000 -0800
+++ pthread_sig.c.new   2008-11-26 12:15:40.000000000 -0800
@@ -803,6 +803,7 @@
 static void
 pthread__kill_self(pthread_t self, siginfo_t *si)
 {
+       sigset_t tmp;
        sigset_t oldmask;
        struct sigaction act;
        ucontext_t uc;  /* XXX: we don't pass the right context here */
@@ -823,6 +824,13 @@
        pthread_spinlock(self, &self->pt_siglock);

        self->pt_sigmask = oldmask;
+
+       tmp = pt_process_sigmask;
+       __sigandset(&self->pt_sigmask, &tmp);
+       if (!__sigsetequal(&tmp, &pt_process_sigmask)) {
+               pt_process_sigmask = tmp;
+               _sys___sigprocmask14(SIG_SETMASK, &pt_process_sigmask, NULL);
+       }
 }

 /* Must be called with target's siglock held */



Home | Main Index | Thread Index | Old Index