From owner-freebsd-bugs Thu Oct 12 19:10:15 2000 Delivered-To: freebsd-bugs@freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.org [216.136.204.21]) by hub.freebsd.org (Postfix) with ESMTP id 3D1CD37B66C for ; Thu, 12 Oct 2000 19:10:01 -0700 (PDT) Received: (from gnats@localhost) by freefall.freebsd.org (8.9.3/8.9.2) id TAA89681; Thu, 12 Oct 2000 19:10:01 -0700 (PDT) (envelope-from gnats@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 To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-1.0 Subject: bin/21943: pthreads: longjmp from signal handler jumps to the wrong location Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >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 #include #include #include #include #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