From bc88a45ce239cb5c07b8dfaac7b09448643d6d26 Mon Sep 17 00:00:00 2001 From: slipher Date: Mon, 27 Jul 2026 05:35:54 -0500 Subject: [PATCH 1/4] Fix and enable debug_stub_test on Windows It needed a Python 3 migration to signed process exit code. --- tests/debug_stub/debug_stub_test.py | 2 +- tests/debug_stub/nacl.scons | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/debug_stub/debug_stub_test.py b/tests/debug_stub/debug_stub_test.py index 6c8f0d6467..43393aee6c 100644 --- a/tests/debug_stub/debug_stub_test.py +++ b/tests/debug_stub/debug_stub_test.py @@ -15,7 +15,7 @@ if sys.platform == 'win32': - RETURNCODE_KILL = -9 + RETURNCODE_KILL = -9 & 0xffffffff else: RETURNCODE_KILL = -9 & 0xff diff --git a/tests/debug_stub/nacl.scons b/tests/debug_stub/nacl.scons index d0639d1392..c858de3268 100644 --- a/tests/debug_stub/nacl.scons +++ b/tests/debug_stub/nacl.scons @@ -30,8 +30,7 @@ if env.GetEmulator(): # qualification tests because they fail under qemu-arm. sel_ldr_command = [env.GetEmulator()] + sel_ldr_command + ['-Q'] -is_broken = (not env.Bit('nacl_static_link') or env.GetSelLdr() is None or - env.Bit('host_windows')) +is_broken = not env.Bit('nacl_static_link') or env.GetSelLdr() is None if env.Bit('build_arm') or env.Bit('build_mips32'): # Use the system's 'nm' tool. The NaCl toolchain will not be @@ -54,7 +53,7 @@ node = env.CommandTest( extra_deps=[env.File('gdb_rsp.py')], # Don't run the host Python through qemu-arm. direct_emulation=False, - size='medium') + size='huge' if env.Bit('host_windows') else 'medium') env.AddNodeToTestSuite(node, ['medium_tests', 'nonpexe_tests'], 'run_debug_stub_test', is_broken=is_broken) env.TestBindsFixedTcpPort(node) From 4bdc85c6d70db0a63866358a410bd13fba8194d8 Mon Sep 17 00:00:00 2001 From: slipher Date: Mon, 27 Jul 2026 21:13:44 -0500 Subject: [PATCH 2/4] Fix printf crash in run_patch_ntdll_test For some reason functions in the printf family were crashing when called from the crash handler in run_patch_ntdll_test. Avoid this by switching them to (f)puts instead of (f)printf, and re-enable the test. Also modify the handler to do an infinite loop (thus timing out the test) if unexpected extra crashes happen. --- src/trusted/service_runtime/build.scons | 5 +---- .../win/exception_patch/ntdll_test.c | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/trusted/service_runtime/build.scons b/src/trusted/service_runtime/build.scons index 70833a48ed..a2728cf1f5 100644 --- a/src/trusted/service_runtime/build.scons +++ b/src/trusted/service_runtime/build.scons @@ -652,10 +652,7 @@ if env.Bit('windows') and env.Bit('build_x86_64'): ]) node = env.CommandTest('patch_ntdll_test.out', command=[test_prog], declares_exit_status=True) - # This is broken in opt but not dbg mode. The difference is - # /MT vs. /MTd. - env.AddNodeToTestSuite(node, ['small_tests'], 'run_patch_ntdll_test', - is_broken=True) + env.AddNodeToTestSuite(node, ['small_tests'], 'run_patch_ntdll_test') intercept_test_prog = env.ComponentProgram( 'ntdll_intercept_test', 'win/exception_patch/intercept_test.c', diff --git a/src/trusted/service_runtime/win/exception_patch/ntdll_test.c b/src/trusted/service_runtime/win/exception_patch/ntdll_test.c index f5662a5de9..3464bb6721 100644 --- a/src/trusted/service_runtime/win/exception_patch/ntdll_test.c +++ b/src/trusted/service_runtime/win/exception_patch/ntdll_test.c @@ -22,7 +22,17 @@ void CauseFault(void) { } void ReplacementHandler(void) { - printf("In replacement handler (%i)\n", g_counter++); + if (++g_counter > 5) { + volatile unsigned x = 0; + /* Arrived here more times than expected. Time out the test to show what is + happening and make sure it fails */ + while (1) { + ++x; + } + } + + puts("In replacement handler"); + if (g_counter < 5) { /* * Cause a fault to demonstrate that ReplacementHandler() gets @@ -35,7 +45,7 @@ void ReplacementHandler(void) { */ void (*exit_fast)(void) = (void (*)(void)) NaCl_exception_dispatcher_exit_fast; - fprintf(stderr, "** intended_exit_status=untrusted_segfault\n"); + fputs("** intended_exit_status=untrusted_segfault", stderr); exit_fast(); } } From f292b641cbd591039ca4e7b5f14c545f981850d1 Mon Sep 17 00:00:00 2001 From: slipher Date: Mon, 27 Jul 2026 21:50:02 -0500 Subject: [PATCH 3/4] Windows: use fastfail for exiting after crash Use int 0x29 aka __fastfail (introduced in Windows 8) to implement NaCl_exception_dispatcher_exit_fast. --- .../service_runtime/win/exception_patch/exit_fast.S | 10 +++++++++- .../service_runtime/win/exception_patch/ntdll_test.c | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/trusted/service_runtime/win/exception_patch/exit_fast.S b/src/trusted/service_runtime/win/exception_patch/exit_fast.S index 1a765fc772..a6634e6b86 100644 --- a/src/trusted/service_runtime/win/exception_patch/exit_fast.S +++ b/src/trusted/service_runtime/win/exception_patch/exit_fast.S @@ -10,6 +10,12 @@ * This routine terminates the process safely and is patched onto * NTDLL's KiUserExceptionDispatcher. * + * For Windows 8+ there is the new __fastfail intrinsic (int 0x29) to + * immediately terminate the process. On an old Windows where that is + * not available, the unrecognized interrupt should be treated like + * a generic exception and the next two paragraphs apply (except that + * the faulting instruction is INT instead of HLT). + * * To terminate the process safely, we set %rsp to zero and then * execute a HLT instruction. If we do not set %rsp to zero, the HLT * instruction will cause the kernel to rerun the fault handler after @@ -28,6 +34,8 @@ .globl IDENTIFIER(NaCl_exception_dispatcher_exit_fast) IDENTIFIER(NaCl_exception_dispatcher_exit_fast): xor %rsp, %rsp - hlt + mov $0x6c43614e, %rcx + int $0x29 + hlt /* should be unreachable even on old Windows */ .globl IDENTIFIER(NaCl_exception_dispatcher_exit_fast_end) IDENTIFIER(NaCl_exception_dispatcher_exit_fast_end): diff --git a/src/trusted/service_runtime/win/exception_patch/ntdll_test.c b/src/trusted/service_runtime/win/exception_patch/ntdll_test.c index 3464bb6721..d75a9fa705 100644 --- a/src/trusted/service_runtime/win/exception_patch/ntdll_test.c +++ b/src/trusted/service_runtime/win/exception_patch/ntdll_test.c @@ -45,7 +45,8 @@ void ReplacementHandler(void) { */ void (*exit_fast)(void) = (void (*)(void)) NaCl_exception_dispatcher_exit_fast; - fputs("** intended_exit_status=untrusted_segfault", stderr); + // fast fail exception code: 0xC0000409 + fputs("** intended_exit_status=3221226505", stderr); exit_fast(); } } From 3681df7492dbbb0a6c7582ddeed51bad39ed71b7 Mon Sep 17 00:00:00 2001 From: slipher Date: Mon, 27 Jul 2026 22:06:18 -0500 Subject: [PATCH 4/4] Robustly dereference the null page! Fixes run_patch_ntdll_test on MinGW. MinGW GCC 16 decided to be clever and optimize out `*(int*)0 = 0`. It does this even if I add `volatile`. So change the target address to 4 instead of 0 so the special null pointer optimization can't be applied. Also do this for the other 14 places where a hard-coded null pointer is dereferenced. --- src/trusted/service_runtime/linux/nacl_bootstrap.c | 2 +- .../service_runtime/win/exception_patch/intercept_test.c | 4 ++-- .../service_runtime/win/exception_patch/ntdll_test.c | 2 +- tests/exception_test/exception_crash_test.c | 8 ++++---- tests/minnacl/minimal_test_guest.c | 2 +- tests/nonsfi/sigaction_test.cc | 2 +- tests/nonsfi/signal_test.cc | 2 +- tests/performance/perf_test_exceptions.cc | 2 +- tests/signal_handler/crash_test.c | 2 +- tests/trusted_crash/osx_crash_filter/crash_filter_test.c | 2 +- .../osx_crash_forwarding/mach_crash_forwarding_test.c | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/trusted/service_runtime/linux/nacl_bootstrap.c b/src/trusted/service_runtime/linux/nacl_bootstrap.c index 4cf204cb1e..70010cb101 100644 --- a/src/trusted/service_runtime/linux/nacl_bootstrap.c +++ b/src/trusted/service_runtime/linux/nacl_bootstrap.c @@ -156,7 +156,7 @@ __attribute__((noreturn)) static void fail(const char *filename, sys_writev(2, iov, niov); sys_exit_group(2); - while (1) *(volatile int *) 0 = 0; /* Crash. */ + while (1) *(volatile int *) 4 = 0; /* Crash. */ } diff --git a/src/trusted/service_runtime/win/exception_patch/intercept_test.c b/src/trusted/service_runtime/win/exception_patch/intercept_test.c index ad1aac67f2..7a87d15b7a 100644 --- a/src/trusted/service_runtime/win/exception_patch/intercept_test.c +++ b/src/trusted/service_runtime/win/exception_patch/intercept_test.c @@ -31,7 +31,7 @@ static void TryCrash(void) { void *handlerHandle = AddVectoredExceptionHandler(/*First=*/0, TheHandler); CHECK(handlerHandle); continueLabel = &&cleanup; - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; cleanup: continueLabel = NULL; printf("Caught exception OK\n"); @@ -42,7 +42,7 @@ static void TryCrash(void) { static void TryCrash(void) { __try { - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } __except (EXCEPTION_EXECUTE_HANDLER) { printf("Caught exception OK\n"); } diff --git a/src/trusted/service_runtime/win/exception_patch/ntdll_test.c b/src/trusted/service_runtime/win/exception_patch/ntdll_test.c index d75a9fa705..879a3f7b1b 100644 --- a/src/trusted/service_runtime/win/exception_patch/ntdll_test.c +++ b/src/trusted/service_runtime/win/exception_patch/ntdll_test.c @@ -18,7 +18,7 @@ static int g_counter = 0; void CauseFault(void) { - *(int *) 0 = 0; + *(volatile int *) 4 = 0; } void ReplacementHandler(void) { diff --git a/tests/exception_test/exception_crash_test.c b/tests/exception_test/exception_crash_test.c index cf57abd7ed..e571634012 100644 --- a/tests/exception_test/exception_crash_test.c +++ b/tests/exception_test/exception_crash_test.c @@ -36,7 +36,7 @@ void test_bad_handler(void) { assert(rc == 0); fprintf(stderr, "** intended_exit_status=untrusted_segfault\n"); /* Cause crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } @@ -155,7 +155,7 @@ void test_stack_in_rwdata(void) { assert(rc == 0); fprintf(stderr, "** intended_exit_status=1\n"); /* Cause crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } @@ -172,7 +172,7 @@ void test_stack_in_rodata(void) { assert(rc == 0); fprintf(stderr, "** intended_exit_status=unwritable_exception_stack\n"); /* Cause crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } @@ -211,7 +211,7 @@ void test_stack_in_code(void) { assert(rc == 0); fprintf(stderr, "** intended_exit_status=unwritable_exception_stack\n"); /* Cause crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } diff --git a/tests/minnacl/minimal_test_guest.c b/tests/minnacl/minimal_test_guest.c index 8bc8804784..ab92aeb098 100644 --- a/tests/minnacl/minimal_test_guest.c +++ b/tests/minnacl/minimal_test_guest.c @@ -14,7 +14,7 @@ static void SimpleAbort(void) { while (1) { /* Exit by causing a crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } } diff --git a/tests/nonsfi/sigaction_test.cc b/tests/nonsfi/sigaction_test.cc index bc7bfa7654..27f0738c82 100644 --- a/tests/nonsfi/sigaction_test.cc +++ b/tests/nonsfi/sigaction_test.cc @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) { rc = setjmp(g_jmp_buf); if (rc == 0) { /* Crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; _exit(1); /* Shouldn't reach here. */ } diff --git a/tests/nonsfi/signal_test.cc b/tests/nonsfi/signal_test.cc index 00e5ec9e8e..e6353cf480 100644 --- a/tests/nonsfi/signal_test.cc +++ b/tests/nonsfi/signal_test.cc @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { int rc = setjmp(g_jmp_buf); if (rc == 0) { /* Crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; _exit(1); /* Shouldn't reach here. */ } diff --git a/tests/performance/perf_test_exceptions.cc b/tests/performance/perf_test_exceptions.cc index f2d2a152dc..1c5891311f 100644 --- a/tests/performance/perf_test_exceptions.cc +++ b/tests/performance/perf_test_exceptions.cc @@ -26,7 +26,7 @@ class TestCatchingFault : public PerfTest { if (!setjmp(return_jmp_buf_)) { // Cause crash. for (;;) - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } } diff --git a/tests/signal_handler/crash_test.c b/tests/signal_handler/crash_test.c index 6f62a8b48b..5ffc94675e 100644 --- a/tests/signal_handler/crash_test.c +++ b/tests/signal_handler/crash_test.c @@ -13,7 +13,7 @@ int main(void) { least on ARM) and generates an illegal instruction, which generates SIGILL instead of SIGSEGV, and LLVM also optimises away the rest of the function. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; fprintf(stderr, "[CRASH_TEST] FAIL: Survived crash attempt\n"); return 1; } diff --git a/tests/trusted_crash/osx_crash_filter/crash_filter_test.c b/tests/trusted_crash/osx_crash_filter/crash_filter_test.c index 93ce13e8ba..8e84780bfe 100644 --- a/tests/trusted_crash/osx_crash_filter/crash_filter_test.c +++ b/tests/trusted_crash/osx_crash_filter/crash_filter_test.c @@ -122,7 +122,7 @@ int main(int argc, char **argv) { g_expect_untrusted = 0; RegisterExceptionHandler(); /* Cause a crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } if (argc != 3) { diff --git a/tests/trusted_crash/osx_crash_forwarding/mach_crash_forwarding_test.c b/tests/trusted_crash/osx_crash_forwarding/mach_crash_forwarding_test.c index cfebb83cb9..f5dbba28a0 100644 --- a/tests/trusted_crash/osx_crash_forwarding/mach_crash_forwarding_test.c +++ b/tests/trusted_crash/osx_crash_forwarding/mach_crash_forwarding_test.c @@ -129,7 +129,7 @@ int main(int argc, char **argv) { if (argc == 2 && strcmp(argv[1], "early_trusted") == 0) { g_expect_crash = 1; /* Cause a crash. */ - *(volatile int *) 0 = 0; + *(volatile int *) 4 = 0; } if (argc != 3) {