Full_Name: Lorenz Bauer
Version: git c367c1f69685
OS: OS X
URL: https://gist.github.com/lmb/c48dcdb74b4bc9bf4ecae1d70553d623
Submission from: (NULL) (2a06:98c0:1000:1200:1160:b579:2042:d902)
In case that mdb_txn_begin fails (e.g. due to no more reader slots available),
mdb_env_copyfd1 leaks my.mc_wbuf[0]. From the pthread_create man pages, it seems
like it also leaks pthread state, since the created thread is never joined.
The linked gist contains a test case, valgrind output which confirms that the
buffer is leaked, and a patch.
The patch properly frees the buffer, and always joins the thread. If the thread
was never created pthread_join simply returns an error, which is ignored
anyways. I think the same logic holds on Windows.
Full_Name: Emmanuel Lecharny
Version: 2.4.44
OS:
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (78.235.65.101)
The current slapo-auditlog could be improve a bit by providing some more context
about the audit log file content. Typically, the first like contains important
informations like :
- the operation
- the timestamp
- the database suffix
- the modifiers' name
- and the connection ID
When this connection ID is -1, it would be interesting in the man page to inform
the users that this update is an internal update (ie, something caused by an
internal slapd update, not an user update).
A simple sentence could be : "A 'conn=-1' in the first line tells that this is
an internal operation, not an user initiated operation".
Full_Name: Lorenz Bauer
Version: git c367c1f69685
OS: OS X
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (2a06:98c0:1000:1200:156f:df88:ee9a:7775)
The function mdb_env_copyfd2 with MDB_CP_COMPACT enabled can currently deadlock,
since a call to mdb_env_cthr_toggle is not checked.
The testcase at https://gist.github.com/lmb/17a528cdadde73fb231a2a1ed84714f7
reproduces this issue as follows:
- Use pthread_sigmask to ignore SIGPIPE
- Create new pipe
- Spawn thread with mdb_env_copyfd2 using MDB_CP_COMPACT
- Close read side of the pipe in main thread
- Join thread and check rc code
On LMDB master the testase e hangs instead of returning EPIPE.
The following patch fixes the deadlock, but since I'm not very familiar with the
codebase it's likely incorrect.
diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c
index 79a958b..97ff7c7 100644
--- a/libraries/liblmdb/mdb.c
+++ b/libraries/liblmdb/mdb.c
@@ -9988,11 +9988,15 @@ mdb_env_copyfd1(MDB_env *env, HANDLE fd)
rc = mdb_env_cwalk(&my, &txn->mt_dbs[MAIN_DBI].md_root, 0);
if (rc == MDB_SUCCESS && my.mc_wlen[my.mc_toggle])
rc = mdb_env_cthr_toggle(&my, 1);
- mdb_env_cthr_toggle(&my, -1);
+ rc = mdb_env_cthr_toggle(&my, -1);
+ if (rc)
+ goto done;
pthread_mutex_lock(&my.mc_mutex);
while(my.mc_new)
pthread_cond_wait(&my.mc_cond, &my.mc_mutex);
pthread_mutex_uocock(&my.mc_mutex);
+
+done:
THREAD_FINISH(thr);
mdb_txn_abort(txn);
doug.leavitt(a)oracle.com wrote:
> 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.
> 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.
Static initializers would be the simplest fix, certainly. On Windows we'd have
to use something similar to pthread_once() for initialization, instead.
> 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.
>
>
--
-- Howard Chu
CTO, Symas Corp. http://www.symas.com
Director, Highland Sun http://highlandsun.com/hyc/
Chief Architect, OpenLDAP http://www.openldap.org/project/
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.