Merge the ASan fix for nested entries on macOS

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Malte Ubl
2026-10-26 13:34:52 -07:00
co-authored by Claude Opus 5.5
+17 -1
View File
@@ -92,6 +92,7 @@ void __sanitizer_start_switch_fiber(void **fake_stack_save, const void *bottom,
void __sanitizer_finish_switch_fiber(void *fake_stack_save, const void **bottom_old,
size_t *size_old);
void __lsan_register_root_region(const void *p, size_t size) __attribute__((weak));
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
/* LeakSanitizer's report at exit, with Node loaded: Node keeps its root
* certificate stores (the bundled roots, the system's with
@@ -940,10 +941,15 @@ static void (*ni_nested_job)(void *) = NULL;
static void *ni_nested_arg = NULL;
static ucontext_t *ni_nested_return = NULL;
#ifdef NI_ASAN
static uintptr_t ni_nested_top = 0; /* the nested context's stack top */
#endif
static void ni_nested_trampoline(void) {
#ifdef NI_ASAN
const void *caller_bottom;
size_t caller_size;
uintptr_t top = ni_nested_top;
__sanitizer_finish_switch_fiber(NULL, &caller_bottom, &caller_size);
#endif
void (*job)(void *) = ni_nested_job;
@@ -951,7 +957,14 @@ static void ni_nested_trampoline(void) {
ucontext_t *back = ni_nested_return;
job(arg);
#ifdef NI_ASAN
/* This context ends: no fake stack to keep. */
/* This context ends: no fake stack to keep. Its frame never returns, so
* the redzones ASan poisoned around this frame's locals would stay in
* the shadow of Node's stack, where Node's own frames (uninstrumented)
* later trip over them. Linux's ASan clears a context's stack when it is
* switched to (its swapcontext interceptor); macOS's has none, so this
* frame's part of the stack is unpoisoned here. */
uintptr_t low = ((uintptr_t)__builtin_frame_address(0) - 4096) & ~(uintptr_t)7;
if (top > low) __asan_unpoison_memory_region((const void *)low, top - low);
__sanitizer_start_switch_fiber(NULL, caller_bottom, caller_size);
#endif
setcontext(back);
@@ -970,6 +983,9 @@ static void ni_on_node_stack_below_mark(void (*job)(void *), void *arg) {
if (scr_node_stack_low != 0 && scr_node_stack_low < top) top = scr_node_stack_low;
uintptr_t hi = (top - 1024) & ~(uintptr_t)15;
ni_make_context(&nested, lo, hi - (uintptr_t)lo, ni_nested_trampoline);
#ifdef NI_ASAN
ni_nested_top = hi;
#endif
ni_nested_job = job;
ni_nested_arg = arg;
ni_nested_return = &caller;