(X64+CET enabled) WASM jump table slot overflow on far jump fallback

20 views
Skip to first unread message

Fabien Romano

unread,
6:18 AM (14 hours ago) 6:18 AM
to v8-...@googlegroups.com
Hi,

Second message attempt (I first tried with the online form).

On OpenBSD 7.9-current, with Node 24.18.0 and Yarn 4.17.0.

Note that this could affect any OS(x64) on any WASM code.
Still, the conditions to reproduce are very specific.

The command "yarn install" randomly fails with (example):

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 gets 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.

Can someone follow the process of submitting and reviewing the below diff?

Regarding CLA, the below diff is just a demonstration and not a contribution.
I think "2 * kInt32Size" should be replaced by a symbol.

Please let me know when it get fixed so we can cherry-pick the proper diff.

Thanks,
--
Fabien Romano

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;

Clemens Backes

unread,
6:53 AM (14 hours ago) 6:53 AM
to v8-...@googlegroups.com
Can you open an issue (http://crbug.com/new) and optionally also upload the proposed fix (https://v8.dev/docs/contribute)?

--
--
v8-dev mailing list
v8-...@googlegroups.com
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to v8-dev+un...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/v8-dev/dab6d0eb-c99e-41c9-bbc9-eeb8653cbe2a%40gmail.com.


--

Clemens Backes

Software Engineer

clem...@google.com

Google Germany GmbH

Erika-Mann-Straße 33

80636 München


Geschäftsführer: Paul Manicle, Liana Sebastian   

Registergericht und -nummer: Hamburg, HRB 86891

Sitz der Gesellschaft: Hamburg


Diese E-Mail ist vertraulich. Falls sie diese fälschlicherweise erhalten haben sollten, leiten Sie diese bitte nicht an jemand anderes weiter, löschen Sie alle Kopien und Anhänge davon und lassen Sie mich bitte wissen, dass die E-Mail an die falsche Person gesendet wurde.


This e-mail is confidential. If you received this communication by mistake, please don't forward it to anyone else, please erase all copies and attachments, and please let me know that it has gone to the wrong person.


Fabien Romano

unread,
2:27 PM (6 hours ago) 2:27 PM
to v8-dev
Actually, I didn't want to go through the process of https://v8.dev/docs/contribute.
Reading CLA, setting up depot_tools (fixing features I never tried on OpenBSD) ...
Just for a few lines of diff that seem obvious with the DCHECK at hand.

I copy-paste this email (without diff) in this issue: https://issues.chromium.org/issues/532112743
I hope that's enough for someone to cook a proper fix.

I also have other issues in Node to diagnose and check if they are OpenBSD-specific.
At least one is related to V8 regex JIT.

By the way, do you think any OpenBSD-specific patches are welcome?
If so, going through the contribution process would be worthwhile.

Anyway, thanks for V8, it is a great engine.
Reply all
Reply to author
Forward
0 new messages