tor-browser

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

commit 9b710255f243aeb399642d6af4c713f8f1b61339
parent 36aa80e5a65150d11dfb757f8597e192c1d5e096
Author: Ryan Hunt <rhunt@eqrion.net>
Date:   Tue, 18 Nov 2025 16:57:29 +0000

Bug 1990931 - wasm: Move stack overflow check trap OOL. r=yury

Differential Revision: https://phabricator.services.mozilla.com/D271457

Diffstat:
Mjs/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js | 2+-
Mjs/src/jit/CodeGenerator.cpp | 8+++++++-
Mjs/src/jit/MacroAssembler.cpp | 32++++++++++----------------------
Mjs/src/jit/MacroAssembler.h | 9++++-----
Mjs/src/wasm/WasmBCFrame.h | 9+++------
Mjs/src/wasm/WasmBaselineCompile.cpp | 24+++++++++++++++---------
Mjs/src/wasm/WasmStubs.cpp | 8+++++++-
7 files changed, 47 insertions(+), 45 deletions(-)

diff --git a/js/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js b/js/src/jit-test/tests/wasm/simd/const-arm64-vixl-codegen.js @@ -9,7 +9,7 @@ var prefix = `${lead}sub sp, sp, #0x.. \\(..\\) ${lead}str x23, \\[sp, #..\\]`; var suffix = -`${lead}b #\\+0x14 \\(addr 0x.*\\) +`${lead}b #\\+0x18 \\(addr 0x.*\\) ${lead}brk #0xf000`; for ( let [bits, expected, values] of [ diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp @@ -17146,7 +17146,13 @@ bool CodeGenerator::generateWasm(wasm::CallIndirectId callIndirectId, // need to check for interrupts. MOZ_ASSERT(omitOverRecursedInterruptCheck()); } else { - masm.wasmReserveStackChecked(frameSize(), entryTrapSiteDesc); + auto* ool = new (alloc()) + LambdaOutOfLineCode([this, entryTrapSiteDesc](OutOfLineCode& ool) { + masm.wasmTrap(wasm::Trap::StackOverflow, entryTrapSiteDesc); + return true; + }); + addOutOfLineCode(ool, (const BytecodeSite*)nullptr); + masm.wasmReserveStackChecked(frameSize(), ool->entry()); if (!omitOverRecursedInterruptCheck()) { wasm::StackMap* functionEntryStackMap = nullptr; diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp @@ -5835,40 +5835,28 @@ void MacroAssembler::wasmTrap(wasm::Trap trap, append(trap, wasm::TrapMachineInsn::OfficialUD, fco.get(), trapSiteDesc); } -std::pair<CodeOffset, uint32_t> MacroAssembler::wasmReserveStackChecked( - uint32_t amount, const wasm::TrapSiteDesc& trapSiteDesc) { +uint32_t MacroAssembler::wasmReserveStackChecked(uint32_t amount, Label* fail) { if (amount > MAX_UNCHECKED_LEAF_FRAME_SIZE) { // The frame is large. Don't bump sp until after the stack limit check so // that the trap handler isn't called with a wild sp. - Label ok; Register scratch = ABINonArgReg0; moveStackPtrTo(scratch); - - Label trap; - branchPtr(Assembler::Below, scratch, Imm32(amount), &trap); + branchPtr(Assembler::Below, scratch, Imm32(amount), fail); subPtr(Imm32(amount), scratch); - branchPtr(Assembler::Below, + branchPtr(Assembler::AboveOrEqual, Address(InstanceReg, wasm::Instance::offsetOfStackLimit()), - scratch, &ok); - - bind(&trap); - wasmTrap(wasm::Trap::StackOverflow, trapSiteDesc); - CodeOffset trapInsnOffset = CodeOffset(currentOffset()); - - bind(&ok); + scratch, fail); reserveStack(amount); - return std::pair<CodeOffset, uint32_t>(trapInsnOffset, 0); + // The stack amount was reserved after branching to the fail label. + return 0; } reserveStack(amount); - Label ok; - branchStackPtrRhs(Assembler::Below, + branchStackPtrRhs(Assembler::AboveOrEqual, Address(InstanceReg, wasm::Instance::offsetOfStackLimit()), - &ok); - wasmTrap(wasm::Trap::StackOverflow, trapSiteDesc); - CodeOffset trapInsnOffset = CodeOffset(currentOffset()); - bind(&ok); - return std::pair<CodeOffset, uint32_t>(trapInsnOffset, amount); + fail); + // The stack amount was reserved before branching to the fail label. + return amount; } static void MoveDataBlock(MacroAssembler& masm, Register base, int32_t from, diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h @@ -3707,11 +3707,10 @@ class MacroAssembler : public MacroAssemblerSpecific { void loadWasmPinnedRegsFromInstance( const wasm::MaybeTrapSiteDesc& trapSiteDesc); - // Returns a pair: the offset of the undefined (trapping) instruction, and - // the number of extra bytes of stack allocated prior to the trap - // instruction proper. - std::pair<CodeOffset, uint32_t> wasmReserveStackChecked( - uint32_t amount, const wasm::TrapSiteDesc& trapSiteDesc); + // Branches to the fail label if the stack would overflow the current stack + // limit. Returns the number of extra bytes of stack allocated prior to + // branching to the fail label. + uint32_t wasmReserveStackChecked(uint32_t amount, Label* fail); // Emit a bounds check against the wasm heap limit, jumping to 'ok' if 'cond' // holds; this can be the label either of the access or of the trap. The diff --git a/js/src/wasm/WasmBCFrame.h b/js/src/wasm/WasmBCFrame.h @@ -539,14 +539,11 @@ class BaseStackFrame final : public BaseStackFrameAllocator { // Note the platform scratch register may be used by branchPtr(), so // generally tmp must be something else. - void checkStack(Register tmp, TrapSiteDesc trapSiteDesc) { + void checkStack(Register tmp, Label* stackOverflowTrap) { stackAddOffset_ = masm.sub32FromStackPtrWithPatch(tmp); - Label ok; - masm.branchPtr(Assembler::Below, + masm.branchPtr(Assembler::AboveOrEqual, Address(InstanceReg, wasm::Instance::offsetOfStackLimit()), - tmp, &ok); - masm.wasmTrap(Trap::StackOverflow, trapSiteDesc); - masm.bind(&ok); + tmp, stackOverflowTrap); } void patchCheckStack() { diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp @@ -245,7 +245,6 @@ class OutOfLineResumableTrap : public OutOfLineCode { masm->wasmTrap(trap_, desc_); if (stackMap_ && !stackMaps_->add(masm->currentOffset(), stackMap_)) { masm->setOOM(); - return; } masm->jump(rejoin()); } @@ -574,9 +573,6 @@ bool BaseCompiler::beginFunction() { // Generate a stack-overflow check and its associated stackmap. - fr.checkStack(ABINonArgReg0, - TrapSiteDesc(BytecodeOffset(func_.lineOrBytecode))); - ExitStubMapVector extras; StackMap* functionEntryStackMap; if (!stackMapGenerator_.generateStackmapEntriesForTrapExit(args, &extras) || @@ -586,15 +582,25 @@ bool BaseCompiler::beginFunction() { return false; } - OutOfLineCode* ool = addOutOfLineCode(new (alloc_) OutOfLineResumableTrap( - Trap::CheckInterrupt, trapSiteDesc(), functionEntryStackMap, stackMaps_)); - if (!ool) { + OutOfLineCode* oolStackOverflowTrap = + addOutOfLineCode(new (alloc_) OutOfLineAbortingTrap( + Trap::StackOverflow, + TrapSiteDesc(BytecodeOffset(func_.lineOrBytecode)))); + if (!oolStackOverflowTrap) { + return false; + } + fr.checkStack(ABINonArgReg0, oolStackOverflowTrap->entry()); + + OutOfLineCode* oolInterruptTrap = addOutOfLineCode( + new (alloc_) OutOfLineResumableTrap(Trap::CheckInterrupt, trapSiteDesc(), + functionEntryStackMap, stackMaps_)); + if (!oolInterruptTrap) { return false; } masm.branch32(Assembler::NotEqual, Address(InstanceReg, wasm::Instance::offsetOfInterrupt()), - Imm32(0), ool->entry()); - masm.bind(ool->rejoin()); + Imm32(0), oolInterruptTrap->entry()); + masm.bind(oolInterruptTrap->rejoin()); size_t reservedBytes = fr.fixedAllocSize() - masm.framePushed(); MOZ_ASSERT(0 == (reservedBytes % sizeof(void*))); diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp @@ -1894,7 +1894,8 @@ static bool GenerateImportFunction(jit::MacroAssembler& masm, sizeof(Frame), // pushed by prologue StackArgBytesForWasmABI(funcType) + sizeOfInstanceSlot); - masm.wasmReserveStackChecked(framePushed, TrapSiteDesc()); + Label stackOverflowTrap; + masm.wasmReserveStackChecked(framePushed, &stackOverflowTrap); MOZ_ASSERT(masm.framePushed() == framePushed); @@ -1941,6 +1942,11 @@ static bool GenerateImportFunction(jit::MacroAssembler& masm, masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1); GenerateFunctionEpilogue(masm, framePushed, offsets); + + // Emit the stack overflow trap as OOL code. + masm.bind(&stackOverflowTrap); + masm.wasmTrap(wasm::Trap::StackOverflow, wasm::TrapSiteDesc()); + return FinishOffsets(masm, offsets); }