MINOR: tools: protect dladdr() against reentrant calls from the debug handler

If a thread is currently resolving a symbol while another thread triggers
a thread dump, the current thread may enter the debug handler and call
resolve_sym_addr() again, possibly deadlocking if the underlying libc
uses locking. Let's postpone the debug signal delivery in this area
during the call. This will slow the resolution a little bit but we don't
care, it's not supposed to happen often and it must remain rock-solid.
This commit is contained in:
Willy Tarreau 2025-04-04 18:08:45 +02:00
parent 8d0c633677
commit 2dfb63313b

View File

@ -35,6 +35,7 @@ extern void *__elf_aux_vector;
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -5562,6 +5563,7 @@ const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *ad
static Dl_info dli_main;
static int dli_main_done; // 0 = not resolved, 1 = resolve in progress, 2 = done
__decl_thread_var(static HA_SPINLOCK_T dladdr_lock);
sigset_t new_mask, old_mask;
int isolated;
Dl_info dli;
size_t size = 0;
@ -5610,9 +5612,26 @@ const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *ad
HA_SPIN_TRYLOCK(OTHER_LOCK, &dladdr_lock) != 0)
goto use_array;
/* make sure we don't re-enter from wdt nor debug coming from other
* threads as dladdr() is not re-entrant. We'll block these sensitive
* signals while possibly dumping a backtrace.
*/
sigemptyset(&new_mask);
#ifdef WDTSIG
sigaddset(&new_mask, WDTSIG);
#endif
#ifdef DEBUGSIG
sigaddset(&new_mask, DEBUGSIG);
#endif
ha_sigmask(SIG_BLOCK, &new_mask, &old_mask);
/* now resolve the symbol */
i = dladdr_and_size(addr, &dli, &size);
if (!i) {
/* unblock temporarily blocked signals */
ha_sigmask(SIG_SETMASK, &old_mask, NULL);
if (!isolated)
HA_SPIN_UNLOCK(OTHER_LOCK, &dladdr_lock);
goto use_array;
@ -5638,6 +5657,9 @@ const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *ad
ha_thread_relax();
}
/* unblock temporarily blocked signals */
ha_sigmask(SIG_SETMASK, &old_mask, NULL);
if (!isolated)
HA_SPIN_UNLOCK(OTHER_LOCK, &dladdr_lock);