Fix NAPI finalizer segfault when running rspack/rsbuild

Fixes #24552

When NAPI modules (like rspack/rsbuild via SWC) are used in subprocesses,
finalizers can be enqueued during garbage collection but may attempt to run
after the NapiEnv has started tearing down, causing segmentation faults at
address 0x0.

The crash occurs because:
1. NAPI objects with finalizers are garbage collected
2. Finalizers are enqueued to run on the next tick
3. The NapiEnv begins cleanup (m_globalObject becomes null/invalid or VM terminates)
4. When the finalizer runs, it dereferences the invalid globalObject pointer

The fix:
- Add NapiEnv__canRunFinalizer() to check if env, globalObject are valid and VM is not terminating
- Call this check in Finalizer.run() before accessing the env
- Skip finalizer execution if the env is no longer valid

This prevents the segfault while ensuring finalizers run when safe to do so.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2025-11-11 03:29:11 +00:00
parent 0a307ed880
commit fa198b1441
2 changed files with 15 additions and 0 deletions

View File

@@ -2970,4 +2970,10 @@ extern "C" void NapiEnv__deref(napi_env env)
env->deref();
}
extern "C" bool NapiEnv__canRunFinalizer(napi_env env)
{
// Check if the global object is still valid and VM is not terminating
return env && env->globalObject() && !env->isVMTerminating();
}
}

View File

@@ -1366,6 +1366,7 @@ pub export fn napi_internal_suppress_crash_on_abort_if_desired() void {
}
extern fn napi_internal_remove_finalizer(env: napi_env, fun: napi_finalize, hint: ?*anyopaque, data: ?*anyopaque) callconv(.C) void;
extern fn NapiEnv__canRunFinalizer(*NapiEnv) bool;
pub const Finalizer = struct {
env: NapiEnv.Ref,
@@ -1375,6 +1376,14 @@ pub const Finalizer = struct {
pub fn run(this: *Finalizer) void {
const env = this.env.get();
// Safety check: Ensure the env is still valid before running the finalizer.
// This prevents crashes when finalizers are enqueued but the NapiEnv is being
// torn down (e.g., when a subprocess using NAPI modules is terminating).
if (!NapiEnv__canRunFinalizer(env)) {
return;
}
const handle_scope = NapiHandleScope.open(env, false);
defer if (handle_scope) |scope| scope.close(env);