[Date Prev][Date Next] [Chronological] [Thread] [Top]

(ITS#8450) race condition in ldap_int_utils_init



Full_Name: Doug Leavitt
Version: 2.4.44
OS: Solaris
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (137.254.4.13)


There is a race condition in ldap_int_utils_init that can be triggered when
multiple threads enter ldap_int_utils_init from ldap_init_initialize about the
same time. The done flag gets set immediately, before the various mutexes are
initialized. If thread A sets done, and thread B tests for done==1 before thread
A has completed the mutex inits, thread B can attempt to use an uninitialized
mutex and fail/core dump etc.

Additionally if judt the done=1 is moved to the bottom of the function thwo
threads can both be initializing the same mutexes multiple times causes other
mayhem.

The short term workaround for Solaris (THR APIs) is to move setting of done=1 to
after the mutex inits, and to protect the mutex inits using another statically
initialized mutex within ldap_int_utils_init.

Example patch:

-- util-int.c.~1~	2016-02-05 17:57:45.000000000 -0600
+++ util-int.c	2016-06-24 13:20:09.793308570 -0500
@@ -89,12 +89,16 @@
 # endif
 # if defined(HAVE_GETHOSTBYADDR_R) && \
 	(GETHOSTBDDDDR_R_NARGS < 7) || (8 < GETHOSTBYADDR_R_NARGS)
 	/* Don't know how to handle this version, pretend it's not there */
 #	undef HAVE_GETHOSTBYADDR_R
 # endif
+#if defined( HAVE_THR )
+	/* use static mutex to protect mutex initialization */
+static mutex_t ldap_int_utils_init_mutex = DEFAULTMUTEX;
+#endif /* HAVE_THR */
 #endif /* LDAP_R_COMPILE */
 
 char *ldap_pvt_ctime( const time_t *tp, char *buf )
 {
 #ifdef USE_CTIME_R
 # if (CTIME_R_NARGS > 3) || (CTIME_R_NARGS < 2)
@@69697,15 +701,22 @@
 
 void ldap_int_utils_init( void )
 {
 	static int done=0;
 	if (done)
 	  return;
-	done=1;
 
 #ifdef LDAP_R_COMPILE
+#if defined( HAVE_THR )
+	/* use static mutex to protect mutex initialization */
+	(void) mutex_lock(&ldap_int_utils_init_mutex);
+	if (done) {
+		(void) mutex_unlock(&ldap_int_utils_init_mutex);
+		return;
+	}
+#endif /* HAVE_THR */
 #if !defined( USE_CTIME_R ) && !defined( HAVE_REENTRANT_FUNCTIONS )
 	ldap_pvt_thread_mutex_init( &ldap_int_ctime_mutex );
 #endif
 #if !defined( USE_GMTIME_R ) && !defined( USE_LOCALTIME_R )
 	ldap_pvt_thread_mutex_init( &ldap_int_gmtime_mutex );
 #endif
@@ -715,15 +726,20 @@
 
 	ldap_pvt_thread_mutex_init( &ldap_int_gettime_mutex );
 
 #ifdef HAVE_GSSAPI
 	ldap_pvt_thread_mutex_init( &ldap_int_gssapi_mutex );
 #endif
-#endif
 
 	/* call other module init functions here... */
+	done=1;
+#if defined( HAVE_THR )
+	/* use static mutex to protect mutex initialization */
+	(void) mutex_unlock(&ldap_int_utils_init_mutex);
+#endif /* HAVE_THR */
+#endif
 }
 
 #if defined( NEED_COPY_HOSTENT )
 # undef NEED_SAFE_REALLOC
 #define NEED_SAFE_REALLOC
 

I know a similar workaround could be made for POSIX threads and possibly some of
the other supported thread models, but this approach seems kludgely.

I suspect the correct solution may be to somehow refactor ldap_int_utils_init
out of libldap and into libldap_r in a cleaner multi-platform manner than the
example above.