From da8385b7a10efe9680c7359bf3cfc8340dbec3cb Mon Sep 17 00:00:00 2001 From: Erik Berlin Date: Wed, 25 Jun 2025 22:22:51 -0700 Subject: [PATCH 1/3] Fix race condition in signal handler query --- signal.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/signal.c b/signal.c index c8120087e74e7e..cc6415eeab6c5b 100644 --- a/signal.c +++ b/signal.c @@ -678,9 +678,12 @@ 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); + static rb_nativethread_lock_t sig_check_lock = RB_NATIVETHREAD_LOCK_INIT; + 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; From 0fa364cb391945e7a49e689b40128eef853c501b Mon Sep 17 00:00:00 2001 From: Erik Berlin Date: Thu, 26 Jun 2025 18:08:52 -0700 Subject: [PATCH 2/3] Initialize signal lock dynamically and reset after fork --- internal/signal.h | 1 + signal.c | 14 +++++++++++++- thread.c | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/internal/signal.h b/internal/signal.h index 2363bf412cfbb2..904747e226a05f 100644 --- a/internal/signal.h +++ b/internal/signal.h @@ -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 */ diff --git a/signal.c b/signal.c index cc6415eeab6c5b..4db1545a2efce9 100644 --- a/signal.c +++ b/signal.c @@ -664,6 +664,8 @@ ruby_nativethread_signal(int signum, sighandler_t handler) #endif #endif +static rb_nativethread_lock_t sig_check_lock; + static int signal_ignored(int sig) { @@ -678,7 +680,6 @@ signal_ignored(int sig) // SIG_GET: Returns the current value of the signal. func = signal(sig, SIG_GET); #else - static rb_nativethread_lock_t sig_check_lock = RB_NATIVETHREAD_LOCK_INIT; sighandler_t old; rb_native_mutex_lock(&sig_check_lock); old = signal(sig, SIG_DFL); @@ -1512,6 +1513,7 @@ 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); + rb_native_mutex_initialize(&sig_check_lock); // It should be ready to call rb_signal_exec() VM_ASSERT(GET_THREAD()->pending_interrupt_queue); @@ -1564,3 +1566,13 @@ Init_signal(void) rb_enable_interrupt(); } + + #if defined(HAVE_WORKING_FORK) + void + rb_signal_atfork(void) + { + rb_native_mutex_initialize(&sig_check_lock); + } + #else + void rb_signal_atfork(void) {} + #endif diff --git a/thread.c b/thread.c index feb44197268986..8442a7e786d7ac 100644 --- a/thread.c +++ b/thread.c @@ -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) { From 76baa0f604ad38c215c062da8fa34d90a7d4802a Mon Sep 17 00:00:00 2001 From: Erik Berlin Date: Fri, 27 Jun 2025 18:56:15 -0700 Subject: [PATCH 3/3] Fix signal handler mutex initialization conditions --- signal.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/signal.c b/signal.c index 4db1545a2efce9..9edac5a7896fb7 100644 --- a/signal.c +++ b/signal.c @@ -664,7 +664,9 @@ 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) @@ -1513,7 +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); @@ -1567,12 +1571,10 @@ Init_signal(void) rb_enable_interrupt(); } - #if defined(HAVE_WORKING_FORK) - void - rb_signal_atfork(void) - { - rb_native_mutex_initialize(&sig_check_lock); - } - #else - void rb_signal_atfork(void) {} - #endif +void +rb_signal_atfork(void) +{ +#if defined(HAVE_WORKING_FORK) && !defined(POSIX_SIGNAL) && !defined(SIG_GET) + rb_native_mutex_initialize(&sig_check_lock); +#endif +}