[v24.x] deps: V8: backport 0b94a9fd23ba - #65753
Open
ruangustavo wants to merge 1 commit into
Open
Conversation
Original commit message:
[leaptiering] Fix BaselineOutOfLinePrologue builtin
... which tried to preserve kJavaScriptCallDispatchHandleRegister even
on configurations where it's not used which resulted in a random value
on the stack discoverable by GC.
This issue triggered only on non-sandbox configuration with enabled
leaptiering.
Drive-by: fix MacroAssembler::GenerateTailCallToReturnedCode() on riscv
port which wasn't preserving dispatch handle as all the other ports do.
Bug: 42204201
Fixed: 413769394
Change-Id: If146b0b7a6cf972ed5a881142f40980774f19cba
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6587010
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Olivier Flückiger <olivf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#100512}
Node.js 24 builds V8 with leaptiering enabled and the sandbox disabled,
so V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE is not defined and the JS
calling convention does not carry the dispatch handle register (x4 on
arm64). BaselineOutOfLinePrologue and GenerateTailCallToReturnedCode
still pushed that register as a tagged slot of an INTERNAL frame, so
whatever value the caller left there is dereferenced by
ClearStaleLeftTrimmedPointerVisitor during mark-compact root scanning
and crashes the process with SIGSEGV (seen as jest workers dying).
Refs: v8/v8@0b94a9f
Fixes: nodejs#62393
Collaborator
|
Review requested:
|
richardlau
approved these changes
Sep 3, 2026
Collaborator
Collaborator
Collaborator
Collaborator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backport of v8/v8@0b94a9f ("[leaptiering] Fix BaselineOutOfLinePrologue builtin") to the V8 13.6 in
v24.x. Applies cleanly todeps/v8;v8_embedder_stringbumped to-node.54.Fixes: #62393
What crashes
A register that nothing guarantees, pushed on the stack as if it were a tagged pointer, then read by the GC:
sequenceDiagram participant M as JS caller (Sparkplug code) participant CL as CompileLazy (TurboFan/CSA-generated) participant P as BaselineOutOfLinePrologue (hand-written asm) participant GC as mark-compact M->>CL: first call of f() Note over CL: x4 is NOT part of the JS linkage here<br/>(V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE undefined)<br/>register allocator uses x4 as scratch → x4 = 0x7 CL->>P: tail call into freshly compiled baseline code Note over P: stack guard slow path:<br/>Push(x4, new_target) // "dispatch handles always look like Smis" P->>GC: Runtime_StackGuardWithGap → CollectGarbage Note over GC: InternalFrame::Iterate → ClearStaleLeftTrimmedPointerVisitor<br/>IsHeapObject(0x7) is true (low bit set)<br/>reads map_word at 0x7 - 1 GC--xGC: SIGSEGV, KERN_INVALID_ADDRESS at 0x6Node.js builds V8 with leaptiering on and the sandbox off. In that configuration
V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLEis not defined (src/common/globals.h), sokJavaScriptCallDispatchHandleRegister(x4on arm64,r15on x64) is not carried by JS calls. Two hand-written builtins still gated the push onV8_ENABLE_LEAPTIERING_BOOL. The upstream commit message says it directly: "This issue triggered only on non-sandbox configuration with enabled leaptiering."Why only v24:
ClearStaleLeftTrimmedPointerVisitorstarted visiting stack roots in V8 12.6 (v8/v8@22c404b8bbbb, 2024-05). Node 22 ships V8 12.4, so the same stale slot was never dereferenced there. The fix landed in V8 main after the 13.6 branch cut and is present in Node 25/26.Evidence
Instrumented
v24.20.0(macOS arm64) logging every non-Smi, non-pointer value inINTERNALframe slots right before the GC visitor ran. Three independent crashes, identical frame:Matching crash report for the same worker:
EXC_BAD_ACCESS / KERN_INVALID_ADDRESS at 0x0000000000000006inClearStaleLeftTrimmedPointerVisitor::VisitRootPointers←InternalFrame::Iterate.Stack walk at that point (callee = first call of a small module-level helper, i.e. the
CompileLazypath):A temporary check at the builtin entry (
x4 == closure.dispatch_handle, abort otherwise) fires even for:node --always-sparkplug -e 'function f(a,b){return a+b}; f(1,2)'so this is not memory corruption: in this configuration the register simply never holds the handle.
The fix (same as upstream, all ports)
With the linkage macro undefined the slot now receives
padreg(xzr, i.e. Smi 0) instead of whatever was inx4. Same change on x64 (#ifdef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLEaround the push/pop), loong64, mips64 and riscv.Verification
Same jest suite (322 suites / 5184 tests), 4 workers,
--no-maglev --always-sparkplug --max-old-space-size=512, macOS arm64:The last column matters more than the crash count: the instrumentation logged any non-pointer tagged value in an
INTERNALframe slot whether or not a GC happened to hit it.I have not run V8 CI /
make test-v8; please trigger it.Refs: v8/v8@0b94a9f