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>