Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 19 Feb 2011 18:09:53 GMT
From:      KOSAKI Motohiro <kosaki.motohiro@gmail.com>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   threads/154893: pthread_sigmask don't work if mask and oldmask are passed the same pointer
Message-ID:  <201102191809.p1JI9rZi063561@red.freebsd.org>
Resent-Message-ID: <201102191810.p1JIACTi075490@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         154893
>Category:       threads
>Synopsis:       pthread_sigmask don't work if mask and oldmask are passed the same pointer
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-threads
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Feb 19 18:10:12 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     KOSAKI Motohiro
>Release:        8.1
>Organization:
>Environment:
FreeBSD FreeBSD8 8.1-RELEASE FreeBSD 8.1-RELEASE #0: Mon Jul 19 02:36:49 UTC 2010     root@mason.cse.buffalo.edu:/\
usr/obj/usr/src/sys/GENERIC  amd64
>Description:
Programmers expect pthread_sigmask(SIG_SETMASK, &msk, &msk) mean
 1) rewritten signal mask by msk.
 2) and, return old signal mask to msk.

But, FreeBSD doesn't. Its pthread_sigmask behave the same as 
pthread_sigmask(SIG_SETMASK, NULL, &msk). It is very strange to me.

Sidenote:
man sigprocmask says its type is below.

     int
     sigprocmask(int how, const sigset_t * restrict set,
                                sigset_t * restrict oset);

It is not POSIX compliant nor user friendly. But the man page
clealy describe set==oset is invalid.
At least, pthread_sigmask's man page shold be fixed if uthread maintainers
woun't fix this issue.


Sidenote2:
This is a source of signal breakage of ruby trunk.
http://redmine.ruby-lang.org/issues/show/4173
>How-To-Repeat:
run following program

------------------------------------------------------
#include <pthread.h>
#include <signal.h>
#include <stdio.h>

void* func(void* arg)
{
        sigset_t old;
        sigset_t add;
        int i;

        sigemptyset(&old);
        pthread_sigmask(SIG_BLOCK, NULL, &old);

        printf("before: ");
        for (i=0; i<4; i++)
                printf(" %08x", old.__bits[i]);
        printf("\n");

        sigemptyset(&add);
        sigaddset(&add, SIGUSR1);
        pthread_sigmask(SIG_BLOCK, &add, NULL);
        pthread_sigmask(SIG_BLOCK, NULL, &old);

        printf("after:  ");
        for (i=0; i<4; i++)
                printf(" %08x", old.__bits[i]);
        printf("\n");

        return 0;
}

void* func2(void* arg)
{
        sigset_t old;
        sigset_t add;
        int i;

        sigemptyset(&old);
        pthread_sigmask(SIG_BLOCK, NULL, &old);

        printf("before: ");
        for (i=0; i<4; i++)
                printf(" %08x", old.__bits[i]);
        printf("\n");

        sigemptyset(&add);
        sigaddset(&add, SIGUSR1);
        pthread_sigmask(SIG_BLOCK, &add, &old);

        printf("after:  ");
        for (i=0; i<4; i++)
                printf(" %08x", old.__bits[i]);
        printf("\n");

        return 0;
}

int main(void)
{
        pthread_t thr;
        void* ret;

        printf("correct case: \n");
        pthread_create(&thr, NULL, func, NULL);
        pthread_join(thr, &ret);

        printf("incorrect case: \n");
        pthread_create(&thr, NULL, func2, NULL);
        pthread_join(thr, &ret);


        return 0;
}


>Fix:
/usr/src/lib/libc_r/uthread/uthread_sigmask.c has following code.
-----------------------------------------------------------------
int
_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
{
        struct pthread  *curthread = _get_curthread();
        sigset_t        sigset;
        int             ret = 0;

        /* Check if the existing signal process mask is to be returned: */
        if (oset != NULL) {
                /* Return the current mask: */
                *oset = curthread->sigmask;                    // (1)
        }
        /* Check if a new signal set was provided by the caller: */
        if (set != NULL) {

      (snip)

}
----------------------------------------------------

Then, if set == oset, set argument was override before use it at (1).
To introduce temporary variable fix this issue easily.


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201102191809.p1JI9rZi063561>