tor-browser

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

WasmContext.cpp (4435B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 *
      4 * Copyright 2025 Mozilla Foundation
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 #include "wasm/WasmContext.h"
     20 
     21 #include "jit/JitRuntime.h"
     22 #include "js/friend/StackLimits.h"
     23 #include "js/TracingAPI.h"
     24 #include "vm/JSContext.h"
     25 #include "wasm/WasmPI.h"
     26 
     27 #ifdef XP_WIN
     28 // We only need the `windows.h` header, but this file can get unified built
     29 // with WasmSignalHandlers.cpp, which requires `winternal.h` to be included
     30 // before the `windows.h` header, and so we must include it here for that case.
     31 #  include <winternl.h>  // must include before util/WindowsWrapper.h's `#undef`s
     32 
     33 #  include "util/WindowsWrapper.h"
     34 #endif
     35 
     36 using namespace js::wasm;
     37 
     38 Context::Context()
     39    : triedToInstallSignalHandlers(false),
     40      haveSignalHandlers(false),
     41      stackLimit(JS::NativeStackLimitMin),
     42      mainStackLimit(JS::NativeStackLimitMin)
     43 #ifdef ENABLE_WASM_JSPI
     44      ,
     45      activeSuspender_(nullptr)
     46 #endif
     47 {
     48 }
     49 
     50 Context::~Context() {
     51 #ifdef ENABLE_WASM_JSPI
     52  MOZ_ASSERT(activeSuspender_ == nullptr);
     53  MOZ_ASSERT(suspenders_.empty());
     54 #endif
     55 }
     56 
     57 void Context::initStackLimit(JSContext* cx) {
     58  // The wasm stack limit is the same as the jit stack limit. We also don't
     59  // use the stack limit for triggering interrupts.
     60  stackLimit = cx->jitStackLimitNoInterrupt;
     61  mainStackLimit = stackLimit;
     62 
     63  // See the comment on wasm::Context for why we do this.
     64 #ifdef ENABLE_WASM_JSPI
     65 #  if defined(_WIN32)
     66  tib_ = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb());
     67  tibStackBase_ = tib_->StackBase;
     68  tibStackLimit_ = tib_->StackLimit;
     69 #  endif
     70 #endif
     71 }
     72 
     73 #ifdef ENABLE_WASM_JSPI
     74 SuspenderObject* Context::findSuspenderForStackAddress(
     75    const void* stackAddress) {
     76  // TODO: add a fast path for the main stack that avoids linear search. We
     77  // need an accurate main stack base/limit for that.
     78  for (auto iter = suspenders_.iter(); !iter.done(); iter.next()) {
     79    SuspenderObject* object = iter.get();
     80    if (object->isActive() && object->hasStackAddress(stackAddress)) {
     81      return object;
     82    }
     83  }
     84  return nullptr;
     85 }
     86 
     87 void Context::trace(JSTracer* trc) {
     88  if (activeSuspender_) {
     89    TraceEdge(trc, &activeSuspender_, "suspender");
     90  }
     91 }
     92 
     93 void Context::traceRoots(JSTracer* trc) {
     94  // The suspendedStacks_ contains suspended stacks frames that need to be
     95  // traced only during minor GC. The major GC tracing is happening via
     96  // SuspenderObject::trace.
     97  // Non-suspended stack frames are traced as part of TraceJitActivations.
     98  if (!trc->isTenuringTracer()) {
     99    return;
    100  }
    101  gc::AssertRootMarkingPhase(trc);
    102  for (auto iter = suspenders_.iter(); !iter.done(); iter.next()) {
    103    SuspenderObject* object = iter.get();
    104    if (object->state() == SuspenderState::Suspended) {
    105      TraceSuspendableStack(trc, object);
    106    }
    107  }
    108 }
    109 
    110 void Context::enterSuspendableStack(JSContext* cx, SuspenderObject* suspender) {
    111  MOZ_ASSERT(!activeSuspender_);
    112  activeSuspender_ = suspender;
    113  stackLimit = suspender->stackMemoryLimitForJit();
    114 
    115  // See the comment on wasm::Context for why we do this.
    116 #  if defined(_WIN32)
    117  tibStackBase_ = tib_->StackBase;
    118  tibStackLimit_ = tib_->StackLimit;
    119  tib_->StackBase = reinterpret_cast<void*>(suspender->stackMemoryBase());
    120  tib_->StackLimit =
    121      reinterpret_cast<void*>(suspender->stackMemoryLimitForSystem());
    122 #  endif
    123 
    124 #  ifdef DEBUG
    125  cx->runtime()->jitRuntime()->disallowArbitraryCode();
    126 #  endif
    127 }
    128 
    129 void Context::leaveSuspendableStack(JSContext* cx) {
    130  MOZ_ASSERT(activeSuspender_);
    131  activeSuspender_ = nullptr;
    132  stackLimit = mainStackLimit;
    133 
    134  // See the comment on wasm::Context for why we do this.
    135 #  if defined(_WIN32)
    136  tib_->StackBase = static_cast<void*>(tibStackBase_);
    137  tib_->StackLimit = static_cast<void*>(tibStackLimit_);
    138 #  endif
    139 
    140 #  ifdef DEBUG
    141  cx->runtime()->jitRuntime()->clearDisallowArbitraryCode();
    142 #  endif
    143 }
    144 #endif