Disable Mach exception handlers when read barriers in place
The GC compaction mechanism implements a kind of read barrier by marking some (OS) pages as unreadable, and installing a SIGBUS/SIGSEGV handler to detect when they're accessed and invalidate an attempt to move the object. Unfortunately, when a debugger is attached to the Ruby interpreter on Mac OS, the debugger will trap the EXC_BAD_ACCES mach exception before the runtime can transform that into a SIGBUS signal and dispatch it. Thus, execution gets stuck; any attempt to continue from the debugger re-executes the line that caused the exception and no forward progress can be made. This makes it impossible to debug either the Ruby interpreter or a C extension whilst compaction is in use. To fix this, we disable the EXC_BAD_ACCESS handler when installing the SIGBUS/SIGSEGV handlers, and re-enable them once the compaction is done. The debugger will still trap on the attempt to read the bad page, but it will be trapping the SIGBUS signal, rather than the EXC_BAD_ACCESS mach exception. It's possible to continue from this in the debugger, which invokes the signal handler and allows forward progress to be made.
This commit is contained in:
parent
7f05f7378d
commit
05ffc037ad
Notes:
git
2022-06-18 00:10:48 +09:00
@ -3129,6 +3129,15 @@ AS_CASE(["$target_cpu-$target_os"],
|
||||
AS_IF([test "x$ac_cv_header_execinfo_h" = xyes], [
|
||||
AC_CHECK_LIB([execinfo], [backtrace])
|
||||
AC_CHECK_HEADERS([libunwind.h])
|
||||
|
||||
AC_CHECK_HEADERS([mach/task.h mach/mach_init.h mach/mach_port.h])
|
||||
AS_IF([ test \
|
||||
"x${ac_cv_header_mach_task_h}" = xyes -a \
|
||||
"x${ac_cv_header_mach_mach_init_h}" = xyes -a \
|
||||
"x${ac_cv_header_mach_mach_port_h}" = xyes \
|
||||
], [
|
||||
AC_DEFINE([HAVE_MACH_TASK_EXCEPTION_PORTS], [1])
|
||||
])
|
||||
])],
|
||||
[*-freebsd*|x86_64-netbsd*], [
|
||||
AC_CHECK_HEADERS([execinfo.h])
|
||||
|
50
gc.c
50
gc.c
@ -87,6 +87,11 @@
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS
|
||||
# include <mach/task.h>
|
||||
# include <mach/mach_init.h>
|
||||
# include <mach/mach_port.h>
|
||||
#endif
|
||||
#undef LIST_HEAD /* ccan/list conflicts with BSD-origin sys/queue.h. */
|
||||
|
||||
#include "constant.h"
|
||||
@ -5277,6 +5282,38 @@ install_handlers(void)
|
||||
static struct sigaction old_sigbus_handler;
|
||||
static struct sigaction old_sigsegv_handler;
|
||||
|
||||
#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS
|
||||
static exception_mask_t old_exception_masks[32];
|
||||
static mach_port_t old_exception_ports[32];
|
||||
static exception_behavior_t old_exception_behaviors[32];
|
||||
static thread_state_flavor_t old_exception_flavors[32];
|
||||
static mach_msg_type_number_t old_exception_count;
|
||||
|
||||
static void
|
||||
disable_mach_bad_access_exc(void)
|
||||
{
|
||||
old_exception_count = sizeof(old_exception_masks) / sizeof(old_exception_masks[0]);
|
||||
task_swap_exception_ports(
|
||||
mach_task_self(), EXC_MASK_BAD_ACCESS,
|
||||
MACH_PORT_NULL, EXCEPTION_DEFAULT, 0,
|
||||
old_exception_masks, &old_exception_count,
|
||||
old_exception_ports, old_exception_behaviors, old_exception_flavors
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
restore_mach_bad_access_exc(void)
|
||||
{
|
||||
for (mach_msg_type_number_t i = 0; i < old_exception_count; i++) {
|
||||
task_set_exception_ports(
|
||||
mach_task_self(),
|
||||
old_exception_masks[i], old_exception_ports[i],
|
||||
old_exception_behaviors[i], old_exception_flavors[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
read_barrier_signal(int sig, siginfo_t * info, void * data)
|
||||
{
|
||||
@ -5291,11 +5328,16 @@ read_barrier_signal(int sig, siginfo_t * info, void * data)
|
||||
sigaddset(&set, SIGBUS);
|
||||
sigaddset(&set, SIGSEGV);
|
||||
sigprocmask(SIG_UNBLOCK, &set, &prev_set);
|
||||
|
||||
#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS
|
||||
disable_mach_bad_access_exc();
|
||||
#endif
|
||||
// run handler
|
||||
read_barrier_handler((uintptr_t)info->si_addr);
|
||||
|
||||
// reset SEGV/BUS handlers
|
||||
#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS
|
||||
restore_mach_bad_access_exc();
|
||||
#endif
|
||||
sigaction(SIGBUS, &prev_sigbus, NULL);
|
||||
sigaction(SIGSEGV, &prev_sigsegv, NULL);
|
||||
sigprocmask(SIG_SETMASK, &prev_set, NULL);
|
||||
@ -5304,6 +5346,9 @@ read_barrier_signal(int sig, siginfo_t * info, void * data)
|
||||
static void
|
||||
uninstall_handlers(void)
|
||||
{
|
||||
#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS
|
||||
restore_mach_bad_access_exc();
|
||||
#endif
|
||||
sigaction(SIGBUS, &old_sigbus_handler, NULL);
|
||||
sigaction(SIGSEGV, &old_sigsegv_handler, NULL);
|
||||
}
|
||||
@ -5319,6 +5364,9 @@ install_handlers(void)
|
||||
|
||||
sigaction(SIGBUS, &action, &old_sigbus_handler);
|
||||
sigaction(SIGSEGV, &action, &old_sigsegv_handler);
|
||||
#ifdef HAVE_MACH_TASK_EXCEPTION_PORTS
|
||||
disable_mach_bad_access_exc();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user