Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 12 Oct 2000 19:02:58 -0700 (PDT)
From:      mike@cs.utah.edu
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   bin/21943: pthreads: longjmp from signal handler jumps to the wrong location
Message-ID:  <20001013020258.39E6C37B502@hub.freebsd.org>

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

>Number:         21943
>Category:       bin
>Synopsis:       pthreads: longjmp from signal handler jumps to the wrong location
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Oct 12 19:10:01 PDT 2000
>Closed-Date:
>Last-Modified:
>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:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




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