From archie@dellroad.org  Tue Apr 30 15:15:03 2002
Return-Path: <archie@dellroad.org>
Received: from InterJet.dellroad.org (adsl-63-194-81-26.dsl.snfc21.pacbell.net [63.194.81.26])
	by hub.freebsd.org (Postfix) with ESMTP id 68BB637B419
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 30 Apr 2002 15:15:02 -0700 (PDT)
Received: from arch20m.dellroad.org (arch20m.dellroad.org [10.1.1.20])
	by InterJet.dellroad.org (8.9.1a/8.9.1) with ESMTP id PAA63657
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 30 Apr 2002 15:12:40 -0700 (PDT)
Received: (from archie@localhost)
	by arch20m.dellroad.org (8.11.6/8.11.6) id g3UMCeV89274;
	Tue, 30 Apr 2002 15:12:40 -0700 (PDT)
	(envelope-from archie)
Message-Id: <200204302212.g3UMCeV89274@arch20m.dellroad.org>
Date: Tue, 30 Apr 2002 15:12:40 -0700 (PDT)
From: Archie Cobbs <archie@dellroad.org>
Reply-To: Archie Cobbs <archie@dellroad.org>
To: FreeBSD-gnats-submit@freebsd.org
Subject: libc_r aborts when exiting thread is canceled
X-Send-Pr-Version: 3.113

>Number:         37614
>Category:       bin
>Synopsis:       libc_r aborts when exiting thread is canceled
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    archie
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Apr 30 15:20:01 PDT 2002
>Closed-Date:    Tue May 07 11:49:05 PDT 2002
>Last-Modified:  Tue May 07 11:49:05 PDT 2002
>Originator:     Archie Cobbs
>Release:        FreeBSD 4.5-RELEASE i386
>Organization:
Packet Design
>Environment:
System: FreeBSD arch20m.dellroad.org 4.5-RELEASE FreeBSD 4.5-RELEASE #1: Sat Feb 2 19:17:35 PST 2002 root@arch20m.dellroad.org:/usr/obj/usr/src/sys/THINKPAD i386


>Description:

There seems to be a bug or problem in our pthreads implementation.

Consider a thread that has exited and is invoking its cleanup routines.
Another thread cancels that thread. Then one of the cleanup routines
invokes a function which is a cancelation point.    

Here's what happens:

> Fatal error 'Thread 0x806fc00 has called pthread_exit() from a destructor. POS
IX 1003.1 1996 s16.2.5.2 does not allow this!' at line ? in file /usr/src/lib/li
bc_r/uthread/uthread_exit.c (errno = ?)
>
> Program received signal SIGABRT, Aborted.
> 0x281ee53c in kill () from /usr/lib/libc_r.so.4
> (gdb) where
> #0  0x281ee53c in kill () from /usr/lib/libc_r.so.4
> #1  0x2823878a in abort () from /usr/lib/libc_r.so.4
> #2  0x2820504a in _thread_exit () from /usr/lib/libc_r.so.4
> #3  0x282050e9 in pthread_exit () from /usr/lib/libc_r.so.4
> #4  0x282370d7 in pthread_testcancel () from /usr/lib/libc_r.so.4
> #5  0x282370f8 in _thread_enter_cancellation_point () from /usr/lib/libc_r.so.
4
> #6  0x28236bc4 in close () from /usr/lib/libc_r.so.4
> #7  0x28193416 in timeout_fp_close (cookie=0x80645b4)
>     at /usr/home/archie/libpdel/io/timeout_fp.c:148
> #8  0x2822622b in fclose () from /usr/lib/libc_r.so.4
> #9  0x2818e780 in foobar_connection_free (connp=0xbfadced4)
>     at /usr/home/archie/libpdel/http/http_connection.c:136
> #10 0x28185d07 in foobar_server_connection_cleanup (arg=0x8062584)
>     at /usr/home/archie/libpdel/http/http_server.c:650
> #11 0x2820611d in pthread_cleanup_pop () from /usr/lib/libc_r.so.4
> #12 0x2820511e in pthread_exit () from /usr/lib/libc_r.so.4
> #13 0x281c5f99 in _thread_start () from /usr/lib/libc_r.so.4
> #14 0x0 in ?? ()

It seems that pthread_testcancel() should do nothing if the
thread is already in the process of exiting. Below is a patch.

>How-To-Repeat:

>Fix:

This is just a guess, but...

Index: uthread_cancel.c
===================================================================
RCS file: /home/cvs/freebsd/src/lib/libc_r/uthread/uthread_cancel.c,v
retrieving revision 1.3.2.4
diff -u -r1.3.2.4 uthread_cancel.c
--- uthread_cancel.c	19 Aug 2001 11:45:58 -0000	1.3.2.4
+++ uthread_cancel.c	30 Apr 2002 22:13:44 -0000
@@ -186,7 +186,8 @@
 pthread_testcancel(void)
 {
 	if (((_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
-	    ((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0)) {
+	    ((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0) &&
+	    ((_thread_run->flags & PTHREAD_EXITING) != 0)) {
 		/*
 		 * It is possible for this thread to be swapped out
 		 * while performing cancellation; do not allow it

>Release-Note:
>Audit-Trail:

From: Archie Cobbs <archie@packetdesign.com>
To: freebsd-gnats-submit@FreeBSD.org
Cc:  
Subject: Re: bin/37614: libc_r aborts when exiting thread is canceled
Date: Tue, 30 Apr 2002 15:23:45 -0700

 Oops, in that patch the "(_thread_run->flags & PTHREAD_EXITING) != 0"
 should of course instead be "(_thread_run->flags & PTHREAD_EXITING) == 0".
 
 -Archie
 
 __________________________________________________________________________
 Archie Cobbs     *     Packet Design     *     http://www.packetdesign.com

From: Archie Cobbs <archie@packetdesign.com>
To: freebsd-gnats-submit@FreeBSD.org
Cc:  
Subject: Re: bin/37614: libc_r aborts when exiting thread is canceled
Date: Wed, 01 May 2002 11:07:46 -0700

 Test case below.
 
 -Archie
 
 __________________________________________________________________________
 Archie Cobbs     *     Packet Design     *     http://www.packetdesign.com
 
 #include <stdio.h>
 #include <signal.h>
 #include <unistd.h>
 #include <errno.h>
 #include <sched.h>
 #include <pthread.h>
 #include <err.h>
 
 static void
 thread_cleanup(void *arg)
 {
         sched_yield();
         printf("Thread: executing cleanup...\n");
         pthread_testcancel();
 }
 
 static void *
 thread_main(void *arg)
 {
         pthread_cleanup_push(thread_cleanup, NULL);
         printf("Thread: sleeping 1 second...\n");
         sleep(1);
         printf("Thread: sending SIGTERM...\n");
         kill(getpid(), SIGTERM);
         sched_yield();
         printf("Thread: exiting...\n");
         return (NULL);
 }
 
 int
 main(int argc, char **argv)
 {
         pthread_t tid;
         sigset_t sigs;
         int sig;
 
         /* Spawn thread */
         printf("Main: spawning thread...\n");
         if ((errno = pthread_create(&tid, NULL, thread_main, NULL)) != 0)
                 err(1, "pthread_create");
 
         /* Wait for signal */
         sigemptyset(&sigs);
         sigaddset(&sigs, SIGINT);
         sigaddset(&sigs, SIGTERM);
         if (sigprocmask(SIG_BLOCK, &sigs, NULL) == -1)
                 err(1, "sigprocmask");
         printf("Main: waiting for signal...\n");
         if (sigwait(&sigs, &sig) == -1)
                 err(1, "sigwait");
 
         /* Cancel thread */
         printf("Main: canceling thread...\n");
         pthread_cancel(tid);
 
         /* Done */
         usleep(500);
         printf("Main: exiting...\n");
         return (0);
 }

From: Archie Cobbs <archie@packetdesign.com>
To: freebsd-gnats-submit@FreeBSD.org
Cc:  
Subject: Re: bin/37614: libc_r aborts when exiting thread is canceled
Date: Thu, 2 May 2002 12:18:02 -0700 (PDT)

 Oops, my patch is incomplete as pointed out by Daniel Eischen.
 The combined patchfile (both patches are required) is below.
 
 NOTE: these patches are for -stable; -current is very similar.
 
 -Archie
 
 __________________________________________________________________________
 Archie Cobbs     *     Packet Design     *     http://www.packetdesign.com
 
 Index: uthread_cancel.c
 ===================================================================
 RCS file: /home/cvs/freebsd/src/lib/libc_r/uthread/uthread_cancel.c,v
 retrieving revision 1.3.2.4
 diff -u -r1.3.2.4 uthread_cancel.c
 --- uthread_cancel.c	19 Aug 2001 11:45:58 -0000	1.3.2.4
 +++ uthread_cancel.c	2 May 2002 19:18:37 -0000
 @@ -15,7 +15,8 @@
  
  	if ((ret = _find_thread(pthread)) != 0) {
  		/* NOTHING */
 -	} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) {
 +	} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK
 +	    || (pthread->flags & PTHREAD_EXITING) != 0) {
  		ret = 0;
  	} else {
  		/* Protect the scheduling queues: */
 @@ -186,7 +187,8 @@
  pthread_testcancel(void)
  {
  	if (((_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
 -	    ((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0)) {
 +	    ((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0) &&
 +	    ((_thread_run->flags & PTHREAD_EXITING) == 0)) {
  		/*
  		 * It is possible for this thread to be swapped out
  		 * while performing cancellation; do not allow it
Responsible-Changed-From-To: freebsd-bugs->archie 
Responsible-Changed-By: archie 
Responsible-Changed-When: Sat May 4 21:36:34 PDT 2002 
Responsible-Changed-Why:  
Assign bug to me since I'm fixing it. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=37614 
State-Changed-From-To: open->closed 
State-Changed-By: archie 
State-Changed-When: Tue May 7 11:47:52 PDT 2002 
State-Changed-Why:  
Fixed in -current and -stable. 

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