]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Explicitly cleanup "thread local" memory in single threaded mode
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 19 Feb 2022 04:50:01 +0000 (23:50 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 22 Feb 2022 20:50:41 +0000 (15:50 -0500)
src/bin/radiusd.c
src/bin/unit_test_attribute.c
src/bin/unit_test_map.c
src/bin/unit_test_module.c
src/lib/tls/log.c
src/lib/util/atexit.c
src/lib/util/atexit.h
src/lib/util/talloc.c
src/lib/util/talloc.h

index f0e291e765ef423db0041ffd5567e3b18333283e..7dcfd914d2d40ec01bd34c4b036a16011d086038 100644 (file)
@@ -1021,7 +1021,7 @@ int main(int argc, char *argv[])
                fr_event_loop_exit(el, 1);
        }
 
-       main_loop_free();               /* Free the requests */
+       main_loop_free();
 
        /*
         *  Send a TERM signal to all associated processes
@@ -1051,6 +1051,16 @@ cleanup:
         */
        (void) fr_schedule_destroy(&sc);
 
+       /*
+        *      Ensure all thread local memory is cleaned up
+        *      before we start cleaning up global resources.
+        *      This is necessay for single threaded mode
+        *      to ensure that thread local resources that
+        *      depend on global resources are freed at the
+        *      appropriate time.
+        */
+       fr_atexit_thread_trigger_all();
+
        /*
         *  Frees request specific logging resources which is OK
         *  because all the requests will have been stopped.
index 0232aa4bfdcb1c5d33f477763b4410291670b788..f7aac270d502d38f18b5b1b9573dd80e57ca55cb 100644 (file)
@@ -3693,6 +3693,14 @@ int main(int argc, char *argv[])
         *      memory, so we get clean talloc reports.
         */
 cleanup:
+       /*
+        *      Ensure all thread local memory is cleaned up
+        *      at the appropriate time.  This emulates what's
+        *      done with worker/network threads in the
+        *      scheduler.
+        */
+       fr_atexit_thread_trigger_all();
+
 #ifdef WITH_TLS
        fr_openssl_free();
 #endif
index 82df15ec0b4077af9f7069b98df4f60f64cab99d..1da278134551c3174e63d2fe46cc91c0c5a4faa3 100644 (file)
@@ -272,6 +272,14 @@ int main(int argc, char *argv[])
        if (ret < 0) ret = 1; /* internal to Unix process return code */
 
 cleanup:
+       /*
+        *      Ensure all thread local memory is cleaned up
+        *      at the appropriate time.  This emulates what's
+        *      done with worker/network threads in the
+        *      scheduler.
+        */
+       fr_atexit_thread_trigger_all();
+
        /*
         *      Try really hard to free any allocated
         *      memory, so we get clean talloc reports.
index 6d6dafb921f50bf30eaa2a908acd94d1209e52ac..e8e3c699525d5cffa439666d6567520c057f7ed7 100644 (file)
@@ -1004,6 +1004,14 @@ cleanup:
         */
        talloc_free(el);
 
+       /*
+        *      Ensure all thread local memory is cleaned up
+        *      at the appropriate time.  This emulates what's
+        *      done with worker/network threads in the
+        *      scheduler.
+        */
+       fr_atexit_thread_trigger_all();
+
        /*
         *      Free request specific logging infrastructure
         */
index e25d765eeec5d28d4d98a8e916a4c0f9732dbf3b..d26be319a3beea192e2715d42804c73144cf10ae 100644 (file)
@@ -31,6 +31,7 @@ USES_APPLE_DEPRECATED_API     /* OpenSSL API has been deprecated by Apple */
 
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/atexit.h>
+#include <stdatomic.h>
 
 #include "log.h"
 #include "utils.h"
@@ -78,6 +79,16 @@ static BIO_METHOD    *tls_request_log_meth;
  */
 static BIO_METHOD      *tls_global_log_meth;
 
+/** Counter for users of the request log bio
+ *
+ */
+static _Atomic(uint32_t) tls_request_log_ref;
+
+/** Counter for users of the global log bio
+ *
+ */
+static _Atomic(uint32_t) tls_global_log_ref;
+
 /** Thread local request log BIO
  */
 static _Thread_local   fr_tls_log_bio_t        *request_log_bio;
@@ -541,6 +552,15 @@ void tls_log_clear(void)
        while (ERR_get_error() != 0);
 }
 
