From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 17 Dec 2020 16:50:06 +0100 Subject: [PATCH] multipathd: add code to initalize unwinder glibc's implementation of pthread_cancel() loads symbols from libgcc_s.so using dlopen() when pthread_cancel() is called for the first time. This happens even with LD_BIND_NOW=1. This may imply the need for file system access when a thread is cancelled, which in the case of multipath-tools might be in a dangerous situation where multipathd must avoid blocking. Call load_unwinder() during startup to make sure the dynamic linker has all necessary symbols resolved early on. This implementation simply creates a dummy thread and cancels it. This way all necessary symbols for thread cancellation will be loaded, no matter what the C library needs to implement cancellation. Reviewed-by: Benjamin Marzinski Signed-off-by: Benjamin Marzinski --- multipathd/Makefile | 2 +- multipathd/init_unwinder.c | 34 ++++++++++++++++++++++++++++++++++ multipathd/init_unwinder.h | 21 +++++++++++++++++++++ multipathd/main.c | 2 ++ 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 multipathd/init_unwinder.c create mode 100644 multipathd/init_unwinder.h diff --git a/multipathd/Makefile b/multipathd/Makefile index 632b82b1..d053c1ed 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -30,7 +30,7 @@ ifeq ($(ENABLE_DMEVENTS_POLL),0) endif OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \ - dmevents.o + dmevents.o init_unwinder.o EXEC = multipathd diff --git a/multipathd/init_unwinder.c b/multipathd/init_unwinder.c new file mode 100644 index 00000000..14467f3d --- /dev/null +++ b/multipathd/init_unwinder.c @@ -0,0 +1,34 @@ +#include +#include +#include "init_unwinder.h" + +static pthread_mutex_t dummy_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t dummy_cond = PTHREAD_COND_INITIALIZER; + +static void *dummy_thread(void *arg __attribute__((unused))) +{ + pthread_mutex_lock(&dummy_mtx); + pthread_cond_broadcast(&dummy_cond); + pthread_mutex_unlock(&dummy_mtx); + pause(); + return NULL; +} + +int init_unwinder(void) +{ + pthread_t dummy; + int rc; + + pthread_mutex_lock(&dummy_mtx); + + rc = pthread_create(&dummy, NULL, dummy_thread, NULL); + if (rc != 0) { + pthread_mutex_unlock(&dummy_mtx); + return rc; + } + + pthread_cond_wait(&dummy_cond, &dummy_mtx); + pthread_mutex_unlock(&dummy_mtx); + + return pthread_cancel(dummy); +} diff --git a/multipathd/init_unwinder.h b/multipathd/init_unwinder.h new file mode 100644 index 00000000..ada09f82 --- /dev/null +++ b/multipathd/init_unwinder.h @@ -0,0 +1,21 @@ +#ifndef _INIT_UNWINDER_H +#define _INIT_UNWINDER_H 1 + +/* + * init_unwinder(): make sure unwinder symbols are loaded + * + * libc's implementation of pthread_cancel() loads symbols from + * libgcc_s.so using dlopen() when pthread_cancel() is called + * for the first time. This happens even with LD_BIND_NOW=1. + * This may imply the need for file system access when a thread is + * cancelled, which in the case of multipath-tools might be in a + * dangerous situation where multipathd must avoid blocking. + * + * Call load_unwinder() during startup to make sure the dynamic + * linker has all necessary symbols resolved early on. + * + * Return: 0 if successful, an error number otherwise. + */ +int init_unwinder(void); + +#endif diff --git a/multipathd/main.c b/multipathd/main.c index 99a89a69..6f851ae8 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -83,6 +83,7 @@ #include "wwids.h" #include "foreign.h" #include "../third-party/valgrind/drd.h" +#include "init_unwinder.h" #define FILE_NAME_SIZE 256 #define CMDSIZE 160 @@ -3041,6 +3042,7 @@ child (__attribute__((unused)) void *param) enum daemon_status state; int exit_code = 1; + init_unwinder(); mlockall(MCL_CURRENT | MCL_FUTURE); signal_init(); mp_rcu_data = setup_rcu();