commit 984eef92f3f7ebe268739fb1c1a3e9875c45a9b5
parent 2cf89694835ff74c2efacb1950b271d31d27f297
Author: Ryan Hunt <rhunt@eqrion.net>
Date: Thu, 18 Dec 2025 16:45:25 +0000
Bug 2002625 - wasm: Move Win32 TIB updating into wasm::Context. r=yury
Continue moving more logic into a single place in
wasm::Context.
Differential Revision: https://phabricator.services.mozilla.com/D274186
Diffstat:
4 files changed, 75 insertions(+), 65 deletions(-)
diff --git a/js/src/wasm/WasmContext.cpp b/js/src/wasm/WasmContext.cpp
@@ -24,12 +24,22 @@
#include "wasm/WasmPI.h"
+#ifdef XP_WIN
+// We only need the `windows.h` header, but this file can get unified built
+// with WasmSignalHandlers.cpp, which requires `winternal.h` to be included
+// before the `windows.h` header, and so we must include it here for that case.
+# include <winternl.h> // must include before util/WindowsWrapper.h's `#undef`s
+
+# include "util/WindowsWrapper.h"
+#endif
+
using namespace js::wasm;
Context::Context()
: triedToInstallSignalHandlers(false),
haveSignalHandlers(false),
- stackLimit(JS::NativeStackLimitMin)
+ stackLimit(JS::NativeStackLimitMin),
+ mainStackLimit(JS::NativeStackLimitMin)
#ifdef ENABLE_WASM_JSPI
,
activeSuspender_(nullptr),
@@ -50,6 +60,16 @@ void Context::initStackLimit(JSContext* cx) {
// The wasm stack limit is the same as the jit stack limit. We also don't
// use the stack limit for triggering interrupts.
stackLimit = cx->jitStackLimitNoInterrupt;
+ mainStackLimit = stackLimit;
+
+ // See the comment on wasm::Context for why we do this.
+#ifdef ENABLE_WASM_JSPI
+# if defined(_WIN32)
+ _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb());
+ tibStackBase_ = tib->StackBase;
+ tibStackLimit_ = tib->StackLimit;
+# endif
+#endif
}
#ifdef ENABLE_WASM_JSPI
@@ -73,17 +93,33 @@ void Context::traceRoots(JSTracer* trc) {
}
}
-void Context::enterSuspendableStack(SuspenderObject* suspender,
- JS::NativeStackLimit newStackLimit) {
+void Context::enterSuspendableStack(SuspenderObject* suspender) {
MOZ_ASSERT(!activeSuspender_);
activeSuspender_ = suspender;
- stackLimit = newStackLimit;
+ stackLimit = suspender->stackMemoryLimitForJit();
+
+ // See the comment on wasm::Context for why we do this.
+# if defined(_WIN32)
+ _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb());
+ tibStackBase_ = tib->StackBase;
+ tibStackLimit = tib->StackLimit;
+ tib->StackBase = reinterpret_cast<void*>(suspender->stackMemoryBase());
+ tib->StackLimit =
+ reinterpret_cast<void*>(suspender->stackMemoryLimitForSystem());
+# endif
}
-void Context::leaveSuspendableStack(JSContext* cx) {
+void Context::leaveSuspendableStack() {
MOZ_ASSERT(activeSuspender_);
activeSuspender_ = nullptr;
- initStackLimit(cx);
+ stackLimit = mainStackLimit;
+
+ // See the comment on wasm::Context for why we do this.
+# if defined(_WIN32)
+ _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb());
+ tib->StackBase = tibStackBase_;
+ tib->StackLimit = tibStackLimit_;
+# endif
}
bool js::IsSuspendableStackActive(JSContext* cx) {
diff --git a/js/src/wasm/WasmContext.h b/js/src/wasm/WasmContext.h
@@ -56,9 +56,8 @@ class Context {
SuspenderObject* activeSuspender() { return activeSuspender_; }
bool onSuspendableStack() const { return activeSuspender_ != nullptr; }
- void enterSuspendableStack(SuspenderObject* suspender,
- JS::NativeStackLimit newStackLimit);
- void leaveSuspendableStack(JSContext* cx);
+ void enterSuspendableStack(SuspenderObject* suspender);
+ void leaveSuspendableStack();
void trace(JSTracer* trc);
void traceRoots(JSTracer* trc);
@@ -73,8 +72,19 @@ class Context {
// use the stack limit for interrupts, but it does update it for stack
// switching.
JS::NativeStackLimit stackLimit;
+ // The original stack limit before any stack switches. Cached for easy
+ // restoration.
+ JS::NativeStackLimit mainStackLimit;
#ifdef ENABLE_WASM_JSPI
+# if defined(_WIN32)
+ // On WIN64, the Thread Information Block stack limits must be updated on
+ // stack switches to avoid failures on SP checks during vectored exeption
+ // handling for traps. We store the original ones here for easy restoration.
+ void* tibStackBase_ = nullptr;
+ void* tibStackLimit_ = nullptr;
+# endif
+
// The currently active suspender object. Null if we're executing on the
// system stack, otherwise we're on a wasm suspendable stack.
HeapPtr<SuspenderObject*> activeSuspender_;
diff --git a/js/src/wasm/WasmPI.cpp b/js/src/wasm/WasmPI.cpp
@@ -53,15 +53,6 @@
# include "jit/mips64/Simulator-mips64.h"
#endif
-#ifdef XP_WIN
-// We only need the `windows.h` header, but this file can get unified built
-// with WasmSignalHandlers.cpp, which requires `winternal.h` to be included
-// before the `windows.h` header, and so we must include it here for that case.
-# include <winternl.h> // must include before util/WindowsWrapper.h's `#undef`s
-
-# include "util/WindowsWrapper.h"
-#endif
-
using namespace js;
using namespace js::jit;
@@ -81,26 +72,6 @@ void SuspenderObjectData::releaseStackMemory() {
stackMemory_ = nullptr;
}
-# if defined(_WIN32)
-// On WIN64, the Thread Information Block stack limits has to be modified to
-// avoid failures on SP checks.
-void SuspenderObjectData::updateTIBStackFields() {
- _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb());
- savedStackBase_ = tib->StackBase;
- savedStackLimit_ = tib->StackLimit;
- uintptr_t stack_limit = (uintptr_t)stackMemory_;
- uintptr_t stack_base = stack_limit + SuspendableStackPlusRedZoneSize;
- tib->StackBase = (void*)stack_base;
- tib->StackLimit = (void*)stack_limit;
-}
-
-void SuspenderObjectData::restoreTIBStackFields() {
- _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb());
- tib->StackBase = savedStackBase_;
- tib->StackLimit = savedStackLimit_;
-}
-# endif
-
# if defined(JS_SIMULATOR_ARM64)
void SuspenderObjectData::switchSimulatorToMain() {
auto* sim = Simulator::Current();
@@ -282,8 +253,16 @@ SuspenderObject* SuspenderObject::create(JSContext* cx) {
return suspender;
}
-JS::NativeStackLimit SuspenderObject::getStackMemoryLimit() {
- return JS::NativeStackLimit(data()->stackMemory()) + SuspendableRedZoneSize;
+JS::NativeStackLimit SuspenderObject::stackMemoryBase() const {
+ return ((uintptr_t)data()->stackMemory()) + SuspendableStackPlusRedZoneSize;
+}
+
+JS::NativeStackLimit SuspenderObject::stackMemoryLimitForSystem() const {
+ return JS::NativeStackLimit(data()->stackMemory());
+}
+
+JS::NativeStackLimit SuspenderObject::stackMemoryLimitForJit() const {
+ return stackMemoryLimitForSystem() + SuspendableRedZoneSize;
}
static_assert(SuspenderObjectDataSlot == SuspenderObject::DataSlot);
@@ -344,10 +323,7 @@ void SuspenderObject::trace(JSTracer* trc, JSObject* obj) {
void SuspenderObject::setMoribund(JSContext* cx) {
MOZ_ASSERT(state() == SuspenderState::Active);
- cx->wasm().leaveSuspendableStack(cx);
-# if defined(_WIN32)
- data()->restoreTIBStackFields();
-# endif
+ cx->wasm().leaveSuspendableStack();
SuspenderObjectData* data = this->data();
data->setState(SuspenderState::Moribund);
data->releaseStackMemory();
@@ -357,18 +333,12 @@ void SuspenderObject::setMoribund(JSContext* cx) {
void SuspenderObject::setActive(JSContext* cx) {
data()->setState(SuspenderState::Active);
- cx->wasm().enterSuspendableStack(this, getStackMemoryLimit());
-# if defined(_WIN32)
- data()->updateTIBStackFields();
-# endif
+ cx->wasm().enterSuspendableStack(this);
}
void SuspenderObject::setSuspended(JSContext* cx) {
data()->setState(SuspenderState::Suspended);
- cx->wasm().leaveSuspendableStack(cx);
-# if defined(_WIN32)
- data()->restoreTIBStackFields();
-# endif
+ cx->wasm().leaveSuspendableStack();
}
void SuspenderObject::enter(JSContext* cx) {
diff --git a/js/src/wasm/WasmPI.h b/js/src/wasm/WasmPI.h
@@ -153,13 +153,6 @@ class SuspenderObjectData
// Identify context that is holding suspended stack, otherwise nullptr.
Context* suspendedBy_;
-# if defined(_WIN32)
- // The storage of main stack limits during stack switching.
- // See updateTibFields and restoreTibFields below.
- void* savedStackBase_;
- void* savedStackLimit_;
-# endif
-
public:
explicit SuspenderObjectData(void* stackMemory);
@@ -193,11 +186,6 @@ class SuspenderObjectData
void releaseStackMemory();
-# if defined(_WIN32)
- void updateTIBStackFields();
- void restoreTIBStackFields();
-# endif
-
# if defined(JS_SIMULATOR_ARM64) || defined(JS_SIMULATOR_ARM) || \
defined(JS_SIMULATOR_RISCV64) || defined(JS_SIMULATOR_LOONG64) || \
defined(JS_SIMULATOR_MIPS64)
@@ -268,7 +256,9 @@ class SuspenderObject : public NativeObject {
setReservedSlot(SuspendingReturnTypeSlot, Int32Value(int32_t(type)));
}
- JS::NativeStackLimit getStackMemoryLimit();
+ JS::NativeStackLimit stackMemoryBase() const;
+ JS::NativeStackLimit stackMemoryLimitForSystem() const;
+ JS::NativeStackLimit stackMemoryLimitForJit() const;
SuspenderState state() { return data()->state(); }
@@ -278,6 +268,10 @@ class SuspenderObject : public NativeObject {
return static_cast<SuspenderObjectData*>(
getReservedSlot(DataSlot).toPrivate());
}
+ inline const SuspenderObjectData* data() const {
+ return static_cast<const SuspenderObjectData*>(
+ getReservedSlot(DataSlot).toPrivate());
+ }
void setMoribund(JSContext* cx);
void setActive(JSContext* cx);