Fix race condition in signal handler query (#13712)

* Fix race condition in signal handler query

* Initialize signal lock dynamically and reset after fork

* Fix signal handler mutex initialization conditions
This commit is contained in:
Erik Berlin 2025-06-27 21:55:59 -07:00 committed by GitHub
parent 31c1f3665a
commit eab4a0bc8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 21 additions and 2 deletions

View file

@ -19,6 +19,7 @@ void (*ruby_posix_signal(int, void (*)(int)))(int);
RUBY_SYMBOL_EXPORT_BEGIN
/* signal.c (export) */
void rb_signal_atfork(void);
RUBY_SYMBOL_EXPORT_END
#endif /* INTERNAL_SIGNAL_H */

View file

@ -664,6 +664,10 @@ ruby_nativethread_signal(int signum, sighandler_t handler)
#endif
#endif
#if !defined(POSIX_SIGNAL) && !defined(SIG_GET)
static rb_nativethread_lock_t sig_check_lock;
#endif
static int
signal_ignored(int sig)
{
@ -678,9 +682,11 @@ signal_ignored(int sig)
// SIG_GET: Returns the current value of the signal.
func = signal(sig, SIG_GET);
#else
// TODO: this is not a thread-safe way to do it. Needs lock.
sighandler_t old = signal(sig, SIG_DFL);
sighandler_t old;
rb_native_mutex_lock(&sig_check_lock);
old = signal(sig, SIG_DFL);
signal(sig, old);
rb_native_mutex_unlock(&sig_check_lock);
func = old;
#endif
if (func == SIG_IGN) return 1;
@ -1509,6 +1515,9 @@ Init_signal(void)
rb_define_method(rb_eSignal, "signo", esignal_signo, 0);
rb_alias(rb_eSignal, rb_intern_const("signm"), rb_intern_const("message"));
rb_define_method(rb_eInterrupt, "initialize", interrupt_init, -1);
#if !defined(POSIX_SIGNAL) && !defined(SIG_GET)
rb_native_mutex_initialize(&sig_check_lock);
#endif
// It should be ready to call rb_signal_exec()
VM_ASSERT(GET_THREAD()->pending_interrupt_queue);
@ -1561,3 +1570,11 @@ Init_signal(void)
rb_enable_interrupt();
}
void
rb_signal_atfork(void)
{
#if defined(HAVE_WORKING_FORK) && !defined(POSIX_SIGNAL) && !defined(SIG_GET)
rb_native_mutex_initialize(&sig_check_lock);
#endif
}

View file

@ -4933,6 +4933,7 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r
thread_sched_atfork(TH_SCHED(th));
ubf_list_atfork();
rb_signal_atfork();
// OK. Only this thread accesses:
ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {