Subject: lib/37654: libc's atexit_mutex should be fully recursive
To: None <lib-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <sverre@abbor.fesk.com>
List: netbsd-bugs
Date: 12/31/2007 21:20:00
>Number: 37654
>Category: lib
>Synopsis: libc's atexit_mutex should be fully recursive
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Dec 31 21:20:00 +0000 2007
>Originator: Sverre Froyen
>Release: NetBSD 4.99.45
>Organization:
Viewmark
>Environment:
System: NetBSD abbor.fesk.com 4.99.45 NetBSD 4.99.45 (ABBOR) #56: Tue Dec 25 11:38:12 MST 2007 toor@abbor.fesk.com:/usr/src/objdir/sys/arch/i386/compile.i386/ABBOR i386
Architecture: i386
Machine: i386
>Description:
The QT4 qdbus command is a QT C++ utility that when destroying objects
after the call to exit(3) causes the method __cxa_atexit to be called
from within __cxa_finalize (__cxa_atexit and __cxa_finalize are libc
methods). Since both __cxa_atexit and __cxa_finalize acquire atexit_mutex,
and since the mutex is not recursive this causes qdbus to hang.
Here's a backtrace from when qdbus is looping in pthread__mutex_spin:
(gdb) bt
#0 pthread__mutex_spin (ptm=0xbb86dba0, owner=<value optimized out>)
at /usr/src/lib/libpthread/pthread_mutex2.c:206
#1 0xbbb56a63 in pthread__mutex_lock_slow (ptm=0xbb86dba0)
at /usr/src/lib/libpthread/pthread_mutex2.c:248
#2 0xbb83fb8c in __cxa_atexit () from /usr/lib/libc.so.12
#3 0xbba8f5ee in guardHash () at kernel/qobject.cpp:420
#4 0xbba92896 in QObjectPrivate::clearGuards (object=0xbb602430)
at kernel/qobject.cpp:487
#5 0xbba94be5 in ~QObject (this=0xbb602430) at kernel/qobject.cpp:799
#6 0xbb9ec75b in ~QThread (this=0xbb602430) at thread/qthread.cpp:354
#7 0xbba70485 in ~QProcessManager (this=0xbb602430)
at io/qprocess_unix.cpp:270
#8 0xbba6f0d0 in __tcf_0 ()
at ../../include/QtCore/../../src/corelib/global/qglobal.h:1434
#9 0xbb83fab6 in __cxa_finalize () from /usr/lib/libc.so.12
#10 0xbb83f962 in exit () from /usr/lib/libc.so.12
#11 0x08051356 in main (argc=-1080033280, argv=0x11110001) at qdbus.cpp:391
I gave this problem priority high because it is likely to affect the soon
to be released KDE4.
>How-To-Repeat:
Build and install x11/qt4-qdbus from pkgsrc and run qdbus.
>Fix:
Apply the following patch which makes atexit_mutex recursive:
Index: lib/libc/stdlib/atexit.c
===================================================================
RCS file: /cvsroot/src/lib/libc/stdlib/atexit.c,v
retrieving revision 1.19
diff -u -r1.19 atexit.c
--- lib/libc/stdlib/atexit.c 8 Aug 2007 01:05:34 -0000 1.19
+++ lib/libc/stdlib/atexit.c 31 Dec 2007 19:43:39 -0000
@@ -81,7 +81,8 @@
#ifdef _REENTRANT
/* ..and a mutex to protect it all. */
-static mutex_t atexit_mutex = MUTEX_INITIALIZER;
+static mutex_t atexit_mutex;
+uint atexit_mutex_ready = 0;
#endif /* _REENTRANT */
/*
@@ -119,6 +120,20 @@
}
/*
+ * Initialize atexit_mutex with the PTHREAD_MUTEX_RECURSIVE attribute.
+ * Note that __cxa_finalize may generate calls to __cxa_atexit.
+ */
+static void
+atexit_mutex_init(void)
+{
+ mutexattr_t atexit_mutex_attr;
+ mutexattr_init(&atexit_mutex_attr);
+ mutexattr_settype(&atexit_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
+ mutex_init(&atexit_mutex, &atexit_mutex_attr);
+ atexit_mutex_ready = 1;
+}
+
+/*
* Register an atexit routine. This is suitable either for a cxa_atexit
* or normal atexit type handler. The __cxa_atexit() name and arguments
* are specified by the C++ ABI. See:
@@ -132,6 +147,8 @@
_DIAGASSERT(func != NULL);
+ if (! atexit_mutex_ready)
+ atexit_mutex_init();
mutex_lock(&atexit_mutex);
ah = atexit_handler_alloc(dso);
@@ -162,23 +179,14 @@
void
__cxa_finalize(void *dso)
{
- static thr_t owner;
static u_int call_depth;
struct atexit_handler *ah, *dead_handlers = NULL, **prevp;
void (*cxa_func)(void *);
void (*atexit_func)(void);
- /*
- * We implement our own recursive mutex here because we need
- * to keep track of the call depth anyway, and it saves us
- * having to dynamically initialize the mutex.
- */
- if (mutex_trylock(&atexit_mutex) == 0)
- owner = thr_self();
- else if (owner != thr_self()) {
- mutex_lock(&atexit_mutex);
- owner = thr_self();
- }
+ if (! atexit_mutex_ready)
+ atexit_mutex_init();
+ mutex_lock(&atexit_mutex);
call_depth++;
@@ -224,13 +232,13 @@
prevp = &ah->ah_next;
}
+ mutex_unlock(&atexit_mutex);
+
call_depth--;
if (call_depth > 0)
return;
- mutex_unlock(&atexit_mutex);
-
/*
* Now free any dead handlers. Do this even if we're about to
* exit, in case a leak-detecting malloc is being used.