tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mjs/src/wasm/WasmContext.cpp | 48++++++++++++++++++++++++++++++++++++++++++------
Mjs/src/wasm/WasmContext.h | 16+++++++++++++---
Mjs/src/wasm/WasmPI.cpp | 56+++++++++++++-------------------------------------------
Mjs/src/wasm/WasmPI.h | 20+++++++-------------
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);