From nobody@FreeBSD.ORG  Thu Oct 12 19:02:58 2000
Return-Path: <nobody@FreeBSD.ORG>
Received: by hub.freebsd.org (Postfix, from userid 32767)
	id 39E6C37B502; Thu, 12 Oct 2000 19:02:58 -0700 (PDT)
Message-Id: <20001013020258.39E6C37B502@hub.freebsd.org>
Date: Thu, 12 Oct 2000 19:02:58 -0700 (PDT)
From: mike@cs.utah.edu
Sender: nobody@FreeBSD.ORG
To: freebsd-gnats-submit@FreeBSD.org
Subject: pthreads: longjmp from signal handler jumps to the wrong location
X-Send-Pr-Version: www-1.0

>Number:         21943
>Category:       bin
>Synopsis:       pthreads: longjmp from signal handler jumps to the wrong location
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    jasone
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Oct 12 19:10:01 PDT 2000
>Closed-Date:    Fri Nov 17 16:15:01 PST 2000
>Last-Modified:  Fri Nov 17 16:15:36 PST 2000
>Originator:     Mike Hibler
>Release:        4.1.1-RELEASE
>Organization:
University of Utah
>Environment:
FreeBSD westdip.cs.utah.edu 4.1.1-RELEASE FreeBSD 4.1.1-RELEASE #2: Thu Oct  5 10:32:50 MDT 2000     mike@cognac.cs.utah.edu:/usr/src/sys/compile/UTAHUP  i386

>Description:
When using pthreads (gcc -pthread), performing a longjmp out of a
signal handler can sometimes return to the point at which the signal
occured rather than to the location indicated by the jmpbuf.

Specifically, when the signal is delivered from pthread_sigmask due
to unblocking a pending signal, a longjmp returns to the end of
pthread_sigmask instead of the corresponding setjmp location.

This appears to have been introduced 1/19/2000 when "continuations"
were added to "correctly handle [sig|_]longjmp() inside of a
signal handler".  The user's setjmp buf is saved in nested_jmp buf
the jmpflags are not checked after delivering signals in pthread_sigmask.


>How-To-Repeat:
With the following program:

#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <setjmp.h>
#include <signal.h>

#if 1
#define JMPBUF          sigjmp_buf
#define SETJMP(b)       sigsetjmp((b), 0)
#define LONGJMP(b,v)    siglongjmp((b), (v))
#else
#define JMPBUF          jmp_buf
#define SETJMP(b)       setjmp((b))
#define LONGJMP(b,v)    longjmp((b), (v))
#endif

static JMPBUF jb;
static pthread_mutex_t lock;
static pthread_cond_t cond;
static volatile int started, signaled;
static sigset_t mask;

static void
handler(int sig, siginfo_t *si, void *sc)
{
        printf("Got signal, longjmping...\n");
        signaled = 1;
        LONGJMP(jb, 1);
        assert(0);
}

static void *
cwloop(void *arg)
{
        struct sigaction act;
        int err;

        err = pthread_mutex_lock(&lock); assert(err == 0);

#ifdef SA_SIGINFO
        act.sa_flags = SA_SIGINFO;
        act.sa_sigaction = handler;
#else
        act.sa_handler = handler;
#endif
        sigemptyset(&act.sa_mask);
        sigaction(SIGUSR1, &act, 0);
        err = pthread_sigmask(SIG_BLOCK, &mask, 0); assert(err == 0);
        if (SETJMP(jb) != 0) {
                assert(signaled == 1);
                printf("WORKED!\n");
                pthread_exit(0);
        }

        printf("Child thread running, waiting for signal...\n");

        started = 1;
        err = pthread_cond_signal(&cond); assert(err == 0);
        err = pthread_mutex_unlock(&lock); assert(err == 0);
        while (!signaled) {
                err = pthread_sigmask(SIG_UNBLOCK, &mask, 0); assert(err == 0);
                err = pthread_sigmask(SIG_BLOCK, &mask, 0); assert(err == 0);
        }

        printf("FAILED!\n");
        pthread_exit((void *)1);

        return 0;
}

main()
{
        pthread_t th;
        int err;

        sigemptyset(&mask);
        sigaddset(&mask, SIGUSR1);

        err = pthread_mutex_init(&lock, 0); assert(err == 0);
        err = pthread_cond_init(&cond, 0); assert(err == 0);

        err = pthread_mutex_lock(&lock); assert(err == 0);
        err = pthread_create(&th, 0, cwloop, 0); assert(err == 0);
        while (!started) {
                err = pthread_cond_wait(&cond, &lock); assert(err == 0);
        }

        printf("Started child, sending SIGUSR1...\n");
        err = pthread_mutex_unlock(&lock); assert(err == 0);
        err = pthread_kill(th, SIGUSR1); assert(err == 0);
        err = pthread_join(th, (void **)&err); assert(err == 0);
        printf("Child died with exit code %d\n", err);
        exit(0);
}

>Fix:
Suggested above, but haven't tried.


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->jasone 
Responsible-Changed-By: jasone 
Responsible-Changed-When: Mon Oct 23 14:36:29 PDT 2000 
Responsible-Changed-Why:  
Over to maintainer. 

From: Jason Evans <jasone@canonware.com>
To: mike@cs.utah.edu
Cc: freebsd-gnats-submit@FreeBSD.org, deischen@freebsd.org
Subject: Re: bin/21943: pthreads: longjmp from signal handler jumps to the wrong location
Date: 6 Nov 2000 18:48:40 -0800

 On Thu, Oct 12, 2000 at 07:02:58PM -0700, mike@cs.utah.edu wrote:
 > >Description:
 > When using pthreads (gcc -pthread), performing a longjmp out of a
 > signal handler can sometimes return to the point at which the signal
 > occured rather than to the location indicated by the jmpbuf.
 
 Dan Eischen has some patches that appear to fix this, and he'll probably be
 committing them to -current within the next few days.  It's still not
 certain whether the libc_r changes are going to be MFCed for FreeBSD 4.2,
 but there's some chance it will happen.
 
 Jason
 
State-Changed-From-To: open->closed 
State-Changed-By: deischen 
State-Changed-When: Fri Nov 17 16:15:01 PST 2000 
State-Changed-Why:  
Fixed in both -stable and -current. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=21943 
>Unformatted:
