On OpenBSD amd64 current, using Node 24.18 and Yarn (4.17).
Randomly the command 'yarn install' fails with:
RangeError: ansi-styles@npm:3.2.1: Maximum call stack size exceeded
at wasm-function[104]:0xbc29
at wasm-function[104]:0xbc9d
... 7506 frames of wasm-function[104] ...
at wasm-function[188]:0x11867
at new PI (.../yarn.js:148:193125)
at new hs (.../yarn.js:148:199501)
at Object.Dit (.../yarn.js:191:212680)
at async e.fetchFromNetwork (.../yarn.js:693:3984)
at async e.fetchPackageFromCache (.../yarn.js:198:3374)
at async e.fetch (.../yarn.js:693:3337)
With dchecks enabled it got caught on:
Fatal error in ../deps/v8/src/wasm/jump-table-assembler.h, line 163
Debug check failed: kJumpTableSlotSize == jtasm.pc_offset() (16 vs. 24).
Trace/BPT trap (core dumped)
This needs x64/amd64 with V8_ENABLE_CET_IBT and a code-space layout where the
compiled Wasm target is outside the rel32 range of its primary jump-table slot.
With CET, an x64 Wasm jump-table slot is 16 bytes:
endbr64; nop; jmp rel32; int3 padding.
The buggy path tries a direct jump first. EmitJumpSlot() writes endbr64; nop
before checking rel32 reachability. If the target is too far, it returns false
after advancing pc_ by 8 bytes. The far-jump fallback then emits another
endbr64; nop and the final jmp starts at slot+16, overwriting the next slot.
Keep EmitJumpSlot() side-effect-free on failure and compute the rel32
displacement from the real jmp location, i.e. after the CET marker.
Maybe someone can follow on the process of reviewing and submit the below diff?
> static constexpr int kCodeEntryMarkerSize = 2 * kInt32Size;
This needs a symbol instead of "2 * kInt32Size".
Anyway, feel free to adjust the code.
Index: deps/v8/src/wasm/jump-table-assembler.cc
--- deps/v8/src/wasm/jump-table-assembler.cc.orig
+++ deps/v8/src/wasm/jump-table-assembler.cc
@@ -115,15 +115,14 @@ void JumpTableAssembler::EmitLazyCompileJumpSlot(uint3
bool JumpTableAssembler::EmitJumpSlot(Address target) {
#ifdef V8_ENABLE_CET_IBT
- uint32_t endbr_insn = 0xfa1e0ff3;
- uint32_t nop = 0x00401f0f;
- emit<uint32_t>(endbr_insn, kRelaxedStore);
- // Add a nop to ensure that the next block is 8 byte aligned.
- emit<uint32_t>(nop, kRelaxedStore);
+ static constexpr int kCodeEntryMarkerSize = 2 kInt32Size;
+#else
+ static constexpr int kCodeEntryMarkerSize = 0;
#endif
intptr_t displacement =
- target - (pc_ + MacroAssembler::kIntraSegmentJmpInstrSize);
+ target - (pc_ + kCodeEntryMarkerSize +
+ MacroAssembler::kIntraSegmentJmpInstrSize);
if (!is_int32(displacement)) return false;
uint8_t inst[kJumpTableSlotSize] = {
@@ -134,6 +133,13 @@ bool JumpTableAssembler::EmitJumpSlot(Address target)
memcpy(&inst[1], &displacement32, sizeof(int32_t));
// The jump table is updated live, so the write has to be atomic.
+#ifdef V8_ENABLE_CET_IBT
+ uint32_t endbr_insn = 0xfa1e0ff3;
+ uint32_t nop = 0x00401f0f;
+ emit<uint32_t>(endbr_insn, kRelaxedStore);
+ // Add a nop to ensure that the next block is 8 byte aligned.
+ emit<uint32_t>(nop, kRelaxedStore);
+#endif
emit<uint64_t>(reinterpret_cast<uint64_t*>(inst), kRelaxedStore);
return true;