From 90d010347c65086b17f9802dd9a7d2da90dc68eb Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 8 Feb 2026 13:35:59 +0000 Subject: [PATCH] fix: use unprotected V8 platform to prevent SIGSEGV on x86_64 Linux The default V8 platform uses Memory Protection Keys (pkeys) which require all V8-using threads to be descendants of the thread that called v8::Initialize. Tokio's spawn_blocking pool threads don't satisfy this, causing SIGSEGV in WasmCodePointerTable during isolate creation on x86_64 Linux. Switch to new_unprotected_default_platform which relaxes the pkey requirement. Also remove --single-threaded V8 flag (was degrading performance without fixing the issue) and scope the creation mutex to just JsRuntime::new() instead of the entire lifecycle. See: https://github.com/denoland/deno_core/issues/952 Co-Authored-By: Claude Opus 4.6 --- backend/windmill-runtime-nativets/src/lib.rs | 21 ++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/backend/windmill-runtime-nativets/src/lib.rs b/backend/windmill-runtime-nativets/src/lib.rs index f44cd33847..30b30dd989 100644 --- a/backend/windmill-runtime-nativets/src/lib.rs +++ b/backend/windmill-runtime-nativets/src/lib.rs @@ -125,10 +125,9 @@ pub struct NativeAnnotation { pub proxy: Option<(String, Option<(String, String)>)>, } -/// Serializes V8 isolate creation to work around a V8 bug in -/// `WasmCodePointerTable::AllocateUninitializedEntry()` that causes SIGSEGV -/// when multiple isolates are created concurrently on x86_64 Linux. -/// See: https://github.com/denoland/deno_core/issues/952 +/// Serializes V8 isolate creation as defense-in-depth against concurrent +/// creation races on x86_64 Linux. The primary fix is using the unprotected +/// V8 platform (see `setup_deno_runtime`). static V8_ISOLATE_CREATE_LOCK: Mutex<()> = Mutex::new(()); /// Guard that terminates a running V8 isolate when dropped (e.g. on job cancellation). @@ -171,7 +170,14 @@ pub fn setup_deno_runtime() -> anyhow::Result<()> { println!("Unrecognized V8 flags: {:?}", unrecognized_v8_flags); } - deno_core::JsRuntime::init_platform(None, false); + // Use an unprotected platform that doesn't enforce thread-isolated allocations + // via Memory Protection Keys (pkeys). The default platform requires all V8-using + // threads to be descendants of the thread that called v8::Initialize, but tokio's + // spawn_blocking pool threads don't satisfy this. Without this, V8 crashes with + // SIGSEGV in WasmCodePointerTable::AllocateUninitializedEntry() on x86_64 Linux. + // See: https://github.com/denoland/deno_core/issues/952 + let platform = deno_core::v8::new_unprotected_default_platform(0, false).make_shared(); + deno_core::JsRuntime::init_platform(Some(platform), false); Ok(()) } @@ -458,8 +464,11 @@ pub async fn eval_fetch_timeout( let (memory_limit_tx, mut memory_limit_rx) = mpsc::unbounded_channel::<()>(); + // Serialize isolate creation as extra safety net against concurrent V8 + // isolate creation races. The main fix is the unprotected platform in + // setup_deno_runtime(), but this provides defense in depth. let mut js_runtime = { - let _lock = V8_ISOLATE_CREATE_LOCK + let _v8_lock = V8_ISOLATE_CREATE_LOCK .lock() .unwrap_or_else(|e| e.into_inner()); JsRuntime::new(options)