From e3b96ee603fd70c7e259cde0ffdab836e88d2779 Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Wed, 21 Aug 2024 16:17:32 +1000 Subject: [PATCH] csighandler3: try a fallback interpreter if none in TLS If an external library, possibly loaded by an XS module, creates a thread, perl has no control over this thread, nor does it create an interpreter for that thread, so if a signal is delivered to that thread my_perl would be NULL, and we would crash trying to safely deliver the signal. To avoid that uses the saved main interpreter pointer instead. Since trying to unsafe deliver the signal to the handler from the wrong thread would result in races on the interpreter's data structures I chose not to direct deliver unsafe signals. Accessing PL_psig_pend[] from the wrong thread isn't great, but to ensure safe handling of that we'd need lockless atomics, which we don't have access to from C99 (and aren't required in C11). Fixes #22487 --- mg.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/mg.c b/mg.c index c37707be462d..00773bc103d5 100644 --- a/mg.c +++ b/mg.c @@ -1564,6 +1564,33 @@ Perl_csighandler3(int sig, Siginfo_t *sip PERL_UNUSED_DECL, void *uap PERL_UNUSE dTHX; #endif + bool can_deliver = true; +#ifdef MULTIPLICITY + /* If a C library creates a thread, signals may be delivered to + * the library thread which doesn't have the perl context set in + * TLS. + * + * So fallback to the interpreter stored during interpreter + * initialization. + * + * This isn't great, but it's better than a segfault or dropping + * the signal on the floor. + */ + if (!aTHX) { + aTHX = PERL_GET_INTERP; + /* Trying to deliver from here is not just unsafe, it's + * foolish, since we'd have two threads working with the + * same interpreter structure. + * + * The signals like SIGSEGV listed below should only be + * delivered to the current thread (I think) so we shouldn't + * get here. + */ + can_deliver = false; + assert(aTHX); + } +#endif + #ifdef PERL_USE_3ARG_SIGHANDLER #if defined(__cplusplus) && defined(__GNUC__) /* g++ doesn't support PERL_UNUSED_DECL, so the sip and uap @@ -1598,7 +1625,7 @@ Perl_csighandler3(int sig, Siginfo_t *sip PERL_UNUSED_DECL, void *uap PERL_UNUSE #ifdef SIGFPE sig == SIGFPE || #endif - (PL_signals & PERL_SIGNALS_UNSAFE_FLAG)) + ((PL_signals & PERL_SIGNALS_UNSAFE_FLAG) && can_deliver)) /* Call the perl level handler now-- * with risk we may be in malloc() or being destructed etc. */ {