]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#10218 Disabling and re-enabling an asyncmeta database via cn=config leaks memory
authorNadezhda Ivanova <nivanova@symas.com>
Thu, 23 May 2024 12:54:04 +0000 (15:54 +0300)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 13 Jun 2024 17:38:06 +0000 (17:38 +0000)
Make sure asyncmeta frees the pending operations structures, resets all connections, frees connection structures and stops the timeout-loop.

servers/slapd/back-asyncmeta/back-asyncmeta.h
servers/slapd/back-asyncmeta/init.c
servers/slapd/back-asyncmeta/meta_result.c

index 038d2fa90ab25876dec883d2b20a41869ff1c587..dafc040f64ee3553b7be3256fec7a8a71f89bc8d 100644 (file)
@@ -427,6 +427,7 @@ typedef struct a_metainfo_t {
        a_metaconn_t          *mi_conns;
 
        struct berval           mi_suffix;
+       volatile int          mi_disabled;
 } a_metainfo_t;
 
 typedef enum meta_op_type {
@@ -780,6 +781,9 @@ asyncmeta_db_has_mscs(a_metainfo_t *mi);
 void
 asyncmeta_target_free(a_metatarget_t *mt);
 
+void
+asyncmeta_back_clear_miconns(a_metainfo_t *mi);
+
 /* The the maximum time in seconds after a result has been received on a connection,
  * after which it can be reset if a sender error occurs. Should this be configurable? */
 #define META_BACK_RESULT_INTERVAL (2)
index def44947450e3788c612ac700bcc8e8790032e09..ba95f60735dc41efc5edd4a864ea0f36d307673c 100644 (file)
@@ -240,13 +240,13 @@ asyncmeta_back_db_open(
        char msg[SLAP_TEXT_BUFLEN];
        int             i;
 
+       mi->mi_disabled = 0;
        if ( mi->mi_ntargets == 0 ) {
 
                Debug( LDAP_DEBUG_ANY,
                        "asyncmeta_back_db_open: no targets defined\n" );
        }
 
-       mi->mi_num_conns = 0;
        for ( i = 0; i < mi->mi_ntargets; i++ ) {
                a_metatarget_t  *mt = mi->mi_targets[ i ];
                if ( asyncmeta_target_finish( mi, mt,
@@ -255,31 +255,34 @@ asyncmeta_back_db_open(
                }
        }
 
-       mi->mi_num_conns = (mi->mi_max_target_conns == 0) ? META_BACK_CFG_MAX_TARGET_CONNS : mi->mi_max_target_conns;
-       assert(mi->mi_num_conns > 0);
-       mi->mi_conns = ch_calloc( mi->mi_num_conns, sizeof( a_metaconn_t ));
-       for (i = 0; i < mi->mi_num_conns; i++) {
-               a_metaconn_t *mc = &mi->mi_conns[i];
-               ldap_pvt_thread_mutex_init( &mc->mc_om_mutex);
-               mc->mc_authz_target = META_BOUND_NONE;
+       if ( mi->mi_conns == NULL ) {
+               mi->mi_num_conns = (mi->mi_max_target_conns == 0) ? META_BACK_CFG_MAX_TARGET_CONNS : mi->mi_max_target_conns;
+               assert(mi->mi_num_conns > 0);
+               mi->mi_conns = ch_calloc( mi->mi_num_conns, sizeof( a_metaconn_t ));
+               for (i = 0; i < mi->mi_num_conns; i++) {
+                       a_metaconn_t *mc = &mi->mi_conns[i];
+                       ldap_pvt_thread_mutex_init( &mc->mc_om_mutex);
+                       mc->mc_authz_target = META_BOUND_NONE;
 
-               if ( mi->mi_ntargets > 0 ) {
-                       mc->mc_conns = ch_calloc( mi->mi_ntargets, sizeof( a_metasingleconn_t ));
-               } else {
-                       mc->mc_conns = NULL;
-               }
+                       if ( mi->mi_ntargets > 0 ) {
+                               mc->mc_conns = ch_calloc( mi->mi_ntargets, sizeof( a_metasingleconn_t ));
+                       } else {
+                               mc->mc_conns = NULL;
+                       }
 
-               mc->mc_info = mi;
-               LDAP_STAILQ_INIT( &mc->mc_om_list );
-       }
+                       mc->mc_info = mi;
+                       LDAP_STAILQ_INIT( &mc->mc_om_list );
+               }
        
-       ber_dupbv ( &mi->mi_suffix, &be->be_suffix[0] );
 
-       if ( ( slapMode & SLAP_SERVER_MODE ) && mi->mi_ntargets > 0 ) {
-               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
-               mi->mi_task = ldap_pvt_runqueue_insert( &slapd_rq, 1,
-                                                       asyncmeta_timeout_loop, mi, "asyncmeta_timeout_loop", mi->mi_suffix.bv_val );
-               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+               ber_dupbv ( &mi->mi_suffix, &be->be_suffix[0] );
+
+               if ( ( slapMode & SLAP_SERVER_MODE ) && mi->mi_ntargets > 0 ) {
+                       ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+                       mi->mi_task = ldap_pvt_runqueue_insert( &slapd_rq, 1,
+                                                                                                       asyncmeta_timeout_loop, mi, "asyncmeta_timeout_loop", mi->mi_suffix.bv_val );
+                       ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+               }
        }
        return 0;
 }
@@ -301,28 +304,26 @@ asyncmeta_back_conn_free(
        free( mc );
 }
 
-static void
-asyncmeta_back_stop_miconns( a_metainfo_t *mi )
-{
-
-       /*Todo do any other mc cleanup here if necessary*/
-}
-
-static void
+void
 asyncmeta_back_clear_miconns( a_metainfo_t *mi )
 {
        int i, j;
        a_metaconn_t *mc;
-       for (i = 0; i < mi->mi_num_conns; i++) {
-               mc = &mi->mi_conns[i];
-               /* todo clear the message queue */
-               for (j = 0; j < mi->mi_ntargets; j ++) {
-                       asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
+       if ( mi->mi_conns != NULL ) {
+               for (i = 0; i < mi->mi_num_conns; i++) {
+                       mc = &mi->mi_conns[i];
+                       for (j = 0; j < mi->mi_ntargets; j ++) {
+                               asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
+                       }
+
+                       if ( mc->mc_conns )
+                               free( mc->mc_conns );
+                       ldap_pvt_thread_mutex_destroy( &mc->mc_om_mutex );
                }
-               free(mc->mc_conns);
-               ldap_pvt_thread_mutex_destroy( &mc->mc_om_mutex );
+               free(mi->mi_conns);
        }
-       free(mi->mi_conns);
+       mi->mi_conns = NULL;
+       mi->mi_num_conns = 0;
 }
 
 void
@@ -392,17 +393,22 @@ asyncmeta_back_db_close(
 
        if ( be->be_private ) {
                mi = ( a_metainfo_t * )be->be_private;
-               if ( mi->mi_task != NULL ) {
-                       ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
-                       if ( ldap_pvt_runqueue_isrunning( &slapd_rq, mi->mi_task )) {
-                               ldap_pvt_runqueue_stoptask( &slapd_rq,  mi->mi_task);
-                       }
-                       ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
-                       mi->mi_task = NULL;
+               mi->mi_disabled = 1;
+               /* there are no pending ops so we can free up the connections and stop the timeout loop
+                * else timeout_loop will clear up the ops and connections and not reschedule */
+               if ( asyncmeta_db_has_pending_ops( mi ) == 0 ) {
+                               ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
+                               asyncmeta_back_clear_miconns(mi);
+                               ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
+                               if ( mi->mi_task != NULL ) {
+                                       ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+                                       if ( ldap_pvt_runqueue_isrunning( &slapd_rq, mi->mi_task )) {
+                                               ldap_pvt_runqueue_stoptask( &slapd_rq,  mi->mi_task);
+                                       }
+                                       ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+                                       mi->mi_task = NULL;
+                               }
                }
-               ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
-               asyncmeta_back_stop_miconns( mi );
-               ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
        }
        return 0;
 }
index 80a02c8c2d43ed6b4678481e4116e58f63d74e32..fd5b52f41ad26af889981846a0a1ace007b82bcc 100644 (file)
@@ -1487,6 +1487,11 @@ asyncmeta_op_handle_result(void *ctx, void *arg)
        bm_context_t *bc;
        void *oldctx;
 
+/* exit if the database is disabled, this will let timeout_loop
+ * do it's job faster */
+       if ( mc->mc_info->mi_disabled > 0 )
+               return NULL;
+
        ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
        rc = ++mc->mc_active;
        ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
@@ -1661,6 +1666,14 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
        LDAP_STAILQ_INIT( &timeout_list );
 
        Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time );
+       if ( mi->mi_disabled > 0 && asyncmeta_db_has_pending_ops( mi ) == 0 ) {
+               Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] database disabled, clearing connections [%ld] \n", rtask, current_time );
+               ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
+               asyncmeta_back_clear_miconns( mi );
+               mi->mi_task = NULL;
+               ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
+               return NULL;
+       }
        void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);
        for (i=0; i<mi->mi_num_conns; i++) {
                a_metaconn_t * mc= &mi->mi_conns[i];
@@ -1671,6 +1684,10 @@ void* asyncmeta_timeout_loop(void *ctx, void *arg)
                                continue;
                        }
 
+                       if (mi->mi_disabled > 0) {
+                               bc->bc_invalid = 1;
+                       }
+
                        if (bc->op->o_abandon ) {
                                Operation *op = bc->op;