Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 20 Aug 2005 16:32:39 GMT
From:      Pascal Hofstee <caelian@gmail.com>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   threads/85160: libobjc + libpthread/libthr crash problem
Message-ID:  <200508201632.j7KGWd9B026955@www.freebsd.org>
Resent-Message-ID: <200508201640.j7KGeGd8006209@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         85160
>Category:       threads
>Synopsis:       libobjc + libpthread/libthr crash problem
>Confidential:   no
>Severity:       critical
>Priority:       low
>Responsible:    freebsd-threads
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Aug 20 16:40:16 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Pascal Hofstee
>Release:        FreeBSD 7.0-CURRENT amd64
>Organization:
>Environment:
System: FreeBSD 7.0-CURRENT #7: Wed Aug 17 22:27:49 PDT 2005
    daeron@synergy.charterpipeline.net.lan:/usr/obj/usr/src/sys/SYNERGY

>Description:
Ever since the GNUstep project decided to make certain changes
regarding how GNUstep applications should be linked to external
libraries ... GNUstep builds on the FreeBSD platform appeared to be
broken where they used to function properly before.
(Problem being applications segfaulting in libobjc's thread initialization)

A while ago now, David Ayers, one of the people with an active GNUstep
interest and willingness to see this issue resolved was kind enough to
allow me to provide him access to my FreeBSD/amd64 7.0-CURRENT
machine in an attempt to get to the bottom of this issue.

His final analysis boils down to the following:

----[ Analysis of GNUstep on FreeBSD pthread problem ]----

The real bug does indeed lie in the internal initialization process of
the FreeBSD libpthread library. In this case when libobjc is not
excplitly linked into the executable, (and is further at the bottom of
the ldd depenency list) it get's initialized before libpthread does.
i.e. it starts calling libpthread functions before the following
constructor: 

void _thread_init_hack(void) __attribute__ ((constructor)); 

void 
_thread_init_hack(void) 
{ 

_libpthread_init(NULL); 
} 

of the libpthread library is called. This constructor sets up the data
structures for _get_curthread(). 

The function that libobjc calls early: 
pthread_key_create() calls _get_currthread which still returns NULL.
This pointer is then dereferenced in the THR_LOCK_ACQUIRE macro
resulting in the segfault. 

Many of the mutex functions have already been guarded for this case with
the following code fragements: 

if (_thr_initial == NULL) 
_libpthread_init(NULL); 

See also the following comment in thr_create.c: 
.. 
* Some notes on new thread creation and first time initializion 
* to enable multi-threading. 
* 
* There are basically two things that need to be done. 
* 
* 1) The internal library variables must be initialized. 
* 2) Upcalls need to be enabled to allow multiple threads 
* to be run. 
* 
* The first may be done as a result of other pthread functions 
* being called. When _thr_initial is null, _libpthread_init is 
* called to initialize the internal variables; this also creates 
* or sets the initial thread. It'd be nice to automatically 
* have _libpthread_init called on program execution so we don't 
* have to have checks throughout the library. 
.. 

So they seem to be aware of the issue but haven't guarded all functions
like pthread_key_create() which libobjc calls. 

I don't believe that we can find a reliable workaround within GNUstep. I
also don't know if this bug is in a stable release version of FreeBSD.
But I want to mention that a call to pthread_self() would workaround
this. Yet this call would have to be done in libobjc's
__objc_init_thread_system and not in GNUstep. The other hack is to
fiddle with the link order so that the constructor above can cover up
the issue. 

I'll leave it up to the -make maintainers to decide what to do. My
current tendency is to close this bug as invalid at least until someone
verifies that the issue exists with released versions of FreeBSD. And
even then a workaround should probably be done in libobjc (which seems
to be installed by FreeBSD but if libobjc needs updating you might as
well update libpthread instead and fix the bug properly).
----[ End of Analysis]----

On a sidenote .. this same problem is duplicated when using libthr ...
and does not occur when using libc_r.


the proposed fixes seem to cause no ill-effects (i've been running with
them locally for a full month by now ...

if a similar fix could be applied inside _get_curthread()  that would like
be the most ideal situation .. but i would rather have somebody confirm such a solution would indeed be possible there.
>How-To-Repeat:
The problem manifests itself when an application linked against
libpthread/libthr indirectly links in libobjc.

During application startup .. libobjc apparently gets to run its initialization
routines before libpthread gets a chance to execute its 
_thread_init_hack constructor .. causing NULL pointer dereferences in
pthread_key_create()
>Fix:
--- libthr.diff begins here ---
--- lib/libthr/thread/thr_spec.c.orig	Sat Jul 16 19:29:04 2005
+++ lib/libthr/thread/thr_spec.c	Sat Jul 16 19:29:14 2005
@@ -52,8 +52,13 @@
 int
 _pthread_key_create(pthread_key_t *key, void (*destructor) (void *))
 {
-	struct pthread *curthread = _get_curthread();
+	struct pthread *curthread;
 	int i;
+
+	if (_thr_initial == NULL)
+		_libpthread_init(NULL);
+
+	curthread = _get_curthread();
 
 	/* Lock the key table: */
 	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
--- libthr.diff ends here ---
--- libpthread.diff begins here ---
--- lib/libpthread/thread/thr_spec.c.orig	Sat Jul 16 19:28:31 2005
+++ lib/libpthread/thread/thr_spec.c	Sat Jul 16 19:28:41 2005
@@ -50,8 +50,13 @@
 int
 _pthread_key_create(pthread_key_t *key, void (*destructor) (void *))
 {
-	struct pthread *curthread = _get_curthread();
+	struct pthread *curthread;
 	int i;
+
+	if (_thr_initial == NULL)
+		_libpthread_init(NULL);
+
+	curthread = _get_curthread();
 
 	/* Lock the key table: */
 	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
--- libpthread.diff ends here ---

>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200508201632.j7KGWd9B026955>