Subject: lib/37524: libpthread crash trying to recursively lock statically inited mutex
To: None <lib-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <rafal@netbsd.org>
List: netbsd-bugs
Date: 12/12/2007 08:10:01
>Number:         37524
>Category:       lib
>Synopsis:       libpthread crash trying to recursively lock statically inited mutex while pthread__started == 0
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Dec 12 08:10:01 +0000 2007
>Originator:     Rafal Boni
>Release:        NetBSD 4.99.40
>Organization:
Whazzat?
>Environment:
System: NetBSD fearless-vampire-killer 4.99.40 NetBSD 4.99.40 (GENERIC) #0: Sat Dec 8 09:29:00 EST 2007 rafal@fearless-vampire-killer:/home/sparc64/obj/sys/arch/sparc64/compile/GENERIC sparc64
Architecture: sparc64
Machine: sparc64
>Description:
	Trying to recursively lock a statically-initialized pthread mutex
	causes libpthread to die deref'ing a NULL pointer.  I know I should
	not do that, but it's not my app I was debugging ;)

	libpthead actually crashes evaluating the condition for a diag assert,
	so we should fix it so the assert actually fires and at least gives
	a clue to the user/caller.

	[The crash only happens when pthread__started == 0, but it's a bug
	 all the same...]
	
	As a side note, it looks like our mutexes are really only mostly-
	initialized by the static initializers at least on archs that use
	pthread_mutex.c vs. pthread_mutex2.c; that's kind-of a shame...
	maybe libpthread should finish the initialization of statically
	init'ed mutexes?  Ie, supply the default 'private' structure for
	them...

>How-To-Repeat:

Script started on Wed Dec 12 01:29:17 2007
$ cat ./pthread-test.c
#include <stdio.h>
#include <pthread.h>
#include <errno.h>

pthread_mutex_t my_mtx = PTHREAD_MUTEX_INITIALIZER;

main()
{
	int rc;

	rc = pthread_mutex_lock(&my_mtx);
	printf("1st lock returned %d (errno %d)\n", rc, errno);
	rc = pthread_mutex_lock(&my_mtx);
	printf("2nd lock returned %d (errno %d)\n", rc, errno);
	rc = pthread_mutex_unlock(&my_mtx);
	printf("1st unlock returned %d (errno %d)\n", rc, errno);
	rc = pthread_mutex_unlock(&my_mtx);
	printf("2nd unlock returned %d (errno %d)\n", rc, errno);
}

$ cc ./pthread-test.c -lpthread
$ ./a.out
1st lock returned 0 (errno 0)
[1]   Segmentation fault (core dumped) ./a.out
$ gdb ./a.out ./a.out.core
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "sparc64--netbsd"...(no debugging symbols found)

Reading symbols from /usr/lib/libpthread.so.0...(no debugging symbols found)...done.
Loaded symbols for /usr/lib/libpthread.so.0
Reading symbols from /usr/lib/libc.so.12...(no debugging symbols found)...done.
Loaded symbols for /usr/lib/libc.so.12
Reading symbols from /usr/libexec/ld.elf_so...
(no debugging symbols found)...done.
Loaded symbols for /usr/libexec/ld.elf_so
Core was generated by `a.out'.
Program terminated with signal 11, Segmentation fault.
#0  0x00000000404078b0 in pthread_mutex_lock () from /usr/lib/libpthread.so.0
(gdb) where
#0  0x00000000404078b0 in pthread_mutex_lock () from /usr/lib/libpthread.so.0
#1  0x0000000000100c40 in main ()
(gdb) x/i $pc
0x404078b0 <pthread_mutex_lock+624>:	ld  [ %l5 ], %g1
(gdb) p/x $l5
$1 = 0x0
(gdb) q
$ ^D
Script done on Wed Dec 12 01:30:00 2007
>Fix:

Index: pthread_mutex.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_mutex.c,v
retrieving revision 1.38
diff -u -r1.38 pthread_mutex.c
--- pthread_mutex.c	19 Nov 2007 15:14:13 -0000	1.38
+++ pthread_mutex.c	12 Dec 2007 06:40:30 -0000
@@ -239,7 +239,8 @@
 
 		if (pthread__started == 0) {
 			/* The spec says we must deadlock, so... */
-			pthread__assert(mp->type == PTHREAD_MUTEX_NORMAL);
+			pthread__assert(mp != NULL && 
+					mp->type == PTHREAD_MUTEX_NORMAL);
 			(void) sigprocmask(SIG_SETMASK, NULL, &ss);
 			for (;;) {
 				sigsuspend(&ss);

After fix, the same test gives the following result:

Script started on Wed Dec 12 01:33:43 2007
$ ./a.out
1st lock returned 0 (errno 0)
assertion "mp != NULL && mp->type == PTHREAD_MUTEX_NORMAL" failed: file "/home/src-current/lib/libpthread/pthread_mutex.c", line 243, function "pthread_mutex_lock_slow"
[1]   Abort trap (core dumped) ./a.out
$ ^D
Script done on Wed Dec 12 01:34:08 2007