]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
Avoid a deadlock with client
authorOndřej Kuzník <ondra@mistotebe.net>
Mon, 3 Jul 2017 09:41:37 +0000 (10:41 +0100)
committerOndřej Kuzník <okuznik@symas.com>
Tue, 17 Nov 2020 17:55:46 +0000 (17:55 +0000)
servers/lloadd/client.c

index b742081a4d337efc482566767851b2067c863c8e..5c589a3020176af16dd6a5a08ae10f3832610bbe 100644 (file)
@@ -358,6 +358,7 @@ void
 client_destroy( Connection *c )
 {
     enum sc_state state;
+    struct event *read_event, *write_event;
 
     Debug( LDAP_DEBUG_CONNS, "client_destroy: "
             "destroying client connid=%lu\n",
@@ -367,14 +368,27 @@ client_destroy( Connection *c )
     state = c->c_state;
     c->c_state = SLAP_C_INVALID;
 
-    if ( c->c_read_event ) {
-        event_free( c->c_read_event );
-        c->c_read_event = NULL;
+    read_event = c->c_read_event;
+    write_event = c->c_write_event;
+
+    /*
+     * FIXME: operation_destroy_from_upstream might copy op->o_client and bump
+     * c_refcnt, it is then responsible to call destroy_client again, does that
+     * mean that we can be triggered for recursion over all connections?
+     */
+    CONNECTION_UNLOCK_INCREF(c);
+
+    /*
+     * Avoid a deadlock:
+     * event_del will block if the event is currently executing its callback,
+     * that callback might be waiting to lock c->c_mutex
+     */
+    if ( read_event ) {
+        event_del( read_event );
     }
 
-    if ( c->c_write_event ) {
-        event_free( c->c_write_event );
-        c->c_write_event = NULL;
+    if ( write_event ) {
+        event_del( write_event );
     }
 
     if ( state != SLAP_C_CLOSING ) {
@@ -383,6 +397,18 @@ client_destroy( Connection *c )
         ldap_pvt_thread_mutex_unlock( &clients_mutex );
     }
 
+    CONNECTION_LOCK_DECREF(c);
+
+    if ( c->c_read_event ) {
+        event_free( c->c_read_event );
+        c->c_read_event = NULL;
+    }
+
+    if ( c->c_write_event ) {
+        event_free( c->c_write_event );
+        c->c_write_event = NULL;
+    }
+
     client_reset( c );
 
     /*