+/** Increment the bio meth reference counter
+ *
+ */
+static int tls_log_request_bio_create_cb(UNUSED BIO *bio)
+{
+       atomic_fetch_add(&tls_request_log_ref, 1);
+       return 1;
+}
+
 /** Converts BIO_write() calls to request log calls
  *
  * This callback is used to glue the output of OpenSSL functions into request log calls.
@@ -615,6 +635,24 @@ static int tls_log_request_bio_puts_cb(BIO *bio, char const *in)
        return tls_log_request_bio_write_cb(bio, in, strlen(in));
 }
 
+/** Decrement the bio meth reference counter
+ *
+ */
+static int tls_log_request_bio_free_cb(UNUSED BIO *bio)
+{
+       atomic_fetch_sub(&tls_request_log_ref, 1);
+       return 1;
+}
+
+/** Increment the bio meth reference counter
+ *
+ */
+static int tls_log_global_bio_create_cb(UNUSED BIO *bio)
+{
+       atomic_fetch_add(&tls_global_log_ref, 1);
+       return 1;
+}
+
 /** Converts BIO_write() calls to global log calls
  *
  * This callback is used to glue the output of OpenSSL functions into global log calls.
@@ -679,6 +717,15 @@ static int tls_log_global_bio_puts_cb(BIO *bio, char const *in)
        return tls_log_global_bio_write_cb(bio, in, strlen(in));
 }
 
+/** Decrement the bio meth reference counter
+ *
+ */
+static int tls_log_global_bio_free_cb(UNUSED BIO *bio)
+{
+       atomic_fetch_sub(&tls_global_log_ref, 1);
+       return 1;
+}
+
 /** Frees a logging bio and its underlying OpenSSL BIO *
  *
  */
@@ -802,8 +849,10 @@ int fr_tls_log_init(void)
        tls_request_log_meth = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "fr_tls_request_log");
        if (unlikely(!tls_request_log_meth)) return -1;
 
+       BIO_meth_set_create(tls_request_log_meth, tls_log_request_bio_create_cb);
        BIO_meth_set_write(tls_request_log_meth, tls_log_request_bio_write_cb);
        BIO_meth_set_puts(tls_request_log_meth, tls_log_request_bio_puts_cb);
+       BIO_meth_set_destroy(tls_request_log_meth, tls_log_request_bio_free_cb);
 
        tls_global_log_meth = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "fr_tls_global_log");
        if (unlikely(!tls_global_log_meth)) {
@@ -812,8 +861,10 @@ int fr_tls_log_init(void)
                return -1;
        }
 
+       BIO_meth_set_create(tls_global_log_meth, tls_log_global_bio_create_cb);
        BIO_meth_set_write(tls_global_log_meth, tls_log_global_bio_write_cb);
        BIO_meth_set_puts(tls_global_log_meth, tls_log_global_bio_puts_cb);
+       BIO_meth_set_destroy(tls_global_log_meth, tls_log_global_bio_free_cb);
 
        return 0;
 }
@@ -823,6 +874,14 @@ int fr_tls_log_init(void)
  */
 void fr_tls_log_free(void)
 {
+       /*
+        *      These must be freed first else
+        *      we get crashes in the OpenSSL
+        *      code when we try to free them.
+        */
+       fr_assert_msg(atomic_load(&tls_request_log_ref) == 0, "request log BIO refs remaining %u", atomic_load(&tls_request_log_ref));
+       fr_assert_msg(atomic_load(&tls_global_log_ref) == 0, "global log BIO refs remaining %u", atomic_load(&tls_global_log_ref));
+
        if (tls_request_log_meth) {
                BIO_meth_free(tls_request_log_meth);
                tls_request_log_meth = NULL;
index 689605bcf439c28f8ed3ad4fcb3f082d20fe335e..ea8acd8939ed9a6003e9c2bacddf4eb174a67f55 100644 (file)
@@ -469,3 +469,42 @@ do_threads:
 
        return count;
 }
+
+/** Cause all thread local free triggers to fire
+ *
+ * This is necessary when we're running in single threaded mode
+ * to ensure all "thread-local" memory (which isn't actually thread local)
+ * is cleaned up.
+ *
+ * One example is the OpenSSL log BIOs which must be cleaned up
+ * before fr_openssl_free is called.
+ */
+unsigned int fr_atexit_thread_trigger_all(void)
+{
+       fr_atexit_entry_t               *e = NULL, *ee;
+       fr_atexit_list_t                *list;
+       unsigned int                    count = 0;
+
+       /*
+        *      Iterate over the list of thread local
+        *      destructor lists running the
+        *      destructors.
+        */
+       while ((e = fr_dlist_next(&fr_atexit_threads->head, e))) {
+               if (!e->func) continue; /* thread already joined */
+
+               list = talloc_get_type_abort(e->uctx, fr_atexit_list_t);
+               ee = NULL;
+               while ((ee = fr_dlist_next(&list->head, ee))) {
+                       ATEXIT_DEBUG("%s - Thread %u triggering %p/%p func=%p, uctx=%p (alloced %s:%u)",
+                                    __FUNCTION__,
+                                    (unsigned int)pthread_self(),
+                                    list, ee, ee->func, ee->uctx, ee->file, ee->line);
+
+                       count++;
+                       ee = fr_dlist_talloc_free_item(&list->head, ee);
+               }
+       }
+
+       return count;
+}
index 2f329c67f76a30018cc774954e0598f92a4108e4..ae013197074b8292896b0252bdd2af57cbe9c2b9 100644 (file)
@@ -123,6 +123,8 @@ void                fr_atexit_global_disarm_all(void);
 
 unsigned int   fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx);
 
+unsigned int   fr_atexit_thread_trigger_all(void);
+
 #ifdef __cplusplus
 }
 #endif
index 6dba9ed25d6ac521adb3d4aced94629d46516932..947a97d1f804a02b0d3fd3ee24fe7a2800079266 100644 (file)
@@ -28,7 +28,7 @@ RCSID("$Id$")
 #include <freeradius-devel/util/strerror.h>
 #include <freeradius-devel/util/atexit.h>
 
-
+static TALLOC_CTX *global_ctx;
 static _Thread_local TALLOC_CTX *thread_local_ctx;
 
 /** A wrapper that can be passed to tree or hash alloc functions that take a #fr_free_t
@@ -761,10 +761,10 @@ void **talloc_array_null_strip(void **array)
        return new;
 }
 
-/** Callback to free the autofree ctx on thread exit
+/** Callback to free the autofree ctx on global exit
  *
  */
-static void _autofree_on_thread_exit(void *af)
+static void _autofree_on_exit(void *af)
 {
        talloc_set_destructor(af, NULL);
        talloc_free(af);
@@ -773,9 +773,33 @@ static void _autofree_on_thread_exit(void *af)
 /** Ensures in the autofree ctx is manually freed, things don't explode atexit
  *
  */
-static int _autofree_destructor(TALLOC_CTX *af)
+static int _autofree_global_destructor(TALLOC_CTX *af)
+{
+       return fr_atexit_global_disarm(true, _autofree_on_exit, af);
+}
+
+TALLOC_CTX *talloc_autofree_context_global(void)
+{
+       TALLOC_CTX *af = global_ctx;
+
+       if (!af) {
+               af = talloc_init_const("global_autofree_context");
+               talloc_set_destructor(af, _autofree_global_destructor);
+               if (unlikely(!af)) return NULL;
+
+               fr_atexit_global(_autofree_on_exit, af);
+               global_ctx = af;
+       }
+
+       return af;
+}
+
+/** Ensures in the autofree ctx is manually freed, things don't explode atexit
+ *
+ */
+static int _autofree_thread_local_destructor(TALLOC_CTX *af)
 {
-       return fr_atexit_thread_local_disarm(true, _autofree_on_thread_exit, af);
+       return fr_atexit_thread_local_disarm(true, _autofree_on_exit, af);
 }
 
 /** Get a thread-safe autofreed ctx that will be freed when the thread or process exits
@@ -791,15 +815,16 @@ TALLOC_CTX *talloc_autofree_context_thread_local(void)
 
        if (!af) {
                af = talloc_init_const("thread_local_autofree_context");
-               talloc_set_destructor(af, _autofree_destructor);
+               talloc_set_destructor(af, _autofree_thread_local_destructor);
                if (unlikely(!af)) return NULL;
 
-               fr_atexit_thread_local(thread_local_ctx, _autofree_on_thread_exit, af);
+               fr_atexit_thread_local(thread_local_ctx, _autofree_on_exit, af);
        }
 
        return af;
 }
 
+
 struct talloc_child_ctx_s {
        struct talloc_child_ctx_s *next;
 };
index 65691c2033304fd5afefce9d7b67b07dc54fbe09..48c4855b305a1f53f6f770b497ba8f18ccfb5dd1 100644 (file)
@@ -46,7 +46,7 @@ DIAG_ON(documentation)
 #undef talloc_autofree_context
 /** The original function is deprecated, so replace it with our version
  */
-#define talloc_autofree_context talloc_autofree_context_thread_local
+#define talloc_autofree_context talloc_autofree_context_global
 
 /** Iterate over a talloced array of elements
  *
@@ -263,6 +263,7 @@ static inline void *_talloc_list_get_type_abort(void *head, size_t offset, char
 #  define talloc_get_type_abort_const talloc_get_type_abort
 #endif
 
+TALLOC_CTX             *talloc_autofree_context_global(void);
 TALLOC_CTX             *talloc_autofree_context_thread_local(void);
 
 typedef struct talloc_child_ctx_s TALLOC_CHILD_CTX;