summaryrefslogtreecommitdiff
path: root/signal.c
diff options
context:
space:
mode:
Diffstat (limited to 'signal.c')
-rw-r--r--signal.c327
1 files changed, 142 insertions, 185 deletions
diff --git a/signal.c b/signal.c
index 544854e014..f37aaf971a 100644
--- a/signal.c
+++ b/signal.c
@@ -36,6 +36,7 @@
#include "debug_counter.h"
#include "eval_intern.h"
#include "internal.h"
+#include "internal/error.h"
#include "internal/eval.h"
#include "internal/sanitizers.h"
#include "internal/signal.h"
@@ -44,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
@@ -132,7 +134,7 @@ static const struct signals {
#ifdef SIGCONT
{"CONT", SIGCONT},
#endif
-#if RUBY_SIGCHLD
+#ifdef RUBY_SIGCHLD
{"CHLD", RUBY_SIGCHLD },
{"CLD", RUBY_SIGCHLD },
#endif
@@ -273,7 +275,7 @@ signm2signo(VALUE *sig_ptr, int negative, int exit, int *prefix_ptr)
vsig = rb_str_subseq(vsig, prefix, len);
prefix = signame_prefix_len;
}
- rb_raise(rb_eArgError, "unsupported signal `%.*s%"PRIsVALUE"'",
+ rb_raise(rb_eArgError, "unsupported signal '%.*s%"PRIsVALUE"'",
prefix, signame_prefix, vsig);
UNREACHABLE_RETURN(0);
}
@@ -401,7 +403,9 @@ 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
void
ruby_default_signal(int sig)
@@ -409,9 +413,11 @@ 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)
+ reset_sigmask(sig);
+#endif
raise(sig);
}
@@ -508,9 +514,6 @@ static struct {
rb_atomic_t cnt[RUBY_NSIG];
rb_atomic_t size;
} signal_buff;
-#if RUBY_SIGCHLD
-volatile unsigned int ruby_nocldwait;
-#endif
#define sighandler_t ruby_sighandler_t
@@ -593,7 +596,7 @@ ruby_signal(int signum, sighandler_t handler)
#endif
sigemptyset(&sigact.sa_mask);
-#ifdef USE_SIGALTSTACK
+#if defined(USE_SIGALTSTACK) && !defined(__wasm__)
if (handler == SIG_IGN || handler == SIG_DFL) {
sigact.sa_handler = handler;
sigact.sa_flags = 0;
@@ -608,27 +611,6 @@ ruby_signal(int signum, sighandler_t handler)
#endif
switch (signum) {
-#if RUBY_SIGCHLD
- case RUBY_SIGCHLD:
- if (handler == SIG_IGN) {
- ruby_nocldwait = 1;
-# ifdef USE_SIGALTSTACK
- if (sigact.sa_flags & SA_SIGINFO) {
- sigact.sa_sigaction = (ruby_sigaction_t*)sighandler;
- }
- else {
- sigact.sa_handler = sighandler;
- }
-# else
- sigact.sa_handler = handler;
- sigact.sa_flags = 0;
-# endif
- }
- else {
- ruby_nocldwait = 0;
- }
- break;
-#endif
#if defined(SA_ONSTACK) && defined(USE_SIGALTSTACK)
case SIGSEGV:
#ifdef SIGBUS
@@ -651,7 +633,7 @@ ruby_signal(int signum, sighandler_t handler)
}
sighandler_t
-posix_signal(int signum, sighandler_t handler)
+ruby_posix_signal(int signum, sighandler_t handler)
{
return ruby_signal(signum, handler);
}
@@ -682,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)
{
@@ -691,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;
@@ -707,35 +700,14 @@ signal_enque(int sig)
ATOMIC_INC(signal_buff.size);
}
-#if RUBY_SIGCHLD
-static rb_atomic_t sigchld_hit;
-/* destructive getter than simple predicate */
-# define GET_SIGCHLD_HIT() ATOMIC_EXCHANGE(sigchld_hit, 0)
-#else
-# define GET_SIGCHLD_HIT() 0
-#endif
-
static void
sighandler(int sig)
{
int old_errnum = errno;
- /* the VM always needs to handle SIGCHLD for rb_waitpid */
- if (sig == RUBY_SIGCHLD) {
-#if RUBY_SIGCHLD
- rb_vm_t *vm = GET_VM();
- ATOMIC_EXCHANGE(sigchld_hit, 1);
-
- /* avoid spurious wakeup in main thread if and only if nobody uses trap(:CHLD) */
- if (vm && ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig])) {
- signal_enque(sig);
- }
-#endif
- }
- else {
- signal_enque(sig);
- }
+ signal_enque(sig);
rb_thread_wakeup_timer_thread(sig);
+
#if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL)
ruby_signal(sig, sighandler);
#endif
@@ -746,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
@@ -774,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]);
@@ -789,13 +761,15 @@ rb_get_next_signal(void)
#if defined SIGSEGV || defined SIGBUS || defined SIGILL || defined SIGFPE
static const char *received_signal;
-# define clear_received_signal() (void)(ruby_disable_gc = 0, received_signal = 0)
+# define clear_received_signal() do { \
+ if (GET_VM() != NULL) rb_gc_enable(); \
+ received_signal = 0; \
+} while (0)
#else
# define clear_received_signal() ((void)0)
#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__))
@@ -841,7 +815,8 @@ check_stack_overflow(int sig, const uintptr_t addr, const ucontext_t *ctx)
const greg_t bp = mctx->gregs[REG_EBP];
# endif
# elif defined __APPLE__
-# if __DARWIN_UNIX03
+# include <AvailabilityMacros.h>
+# if defined(MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
# define MCTX_SS_REG(reg) __ss.__##reg
# else
# define MCTX_SS_REG(reg) ss.reg
@@ -880,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);
@@ -898,23 +877,28 @@ 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
+
# ifdef _WIN32
# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, 0)
# else
# define FAULT_ADDRESS info->si_addr
# ifdef USE_UCONTEXT_REG
-# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, (uintptr_t)FAULT_ADDRESS, ctx)
+# define CHECK_STACK_OVERFLOW_() check_stack_overflow(sig, (uintptr_t)FAULT_ADDRESS, ctx)
# else
-# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, FAULT_ADDRESS)
+# define CHECK_STACK_OVERFLOW_() check_stack_overflow(sig, FAULT_ADDRESS)
# endif
# define MESSAGE_FAULT_ADDRESS " at %p", FAULT_ADDRESS
+# define SIGNAL_FROM_USER_P() ((info)->si_code == SI_USER)
+# define CHECK_STACK_OVERFLOW() (SIGNAL_FROM_USER_P() ? (void)0 : CHECK_STACK_OVERFLOW_())
# endif
#else
# define CHECK_STACK_OVERFLOW() (void)0
@@ -924,10 +908,10 @@ check_stack_overflow(int sig, const void *addr)
#endif
#if defined SIGSEGV || defined SIGBUS || defined SIGILL || defined SIGFPE
-NOINLINE(static void check_reserved_signal_(const char *name, size_t name_len));
+NOINLINE(static void check_reserved_signal_(const char *name, size_t name_len, int signo));
/* noinine to reduce stack usage in signal handers */
-#define check_reserved_signal(name) check_reserved_signal_(name, sizeof(name)-1)
+#define check_reserved_signal(name) check_reserved_signal_(name, sizeof(name)-1, sig)
#ifdef SIGBUS
@@ -965,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;
@@ -999,38 +997,57 @@ ruby_abort(void)
}
static void
-check_reserved_signal_(const char *name, size_t name_len)
+check_reserved_signal_(const char *name, size_t name_len, int signo)
{
const char *prev = ATOMIC_PTR_EXCHANGE(received_signal, name);
if (prev) {
ssize_t RB_UNUSED_VAR(err);
-#define NOZ(name, str) name[sizeof(str)-1] = str
+ static const int stderr_fd = 2;
+#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");
#ifdef HAVE_WRITEV
struct iovec iov[4];
+ int i = 0;
+# define W(str, len) \
+ iov[i++] = (struct iovec){.iov_base = (void *)(str), .iov_len = (len)}
+#else
+# define W(str, len) err = write(stderr_fd, (str), (len))
+#endif
+
+#if __has_feature(address_sanitizer) || \
+ __has_feature(memory_sanitizer) || \
+ defined(HAVE_VALGRIND_MEMCHECK_H)
+# define SANITIZING true
+#else
+# define SANITIZING false
+#endif
- iov[0].iov_base = (void *)name;
- iov[0].iov_len = name_len;
- iov[1].iov_base = (void *)msg1;
- iov[1].iov_len = sizeof(msg1);
- iov[2].iov_base = (void *)prev;
- iov[2].iov_len = strlen(prev);
- iov[3].iov_base = (void *)msg2;
- iov[3].iov_len = sizeof(msg2);
- err = writev(2, iov, 4);
+#ifdef SIGABRT
+// Avoid infinite loop when already aborting
+# define RECURSIVE (signo == SIGABRT)
#else
- err = write(2, name, name_len);
- err = write(2, msg1, sizeof(msg1));
- err = write(2, prev, strlen(prev));
- err = write(2, msg2, sizeof(msg2));
+# 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));
+ W(msg2, sizeof(msg2));
+# undef W
+#ifdef HAVE_WRITEV
+ err = writev(stderr_fd, iov, i);
#endif
ruby_abort();
}
- ruby_disable_gc = 1;
+ if (GET_VM() != NULL) {
+ rb_gc_disable_no_rest();
+ }
}
#endif
@@ -1061,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();
@@ -1085,16 +1102,6 @@ rb_vm_trap_exit(rb_vm_t *vm)
}
}
-void ruby_waitpid_all(rb_vm_t *); /* process.c */
-
-void
-ruby_sigchld_handler(rb_vm_t *vm)
-{
- if (SIGCHLD_LOSSY || GET_SIGCHLD_HIT()) {
- ruby_waitpid_all(vm);
- }
-}
-
/* returns true if a trap handler was run, false otherwise */
int
rb_signal_exec(rb_thread_t *th, int sig)
@@ -1162,7 +1169,7 @@ default_handler(int sig)
#ifdef SIGUSR2
case SIGUSR2:
#endif
-#if RUBY_SIGCHLD
+#ifdef RUBY_SIGCHLD
case RUBY_SIGCHLD:
#endif
func = sighandler;
@@ -1230,9 +1237,6 @@ trap_handler(VALUE *cmd, int sig)
break;
case 14:
if (memcmp(cptr, "SYSTEM_DEFAULT", 14) == 0) {
- if (sig == RUBY_SIGCHLD) {
- goto sig_dfl;
- }
func = SIG_DFL;
*cmd = 0;
}
@@ -1260,11 +1264,6 @@ trap_handler(VALUE *cmd, int sig)
break;
}
}
- else {
- rb_proc_t *proc;
- GetProcPtr(*cmd, proc);
- (void)proc;
- }
}
return func;
@@ -1359,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.
+ *
+ * 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.
*
- * 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 +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
@@ -1450,6 +1454,7 @@ sig_list(VALUE _)
if (reserved_signal_p(signum)) rb_bug(failed); \
perror(failed); \
} while (0)
+
static int
install_sighandler_core(int signum, sighandler_t handler, sighandler_t *old_handler)
{
@@ -1474,25 +1479,6 @@ install_sighandler_core(int signum, sighandler_t handler, sighandler_t *old_hand
# define force_install_sighandler(signum, handler, old_handler) \
INSTALL_SIGHANDLER(install_sighandler_core(signum, handler, old_handler), #signum, signum)
-#if RUBY_SIGCHLD
-static int
-init_sigchld(int sig)
-{
- sighandler_t oldfunc;
- sighandler_t func = sighandler;
-
- oldfunc = ruby_signal(sig, SIG_DFL);
- if (oldfunc == SIG_ERR) return -1;
- ruby_signal(sig, func);
- ACCESS_ONCE(VALUE, GET_VM()->trap_list.cmd[sig]) = 0;
-
- return 0;
-}
-
-# define init_sigchld(signum) \
- INSTALL_SIGHANDLER(init_sigchld(signum), #signum, signum)
-#endif
-
void
ruby_sig_finalize(void)
{
@@ -1504,7 +1490,6 @@ ruby_sig_finalize(void)
}
}
-
int ruby_enable_coredump = 0;
/*
@@ -1558,6 +1543,12 @@ 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);
/* At this time, there is no subthread. Then sigmask guarantee atomics. */
rb_disable_interrupt();
@@ -1593,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);
@@ -1601,55 +1596,17 @@ Init_signal(void)
install_sighandler(SIGSYS, sig_do_nothing);
#endif
-#if RUBY_SIGCHLD
- init_sigchld(RUBY_SIGCHLD);
+#ifdef RUBY_SIGCHLD
+ install_sighandler(RUBY_SIGCHLD, sighandler);
#endif
rb_enable_interrupt();
}
-#if defined(HAVE_GRANTPT)
-extern int grantpt(int);
-#else
-static int
-fake_grantfd(int masterfd)
+void
+rb_signal_atfork(void)
{
- errno = ENOSYS;
- return -1;
-}
-#define grantpt(fd) fake_grantfd(fd)
+#if defined(HAVE_WORKING_FORK) && !defined(POSIX_SIGNAL) && !defined(SIG_GET)
+ rb_native_mutex_initialize(&sig_check_lock);
#endif
-
-int
-rb_grantpt(int masterfd)
-{
- if (RUBY_SIGCHLD) {
- rb_vm_t *vm = GET_VM();
- int ret, e;
-
- /*
- * Prevent waitpid calls from Ruby by taking waitpid_lock.
- * Pedantically, grantpt(3) is undefined if a non-default
- * SIGCHLD handler is defined, but preventing conflicting
- * waitpid calls ought to be sufficient.
- *
- * We could install the default sighandler temporarily, but that
- * could cause SIGCHLD to be missed by other threads. Blocking
- * SIGCHLD won't work here, either, unless we stop and restart
- * timer-thread (as only timer-thread sees SIGCHLD), but that
- * seems like overkill.
- */
- rb_nativethread_lock_lock(&vm->waitpid_lock);
- {
- ret = grantpt(masterfd); /* may spawn `pt_chown' and wait on it */
- if (ret < 0) e = errno;
- }
- rb_nativethread_lock_unlock(&vm->waitpid_lock);
-
- if (ret < 0) errno = e;
- return ret;
- }
- else {
- return grantpt(masterfd);
- }
}