From 1a7ea3b8d8bf9d5ffbbfdc785ace2b81e9f2356d Mon Sep 17 00:00:00 2001 From: Christine Caulfield Date: Thu, 1 Oct 2015 15:01:14 +0100 Subject: [PATCH] log: don't call dlopen inside dl_iterate_phdr() Some platforms (eg. FreeBSD 10+) don't support calling dlopen() from within a dl_iterate_phdr() callback; so save all of the dlpi_names to a list and iterate over them afterwards. This fixes a deadlock which occurs in dlopen() when called from within dl_iterate_phdr() on FreeBSD 10+ when linked to libthr. Patch from dpejesh on github. Reviewed-by: Christine Caulfield --- lib/log.c | 57 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/lib/log.c b/lib/log.c index 9808c63..3336af9 100644 --- a/lib/log.c +++ b/lib/log.c @@ -47,9 +47,15 @@ static int32_t logger_inited = QB_FALSE; static pthread_rwlock_t _listlock; static qb_log_filter_fn _custom_filter_fn = NULL; +static QB_LIST_DECLARE(dlnames); static QB_LIST_DECLARE(tags_head); static QB_LIST_DECLARE(callsite_sections); +struct dlname { + char *dln_name; + struct qb_list_head list; +}; + struct callsite_section { struct qb_log_callsite *start; struct qb_log_callsite *stop; @@ -739,23 +745,45 @@ qb_log_filter_ctl(int32_t t, enum qb_log_filter_conf c, } #ifdef QB_HAVE_ATTRIBUTE_SECTION +/* Some platforms (eg. FreeBSD 10+) don't support calling dlopen() from + * within a dl_iterate_phdr() callback; so save all of the dlpi_names to + * a list and iterate over them afterwards. */ static int32_t _log_so_walk_callback(struct dl_phdr_info *info, size_t size, void *data) { - if (strlen(info->dlpi_name) > 0) { - void *handle; - void *start; - void *stop; - const char *error; + struct dlname *dlname; - handle = dlopen(info->dlpi_name, RTLD_LAZY); + if (strlen(info->dlpi_name) > 0) { + dlname = calloc(1, sizeof(struct dlname)); + if (!dlname) + return 0; + dlname->dln_name = strdup(info->dlpi_name); + qb_list_add_tail(&dlname->list, &dlnames); + } + + return 0; +} + +static void +_log_so_walk_dlnames(void) +{ + struct dlname *dlname; + struct qb_list_head *iter; + struct qb_list_head *next; + + void *handle; + void *start; + void *stop; + const char *error; + + qb_list_for_each_safe(iter, next, &dlnames) { + dlname = qb_list_entry(iter, struct dlname, list); + + handle = dlopen(dlname->dln_name, RTLD_LAZY); error = dlerror(); if (!handle || error) { qb_log(LOG_ERR, "%s", error); - if (handle) { - dlclose(handle); - } - return 0; + goto done; } start = dlsym(handle, "__start___verbose"); @@ -773,9 +801,13 @@ _log_so_walk_callback(struct dl_phdr_info *info, size_t size, void *data) qb_log_callsites_register(start, stop); } done: - dlclose(handle); + if (handle) + dlclose(handle); + qb_list_del(iter); + if (dlname->dln_name) + free(dlname->dln_name); + free(dlname); } - return 0; } #endif /* QB_HAVE_ATTRIBUTE_SECTION */ @@ -823,6 +855,7 @@ qb_log_init(const char *name, int32_t facility, uint8_t priority) #ifdef QB_HAVE_ATTRIBUTE_SECTION qb_log_callsites_register(__start___verbose, __stop___verbose); dl_iterate_phdr(_log_so_walk_callback, NULL); + _log_so_walk_dlnames(); #endif /* QB_HAVE_ATTRIBUTE_SECTION */ conf[QB_LOG_STDERR].state = QB_LOG_STATE_DISABLED;