Skip to content

Commit cc61b03

Browse files
committed
src,win: disable Maglev when Windows CET shadow stacks are active
When a Node.js process runs on a Windows build that enforces hardware CET (Control-flow Enforcement Technology) shadow stacks but the binary was not compiled with V8_ENABLE_CET_SHADOW_STACK support, V8's Maglev deoptimizer reconstructs call stack frames without synchronizing the hardware shadow stack. When the CPU subsequently executes a RET instruction, the return address on the regular stack does not match the address on the shadow stack, causing a STATUS_STACK_BUFFER_OVERRUN (0xC0000409) termination via __fastfail with no JavaScript stack trace. Detect active CET shadow stacks at startup via GetProcessMitigationPolicy(ProcessUserShadowStackPolicy) and automatically apply --no-maglev when the binary lacks V8_ENABLE_CET_SHADOW_STACK support. TurboFan remains active so JIT performance and fetch() are preserved. The detection is a no-op on platforms without CET and compiles to nothing when V8_ENABLE_CET_SHADOW_STACK is enabled, making this fully forward- compatible with future V8 CET support. Also register --maglev and --no-maglev as kAllowedInEnvvar options so users can override the auto-detection via NODE_OPTIONS. Fixes: #62260
1 parent 4579957 commit cc61b03

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

src/node.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@
119119
#include <unistd.h> // STDIN_FILENO, STDERR_FILENO
120120
#endif
121121

122+
#if defined(_WIN32)
123+
// For GetProcessMitigationPolicy(), PROCESS_MITIGATION_USER_SHADOW_STACK_POLICY,
124+
// and IsUserCetAvailableInEnvironment(), used in the CET shadow stack detection
125+
// in InitializeNodeWithArgsInternal().
126+
#include <processthreadsapi.h>
127+
#endif // defined(_WIN32)
128+
122129
#include "absl/synchronization/mutex.h"
123130

124131
// ========== global C++ headers ==========
@@ -867,6 +874,46 @@ static ExitCode InitializeNodeWithArgsInternal(
867874
// default value.
868875
V8::SetFlagsFromString("--rehash-snapshot");
869876

877+
#if defined(_WIN32) && !defined(V8_ENABLE_CET_SHADOW_STACK)
878+
// When Windows CET (Control-flow Enforcement Technology) hardware shadow
879+
// stacks are active but Node.js was not compiled with
880+
// V8_ENABLE_CET_SHADOW_STACK support, V8's Maglev deoptimizer does not
881+
// synchronize the hardware shadow stack. This causes a
882+
// STATUS_STACK_BUFFER_OVERRUN (0xC0000409) crash when Maglev JIT-compiled
883+
// code deoptimizes on Windows versions that enforce CET strictly (e.g.
884+
// Windows 11 Insider builds).
885+
//
886+
// Automatically disable the Maglev tier when CET shadow stacks are active.
887+
// TurboFan remains enabled, preserving JIT performance and fetch()
888+
// functionality. The user can explicitly re-enable Maglev with --maglev.
889+
{
890+
using IsUserCetAvailableInEnvironment_t = BOOL(WINAPI*)(DWORD);
891+
using GetProcessMitigationPolicy_t =
892+
BOOL(WINAPI*)(HANDLE, PROCESS_MITIGATION_POLICY, PVOID, SIZE_T);
893+
894+
HMODULE kernel32 = ::GetModuleHandleW(L"kernel32.dll");
895+
auto fn_is_cet_available =
896+
reinterpret_cast<IsUserCetAvailableInEnvironment_t>(
897+
::GetProcAddress(kernel32, "IsUserCetAvailableInEnvironment"));
898+
auto fn_get_mitigation_policy =
899+
reinterpret_cast<GetProcessMitigationPolicy_t>(
900+
::GetProcAddress(kernel32, "GetProcessMitigationPolicy"));
901+
902+
if (fn_is_cet_available != nullptr &&
903+
fn_get_mitigation_policy != nullptr &&
904+
fn_is_cet_available(USER_CET_ENVIRONMENT_WIN32_PROCESS)) {
905+
PROCESS_MITIGATION_USER_SHADOW_STACK_POLICY uss_policy{};
906+
if (fn_get_mitigation_policy(GetCurrentProcess(),
907+
ProcessUserShadowStackPolicy,
908+
&uss_policy,
909+
sizeof(uss_policy)) &&
910+
uss_policy.EnableUserShadowStack) {
911+
V8::SetFlagsFromString("--no-maglev");
912+
}
913+
}
914+
}
915+
#endif // defined(_WIN32) && !defined(V8_ENABLE_CET_SHADOW_STACK)
916+
870917
HandleEnvOptions(per_process::cli_options->per_isolate->per_env);
871918

872919
std::string node_options;

src/node_options.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,14 @@ PerIsolateOptionsParser::PerIsolateOptionsParser(
12461246
"disable runtime allocation of executable memory",
12471247
V8Option{},
12481248
kAllowedInEnvvar);
1249+
AddOption("--maglev",
1250+
"enable the Maglev optimizing compiler",
1251+
V8Option{},
1252+
kAllowedInEnvvar);
1253+
AddOption("--no-maglev",
1254+
"disable the Maglev optimizing compiler",
1255+
V8Option{},
1256+
kAllowedInEnvvar);
12491257
AddOption("--report-uncaught-exception",
12501258
"generate diagnostic report on uncaught exceptions",
12511259
&PerIsolateOptions::report_uncaught_exception,

0 commit comments

Comments
 (0)