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 <ccaulfie@redhat.com>
This commit is contained in:
Christine Caulfield 2015-10-01 15:01:14 +01:00
parent b635cc9703
commit 1a7ea3b8d8

View File

@ -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;