}
fr_dhcpv4_global_free();
- fr_dict_autofree(dhcpclient_dict);
+
+ if (fr_dict_autofree(dhcpclient_dict) < 0) {
+ fr_perror("dhcpclient");
+ ret = -1;
+ }
/*
* Ensure our atexit handlers run before any other
*/
int main(int argc, char **argv)
{
+ int ret = EXIT_SUCCESS;
int c;
char const *raddb_dir = RADDBDIR;
char const *dict_dir = DICTDIR;
fr_radius_free();
- fr_dict_autofree(radclient_dict);
+ if (fr_dict_autofree(radclient_dict) < 0) {
+ fr_perror("radclient");
+ ret = EXIT_FAILURE;
+ }
#ifndef NDEBUG
talloc_free(autofree);
if ((stats.lost > 0) || (stats.failed > 0)) return EXIT_FAILURE;
- return EXIT_SUCCESS;
+ return ret;
}
*/
static _Thread_local EVP_CIPHER_CTX *evp_chipher_ctx;
-static void _evp_cipher_ctx_free_on_exit(void *arg)
+static int _evp_cipher_ctx_free_on_exit(void *arg)
{
EVP_CIPHER_CTX_free(arg);
+ return 0;
}
/** Allocate and reset a resumable EVP_CIPHER_CTX for each thread
* Explicitly cleanup the memory allocated to the ring buffer,
* just in case valgrind complains about it.
*/
-static void _fr_network_rb_free(void *arg)
+static int _fr_network_rb_free(void *arg)
{
- talloc_free(arg);
+ return talloc_free(arg);
}
/** Initialise thread local storage
return query;
}
-static void _ldap_handle_thread_local_free(void *handle)
+static int _ldap_handle_thread_local_free(void *handle)
{
- ldap_unbind_ext_s(handle, NULL, NULL);
+ if (ldap_unbind_ext_s(handle, NULL, NULL) < 0) return -1;
+ return 0;
}
/** Get a thread local dummy LDAP handle
/** Free any free requests when the thread is joined
*
*/
-static void _command_set_free_list_free_on_exit(void *arg)
+static int _command_set_free_list_free_on_exit(void *arg)
{
fr_dlist_head_t *list = talloc_get_type_abort(arg, fr_dlist_head_t);
fr_redis_command_set_t *cmds;
/*
* See the destructor for why this works
*/
- while ((cmds = fr_dlist_head(list))) talloc_free(cmds);
- talloc_free(list);
+ while ((cmds = fr_dlist_head(list))) if (talloc_free(cmds) < 0) return -1;
+ return talloc_free(list);
}
/** Free a command set
/*
* Disarm the thread local destructors
*
- * FIXME - Leaving them enabled causes issues in child
- * execd processes, but we should really track down why.
+ * It's not safe to free memory between fork and exec.
*/
fr_atexit_thread_local_disarm_all();
/*
* Disarm the thread local destructors
*
- * FIXME - Leaving them enabled causes issues in child
- * execd processes, but we should really track down why.
+ * It's not safe to free memory between fork and exec.
*/
fr_atexit_thread_local_disarm_all();
/** Cleanup the memory pool used by vlog_request
*
*/
-static void _fr_vlog_request_pool_free(void *arg)
+static int _fr_vlog_request_pool_free(void *arg)
{
- talloc_free(arg);
+ return talloc_free(arg);
}
/** Send a log message to its destination, possibly including fields from the request
/** Cleanup the memory pool used by the OID sbuff
*
*/
-static void _fr_log_request_oid_buff_free(void *arg)
+static int _fr_log_request_oid_buff_free(void *arg)
{
- talloc_free(arg);
+ return talloc_free(arg);
}
/** Allocate an extensible sbuff for printing OID strings
*
* All thread local module lists should have been destroyed by this point
*/
-static void _module_thread_inst_list_free(void *tilp)
+static int _module_thread_inst_list_free(void *tilp)
{
module_thread_instance_t **til = talloc_get_type_abort(tilp, module_thread_instance_t *);
size_t i, len = talloc_array_length(til);
if (!fr_cond_assert_msg(found == 0,
"Thread local array has %u non-null elements remaining on exit. This is a leak",
found)) {
- return;
+ return -1;
}
- talloc_free(til);
+ return talloc_free(til);
}
/** Creates per-thread instance data for modules which need it
MEM(module_global_inst_list = fr_heap_alloc(NULL, _module_instance_global_cmp, module_instance_t, inst_idx, 256));
}
-static void _module_global_list_free(UNUSED void *uctx)
+static int _module_global_list_free(UNUSED void *uctx)
{
if (!fr_cond_assert_msg(fr_heap_num_elements(module_global_inst_list) == 0,
"Global module heap has %u elements remaining on exit. This is a leak",
- fr_heap_num_elements(module_global_inst_list))) return;
- TALLOC_FREE(module_global_inst_list);
- TALLOC_FREE(dl_modules);
+ fr_heap_num_elements(module_global_inst_list))) return -1;
+ if (talloc_free(module_global_inst_list) < 0) return -1;
+ module_global_inst_list = NULL;
+
+ if (talloc_free(dl_modules) < 0) return -1;
+ dl_modules = NULL;
+ return 0;
}
/** Perform global initialisation for modules
*
* Automatically called on exit.
*/
-void modules_rlm_free(void)
+int modules_rlm_free(void)
{
- TALLOC_FREE(rlm_modules);
- TALLOC_FREE(module_rlm_virtual_name_tree);
+ if (talloc_free(rlm_modules) < 0) return -1;
+ rlm_modules = NULL;
+ if (talloc_free(module_rlm_virtual_name_tree) < 0) return -1;
+ module_rlm_virtual_name_tree = NULL;
+
+ return 0;
}
-static void _modules_rlm_free_atexit(UNUSED void *uctx)
+static int _modules_rlm_free_atexit(UNUSED void *uctx)
{
- modules_rlm_free();
+ return modules_rlm_free();
}
/** Initialise the module list structure
*
* @{
*/
-void modules_rlm_free(void);
+int modules_rlm_free(void);
int modules_rlm_init(void);
/** @} */
/** Free any free requests when the thread is joined
*
*/
-static void _request_free_list_free_on_exit(void *arg)
+static int _request_free_list_free_on_exit(void *arg)
{
fr_dlist_head_t *list = talloc_get_type_abort(arg, fr_dlist_head_t);
request_t *request;
/*
* See the destructor for why this works
*/
- while ((request = fr_dlist_head(list))) talloc_free(request);
- talloc_free(list);
+ while ((request = fr_dlist_head(list))) if (talloc_free(request) < 0) return -1;
+ return talloc_free(list);
}
static inline CC_HINT(always_inline) request_t *request_alloc_pool(TALLOC_CTX *ctx)
return 0;
}
-void virtual_servers_free(void)
+int virtual_servers_free(void)
{
- TALLOC_FREE(listen_addr_root);
- TALLOC_FREE(server_section_name_tree);
- TALLOC_FREE(process_modules);
- TALLOC_FREE(proto_modules);
- fr_dict_autofree(virtual_server_dict_autoload);
+ if (talloc_free(listen_addr_root) < 0) return -1;
+ listen_addr_root = NULL;
+ if (talloc_free(server_section_name_tree) < 0) return -1;
+ server_section_name_tree = NULL;
+ if (talloc_free(process_modules) < 0) return -1;
+ process_modules = NULL;
+ if (talloc_free(proto_modules) < 0) return -1;
+ proto_modules = NULL;
+ if (fr_dict_autofree(virtual_server_dict_autoload) < 0) return -1;
+
+ return 0;
}
-static void _virtual_servers_atexit(UNUSED void *uctx)
+static int _virtual_servers_atexit(UNUSED void *uctx)
{
- virtual_servers_free();
+ return virtual_servers_free();
}
/** Performs global initialisation for the virtual server code
int virtual_servers_bootstrap(CONF_SECTION *config) CC_HINT(nonnull);
-void virtual_servers_free(void);
+int virtual_servers_free(void);
int virtual_servers_init(void) CC_HINT(nonnull);
/** @} */
/** Cleanup async pools if the thread exits
*
*/
-static void _openssl_thread_free(void *init)
+static int _openssl_thread_free(void *init)
{
ASYNC_cleanup_thread();
- talloc_free(init);
+ return talloc_free(init);
}
/** Perform thread-specific initialisation for OpenSSL
}
#endif
-static void fr_openssl_cleanup(UNUSED void *uctx)
+static int fr_openssl_cleanup(UNUSED void *uctx)
{
OPENSSL_cleanup();
+ return 0;
}
/** Add all the default ciphers and message digests to our context.
/** Frees the thread local TALLOC bio and its underlying OpenSSL BIO *
*
*/
-static void _fr_tls_bio_dbuff_thread_local_free(void *bio_talloc_agg)
+static int _fr_tls_bio_dbuff_thread_local_free(void *bio_talloc_agg)
{
fr_tls_bio_dbuff_t *our_bio_talloc_agg = talloc_get_type_abort(bio_talloc_agg, fr_tls_bio_dbuff_t);
- talloc_free(our_bio_talloc_agg); /* Frees the #fr_tls_bio_dbuff_t and BIO */
+ return talloc_free(our_bio_talloc_agg); /* Frees the #fr_tls_bio_dbuff_t and BIO */
}
/** Return a BIO which will aggregate data in an expandable talloc buffer
/** Frees a logging bio and its underlying OpenSSL BIO *
*
*/
-static void _fr_tls_log_bio_free(void *log_bio)
+static int _fr_tls_log_bio_free(void *log_bio)
{
fr_tls_log_bio_t *our_log_bio = talloc_get_type_abort(log_bio, fr_tls_log_bio_t);
BIO_free(our_log_bio->bio);
our_log_bio->bio = NULL;
- talloc_free(our_log_bio);
+ return talloc_free(our_log_bio);
}
/** Return a request log BIO to use with OpenSSL logging functions
*
* @param[in] list The thread-specific exit handler list.
*/
-static void _thread_local_free(void *list)
+static void _thread_local_pthread_free(void *list)
{
talloc_free(list);
}
+/** Run all the thread local destructors
+ *
+ * @param[in] list The thread-specific exit handler list.
+ */
+static int _thread_local_free(void *list)
+{
+ return talloc_free(list);
+}
+
/** Talloc destructor for freeing list elements in order
*
*/
(unsigned int)pthread_self(), list);
fr_dlist_talloc_init(&list->head, fr_atexit_entry_t, entry);
- (void) pthread_key_create(&list->key, _thread_local_free);
+ (void) pthread_key_create(&list->key, _thread_local_pthread_free);
/*
* We need to pass in a pointer to the heap
*
* One example is the OpenSSL log BIOs which must be cleaned up
* before fr_openssl_free is called.
+ *
+ * @return
+ * - >= 0 The number of atexit handlers triggered on success.
+ * - <0 the return code from any atexit handlers that returned an error.
*/
-unsigned int fr_atexit_thread_trigger_all(void)
+int fr_atexit_thread_trigger_all(void)
{
- fr_atexit_entry_t *e = NULL, *ee;
+ fr_atexit_entry_t *e = NULL, *ee, *to_free;
fr_atexit_list_t *list;
unsigned int count = 0;
list, ee, ee->func, ee->uctx, ee->file, ee->line);
count++;
- ee = fr_dlist_talloc_free_item(&list->head, ee);
+ to_free = ee;
+ ee = fr_dlist_remove(&list->head, ee);
+ if (talloc_free(to_free) < 0) {
+ fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p (alloced %s:%u)",
+ list, to_free,
+ to_free->func, to_free->uctx, to_free->file, to_free->line);
+ return -1;
+ }
}
}
* atexit handlers using the normal POSIX mechanism, and we need
* to ensure all our atexit handlers fire before so any global
* deinit is done explicitly by us.
+ *
+ * @return
+ * - >= 0 The number of atexit handlers triggered on success.
+ * - <0 the return code from any atexit handlers that returned an error.
*/
-unsigned int fr_atexit_global_trigger_all(void)
+int fr_atexit_global_trigger_all(void)
{
- fr_atexit_entry_t *e = NULL;
+ fr_atexit_entry_t *e = NULL, *to_free;
unsigned int count = 0;
/*
fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
count++;
- e = fr_dlist_talloc_free_item(&fr_atexit_global->head, e);
+ to_free = e;
+ e = fr_dlist_remove(&fr_atexit_global->head, e);
+ if (talloc_free(to_free) < 0) {
+ fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p (alloced %s:%u)",
+ fr_atexit_global, to_free,
+ to_free->func, to_free->uctx, to_free->file, to_free->line);
+ return -1;
+ }
}
return count;
* @param[in] uctx_scope Only process entries where the func and scope both match.
* @param[in] func Entries matching this function will be triggered.
* @param[in] uctx associated with the entry.
- * @return How many triggers fired.
+ * @return
+ * - >= 0 The number of atexit handlers triggered on success.
+ * - <0 the return code from any atexit handlers that returned an error.
*/
-unsigned int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx)
+int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx)
{
- fr_atexit_entry_t *e = NULL, *ee;
+ fr_atexit_entry_t *e = NULL, *ee, *to_free;
fr_atexit_list_t *list;
unsigned int count = 0;
fr_atexit_global, e, e->func, e->uctx, e->file, e->line);
count++;
- e = fr_dlist_talloc_free_item(&fr_atexit_global->head, e);
+ to_free = e;
+ e = fr_dlist_remove(&fr_atexit_global->head, e);
+ if (talloc_free(to_free) < 0) {
+ fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p (alloced %s:%u)",
+ fr_atexit_global, to_free,
+ to_free->func, to_free->uctx, to_free->file, to_free->line);
+ return -1;
+ }
}
e = NULL;
list, ee, ee->func, ee->uctx, ee->file, ee->line);
count++;
- ee = fr_dlist_talloc_free_item(&list->head, ee);
+ to_free = ee;
+ ee = fr_dlist_remove(&list->head, ee);
+ if (talloc_free(to_free) < 0) {
+ fr_strerror_printf_push("atexit handler failed %p/%p func=%p, uctx=%p (alloced %s:%u)",
+ list, to_free,
+ to_free->func, to_free->uctx, to_free->file, to_free->line);
+ return -1;
+ }
}
}
*
* @param[in] uctx to free.
*/
-typedef void(*fr_atexit_t)(void *uctx);
+typedef int(*fr_atexit_t)(void *uctx);
int fr_atexit_global_setup(void);
void fr_atexit_thread_local_disarm_all(void);
-unsigned int fr_atexit_thread_trigger_all(void);
+int fr_atexit_thread_trigger_all(void);
unsigned int fr_atexit_global_disarm(bool uctx_scope, fr_atexit_t func, void const *uctx);
void fr_atexit_global_disarm_all(void);
-unsigned int fr_atexit_global_trigger_all(void);
+int fr_atexit_global_trigger_all(void);
-unsigned int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx);
+int fr_atexit_trigger(bool uctx_scope, fr_atexit_t func, void const *uctx);
bool fr_atexit_is_exiting(void);
return 0;
}
-static void _dict_global_free_at_exit(void *uctx)
+static int _dict_global_free_at_exit(void *uctx)
{
- talloc_free(uctx);
+ return talloc_free(uctx);
}
static int _dict_global_free(fr_dict_gctx_t *gctx)
/** Free any memory we allocated for indexes
*
*/
-static void _event_free_indexes(UNUSED void *uctx)
+static int _event_free_indexes(UNUSED void *uctx)
{
unsigned int i;
- for (i = 0; i < NUM_ELEMENTS(filter_maps); i++) talloc_free(filter_maps[i].ev_to_func);
+ for (i = 0; i < NUM_ELEMENTS(filter_maps); i++) if (talloc_free(filter_maps[i].ev_to_func) < 0) return -1;
+ return 0;
}
static void _event_build_indexes(UNUSED void *uctx)
static _Thread_local EVP_MD_CTX *md5_hmac_ctx;
-static void _hmac_md5_ctx_free_on_exit(void *arg)
+static int _hmac_md5_ctx_free_on_exit(void *arg)
{
EVP_MD_CTX_free(arg);
+ return 0;
}
/** Calculate HMAC using OpenSSL's MD5 implementation
static _Thread_local EVP_MD_CTX *sha1_hmac_ctx;
-static void _hmac_sha1_ctx_free_on_exit(void *arg)
+static int _hmac_sha1_ctx_free_on_exit(void *arg)
{
EVP_MD_CTX_free(arg);
+ return 0;
}
/** Calculate HMAC using OpenSSL's SHA1 implementation
/** Cleanup the memory pool used by vlog_request
*
*/
-static void _fr_log_pool_free(void *arg)
+static int _fr_log_pool_free(void *arg)
{
- talloc_free(arg);
+ if (talloc_free(arg) < 0) return -1;
fr_log_pool = NULL;
+ return 0;
}
/** talloc ctx to use when composing log messages
fr_md4_ctx_free_from_list(&ctx);
}
-static void _md4_ctx_free_on_exit(void *arg)
+static int _md4_ctx_free_on_exit(void *arg)
{
int i;
fr_md4_free_list_t *free_list = arg;
fr_md4_ctx_free(&free_list[i].md_ctx);
}
- talloc_free(free_list);
+ return talloc_free(free_list);
}
/** @copydoc fr_md4_ctx_alloc
fr_md5_ctx_free_from_list(&ctx);
}
-static void _md5_ctx_free_on_exit(void *arg)
+static int _md5_ctx_free_on_exit(void *arg)
{
int i;
fr_md5_free_list_t *free_list = arg;
fr_md5_ctx_free(&free_list[i].md_ctx);
}
- talloc_free(free_list);
+ return talloc_free(free_list);
}
/** @copydoc fr_md5_ctx_alloc
return 0;
}
-static void _pcre2_tls_free_on_exit(void *arg)
+static int _pcre2_tls_free_on_exit(void *arg)
{
- talloc_free(arg);
+ return talloc_free(arg);
}
/** Thread local init for pcre2
return 0;
}
-static void _pcre_tls_free_on_exit(void *arg)
+static int _pcre_tls_free_on_exit(void *arg)
{
- talloc_free(arg);
+ return talloc_free(arg);
}
/** Performs thread local storage initialisation for libpcre
/** Free the scratch buffer used for printf
*
*/
-static void _sbuff_scratch_free(void *arg)
+static int _sbuff_scratch_free(void *arg)
{
- talloc_free(arg);
+ return talloc_free(arg);
}
static inline CC_HINT(always_inline) int sbuff_scratch_init(TALLOC_CTX **out)
fr_sbuff_uctx_talloc_t tctx; //!< Thread local tctx.
} fr_sbuff_thread_local_t;
-static inline void _sbuff_thread_local_free(void *sbtl)
+static inline int _sbuff_thread_local_free(void *sbtl)
{
- talloc_free(sbtl);
+ return talloc_free(sbtl);
}
/** Create a function local and thread local extensible sbuff
* Explicitly cleanup the memory allocated to the error buffer,
* just in case valgrind complains about it.
*/
-static void _fr_logging_free(void *arg)
+static int _fr_logging_free(void *arg)
{
/*
* Free arg instead of thread local storage
* as address sanitizer does a better job
* of tracking and doesn't report a leak.
*/
- talloc_free(arg);
+ if (talloc_free(arg) < 0) return -1;
fr_strerror_buffer = NULL;
logging_stop = true;
+
+ return 0;
}
/** Initialise thread local storage
* Explicitly cleanup the memory allocated to the error buffer,
* just in case valgrind complains about it.
*/
-static void _fr_logging_free(UNUSED void *arg)
+static int _fr_logging_free(UNUSED void *arg)
{
- TALLOC_FREE(fr_syserror_buffer);
+ if (talloc_free(fr_syserror_buffer) < 0) return -1;
+ fr_syserror_buffer = NULL;
logging_stop = true;
+ return 0;
}
/** POSIX-2008 errno macros
/** Callback to free the autofree ctx on global exit
*
*/
-static void _autofree_on_exit(void *af)
+static int _autofree_on_exit(void *af)
{
talloc_set_destructor(af, NULL);
- talloc_free(af);
+ return talloc_free(af);
}
/** Ensures in the autofree ctx is manually freed, things don't explode atexit
/*
* Explicitly cleanup the memory allocated to the error buffer.
*/
-static void _krb5_logging_free(void *arg)
+static int _krb5_logging_free(void *arg)
{
- talloc_free(arg);
+ return talloc_free(arg);
}
char const *rlm_krb5_error(rlm_krb5_t const *inst, krb5_context context, krb5_error_code code)