summaryrefslogtreecommitdiff
path: root/signal.c
diff options
context:
space:
mode:
Diffstat (limited to 'signal.c')
-rw-r--r--signal.c128
1 files changed, 92 insertions, 36 deletions
diff --git a/signal.c b/signal.c
index 6c2a757a98..f37aaf971a 100644
--- a/signal.c
+++ b/signal.c
@@ -45,6 +45,7 @@
#include "ruby_atomic.h"
#include "vm_core.h"
#include "ractor_core.h"
+#include "ruby/internal/attr/nonstring.h"
#ifdef NEED_RUBY_ATOMIC_OPS
rb_atomic_t
@@ -402,7 +403,6 @@ interrupt_init(int argc, VALUE *argv, VALUE self)
return rb_call_super(2, args);
}
-void rb_malloc_info_show_results(void); /* gc.c */
#if defined(USE_SIGALTSTACK) || defined(_WIN32)
static void reset_sigmask(int sig);
#endif
@@ -413,7 +413,6 @@ ruby_default_signal(int sig)
#if USE_DEBUG_COUNTER
rb_debug_counter_show_results("killed by signal.");
#endif
- rb_malloc_info_show_results();
signal(sig, SIG_DFL);
#if defined(USE_SIGALTSTACK) || defined(_WIN32)
@@ -665,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)
{
@@ -674,9 +677,16 @@ signal_ignored(int sig)
(void)VALGRIND_MAKE_MEM_DEFINED(&old, sizeof(old));
if (sigaction(sig, NULL, &old) < 0) return FALSE;
func = old.sa_handler;
+#elif defined SIG_GET
+ // https://learn.microsoft.com/en-us/cpp/c-runtime-library/signal-action-constants
+ // SIG_GET: Returns the current value of the signal.
+ func = signal(sig, SIG_GET);
#else
- 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;
@@ -708,7 +718,7 @@ sighandler(int sig)
int
rb_signal_buff_size(void)
{
- return signal_buff.size;
+ return RUBY_ATOMIC_LOAD(signal_buff.size);
}
static void
@@ -736,7 +746,7 @@ rb_get_next_signal(void)
{
int i, sig = 0;
- if (signal_buff.size != 0) {
+ if (rb_signal_buff_size() != 0) {
for (i=1; i<RUBY_NSIG; i++) {
if (signal_buff.cnt[i] > 0) {
ATOMIC_DEC(signal_buff.cnt[i]);
@@ -760,7 +770,6 @@ static const char *received_signal;
#endif
#if defined(USE_SIGALTSTACK) || defined(_WIN32)
-NORETURN(void rb_ec_stack_overflow(rb_execution_context_t *ec, int crit));
# if defined __HAIKU__
# define USE_UCONTEXT_REG 1
# elif !(defined(HAVE_UCONTEXT_H) && (defined __i386__ || defined __x86_64__ || defined __amd64__))
@@ -846,13 +855,17 @@ check_stack_overflow(int sig, const uintptr_t addr, const ucontext_t *ctx)
if (sp_page == fault_page || sp_page == fault_page + 1 ||
(sp_page <= fault_page && fault_page <= bp_page)) {
rb_execution_context_t *ec = GET_EC();
- int crit = FALSE;
+ ruby_stack_overflow_critical_level crit = rb_stack_overflow_signal;
int uplevel = roomof(pagesize, sizeof(*ec->tag)) / 2; /* XXX: heuristic */
while ((uintptr_t)ec->tag->buf / pagesize <= fault_page + 1) {
/* drop the last tag if it is close to the fault,
* otherwise it can cause stack overflow again at the same
* place. */
- if ((crit = (!ec->tag->prev || !--uplevel)) != FALSE) break;
+ if (!ec->tag->prev || !--uplevel) {
+ crit = rb_stack_overflow_fatal;
+ break;
+ }
+ rb_vm_tag_jmpbuf_deinit(&ec->tag->buf);
ec->tag = ec->tag->prev;
}
reset_sigmask(sig);
@@ -864,10 +877,12 @@ static void
check_stack_overflow(int sig, const void *addr)
{
int ruby_stack_overflowed_p(const rb_thread_t *, const void *);
- rb_thread_t *th = GET_THREAD();
+ rb_execution_context_t *ec = rb_current_execution_context(false);
+ if (!ec) return;
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
if (ruby_stack_overflowed_p(th, addr)) {
reset_sigmask(sig);
- rb_ec_stack_overflow(th->ec, FALSE);
+ rb_ec_stack_overflow(th->ec, 1);
}
}
# endif
@@ -934,6 +949,20 @@ sigsegv(int sig SIGINFO_ARG)
}
#endif
+#ifdef SIGABRT
+
+static sighandler_t default_sigabrt_handler;
+NORETURN(static ruby_sigaction_t sigabrt);
+
+static void
+sigabrt(int sig SIGINFO_ARG)
+{
+ check_reserved_signal("ABRT");
+ CHECK_STACK_OVERFLOW();
+ rb_bug_for_fatal_signal(default_sigabrt_handler, sig, SIGINFO_CTX, "Aborted" MESSAGE_FAULT_ADDRESS);
+}
+#endif
+
#ifdef SIGILL
static sighandler_t default_sigill_handler;
@@ -975,7 +1004,7 @@ check_reserved_signal_(const char *name, size_t name_len, int signo)
if (prev) {
ssize_t RB_UNUSED_VAR(err);
static const int stderr_fd = 2;
-#define NOZ(name, str) name[sizeof(str)-1] = str
+#define NOZ(name, str) RBIMPL_ATTR_NONSTRING() name[sizeof(str)-1] = str
static const char NOZ(msg1, " received in ");
static const char NOZ(msg2, " handler\n");
@@ -991,8 +1020,20 @@ check_reserved_signal_(const char *name, size_t name_len, int signo)
#if __has_feature(address_sanitizer) || \
__has_feature(memory_sanitizer) || \
defined(HAVE_VALGRIND_MEMCHECK_H)
- ruby_posix_signal(signo, SIG_DFL);
+# define SANITIZING true
+#else
+# define SANITIZING false
+#endif
+
+#ifdef SIGABRT
+// Avoid infinite loop when already aborting
+# define RECURSIVE (signo == SIGABRT)
+#else
+# define RECURSIVE false
#endif
+ if (SANITIZING || RECURSIVE) ruby_signal(signo, SIG_DFL);
+# undef SANITIZING
+# undef RECURSIVE
W(name, name_len);
W(msg1, sizeof(msg1));
W(prev, strlen(prev));
@@ -1037,7 +1078,7 @@ signal_exec(VALUE cmd, int sig)
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
VALUE signum = INT2NUM(sig);
- rb_eval_cmd_kw(cmd, rb_ary_new3(1, signum), RB_NO_KEYWORDS);
+ rb_eval_cmd_call_kw(cmd, 1, &signum, RB_NO_KEYWORDS);
}
EC_POP_TAG();
ec = GET_EC();
@@ -1223,11 +1264,6 @@ trap_handler(VALUE *cmd, int sig)
break;
}
}
- else {
- rb_proc_t *proc;
- GetProcPtr(*cmd, proc);
- (void)proc;
- }
}
return func;
@@ -1322,31 +1358,36 @@ reserved_signal_p(int signo)
/*
* call-seq:
- * Signal.trap( signal, command ) -> obj
- * Signal.trap( signal ) {| | block } -> obj
+ * Signal.trap(signal, command) -> obj
+ * Signal.trap(signal) { ... } -> obj
+ *
+ * Specifies the handling of signals. Returns the previous handler for
+ * the given signal.
*
- * Specifies the handling of signals. The first parameter is a signal
- * name (a string such as ``SIGALRM'', ``SIGUSR1'', and so on) or a
- * signal number. The characters ``SIG'' may be omitted from the
- * signal name. The command or block specifies code to be run when the
+ * Argument +signal+ is a signal name (a string or symbol such
+ * as +SIGALRM+ or +SIGUSR1+) or an integer signal number. When +signal+
+ * is a string or symbol, the leading characters +SIG+ may be omitted.
+ *
+ * Argument +command+ or block provided specifies code to be run when the
* signal is raised.
- * If the command is the string ``IGNORE'' or ``SIG_IGN'', the signal
- * will be ignored.
- * If the command is ``DEFAULT'' or ``SIG_DFL'', the Ruby's default handler
- * will be invoked.
- * If the command is ``EXIT'', the script will be terminated by the signal.
- * If the command is ``SYSTEM_DEFAULT'', the operating system's default
- * handler will be invoked.
- * Otherwise, the given command or block will be run.
- * The special signal name ``EXIT'' or signal number zero will be
- * invoked just prior to program termination.
- * trap returns the previous handler for the given signal.
+ *
+ * Argument +command+ may also be a string or symbol with the following special
+ * values:
+ *
+ * - +IGNORE+, +SIG_IGN+: the signal will be ignored.
+ * - +DEFAULT+, +SIG_DFL+: Ruby's default handler will be invoked.
+ * - +EXIT+: the process will be terminated by the signal.
+ * - +SYSTEM_DEFAULT+: the operating system's default handler will be invoked.
+ *
+ * The special signal name +EXIT+ or signal number zero will be
+ * invoked just prior to program termination:
*
* Signal.trap(0, proc { puts "Terminating: #{$$}" })
* Signal.trap("CLD") { puts "Child died" }
* fork && Process.wait
*
- * <em>produces:</em>
+ * Outputs:
+ *
* Terminating: 27461
* Child died
* Terminating: 27460
@@ -1502,6 +1543,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);
@@ -1540,6 +1584,10 @@ Init_signal(void)
RB_ALTSTACK_INIT(GET_VM()->main_altstack, rb_allocate_sigaltstack());
force_install_sighandler(SIGSEGV, (sighandler_t)sigsegv, &default_sigsegv_handler);
#endif
+
+#ifdef SIGABRT
+ force_install_sighandler(SIGABRT, (sighandler_t)sigabrt, &default_sigabrt_handler);
+#endif
}
#ifdef SIGPIPE
install_sighandler(SIGPIPE, sig_do_nothing);
@@ -1554,3 +1602,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
+}