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>
