tor-browser

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

JSContext-inl.h (10672B)


      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 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef vm_JSContext_inl_h
      8 #define vm_JSContext_inl_h
      9 
     10 #include "vm/JSContext.h"
     11 
     12 #include <type_traits>
     13 
     14 #include "gc/Marking.h"
     15 #include "gc/Zone.h"
     16 #include "jit/JitFrames.h"
     17 #include "util/DiagnosticAssertions.h"
     18 #include "vm/BigIntType.h"
     19 #include "vm/GlobalObject.h"
     20 #include "vm/Realm.h"
     21 
     22 #include "gc/Allocator-inl.h"
     23 #include "vm/Activation-inl.h"  // js::Activation::hasWasmExitFP
     24 
     25 namespace js {
     26 
     27 class ContextChecks {
     28  JSContext* cx;
     29 
     30  JS::Realm* realm() const { return cx->realm(); }
     31  JS::Compartment* compartment() const { return cx->compartment(); }
     32  JS::Zone* zone() const { return cx->zone(); }
     33 
     34 public:
     35  explicit ContextChecks(JSContext* cx) : cx(cx) {
     36 #ifdef DEBUG
     37    if (realm()) {
     38      GlobalObject* global = realm()->unsafeUnbarrieredMaybeGlobal();
     39      if (global) {
     40        checkObject(global);
     41      }
     42    }
     43 #endif
     44  }
     45 
     46  /*
     47   * Set a breakpoint here (break js::ContextChecks::fail) to debug
     48   * realm/compartment/zone mismatches.
     49   */
     50  static void fail(JS::Realm* r1, JS::Realm* r2, int argIndex) {
     51    MOZ_CRASH_UNSAFE_PRINTF("*** Realm mismatch %p vs. %p at argument %d", r1,
     52                            r2, argIndex);
     53  }
     54  static void fail(JS::Compartment* c1, JS::Compartment* c2, int argIndex) {
     55    MOZ_CRASH_UNSAFE_PRINTF("*** Compartment mismatch %p vs. %p at argument %d",
     56                            c1, c2, argIndex);
     57  }
     58  static void fail(JS::Zone* z1, JS::Zone* z2, int argIndex) {
     59    MOZ_CRASH_UNSAFE_PRINTF("*** Zone mismatch %p vs. %p at argument %d", z1,
     60                            z2, argIndex);
     61  }
     62 
     63  void check(JS::Realm* r, int argIndex) {
     64    if (r && MOZ_UNLIKELY(r != realm())) {
     65      fail(realm(), r, argIndex);
     66    }
     67  }
     68 
     69  void check(JS::Compartment* c, int argIndex) {
     70    if (c && MOZ_UNLIKELY(c != compartment())) {
     71      fail(compartment(), c, argIndex);
     72    }
     73  }
     74 
     75  void check(JS::Zone* z, int argIndex) {
     76    if (zone() && MOZ_UNLIKELY(z != zone())) {
     77      fail(zone(), z, argIndex);
     78    }
     79  }
     80 
     81  void check(JSObject* obj, int argIndex) {
     82    if (obj) {
     83      checkObject(obj);
     84      check(obj->compartment(), argIndex);
     85    }
     86  }
     87 
     88  void checkObject(JSObject* obj) {
     89    JS::AssertObjectIsNotGray(obj);
     90    MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(obj));
     91  }
     92 
     93  template <typename T>
     94  void checkAtom(T* thing, int argIndex) {
     95    static_assert(std::is_same_v<T, JSAtom> || std::is_same_v<T, JS::Symbol>,
     96                  "Should only be called with JSAtom* or JS::Symbol* argument");
     97 
     98    JS::AssertCellIsNotGray(thing);
     99 
    100 #ifdef DEBUG
    101    // Atoms which move across zone boundaries need to be marked in the atom
    102    // marking bitmap for the new zone, see JS_MarkCrossZoneId.
    103    // Note that the atom marking state may not be up-to-date if incremental
    104    // marking is taking place.
    105    gc::GCRuntime* gc = &cx->runtime()->gc;
    106    bool isGCMarking =
    107        gc->state() >= gc::State::Prepare && gc->state() <= gc::State::Sweep;
    108    if (zone() && !isGCMarking) {
    109      gc::CellColor color = gc->atomMarking.getAtomMarkColor(zone(), thing);
    110      if (color != gc::CellColor::Black) {
    111        MOZ_CRASH_UNSAFE_PRINTF(
    112            "*** Atom is marked %s for zone %p at argument %d",
    113            gc::CellColorName(color), zone(), argIndex);
    114      }
    115    }
    116 #endif
    117  }
    118 
    119  void check(JSString* str, int argIndex) {
    120    JS::AssertCellIsNotGray(str);
    121    if (str->isAtom()) {
    122      checkAtom(&str->asAtom(), argIndex);
    123    } else {
    124      check(str->zone(), argIndex);
    125    }
    126  }
    127 
    128  void check(JS::Symbol* symbol, int argIndex) { checkAtom(symbol, argIndex); }
    129 
    130  void check(JS::BigInt* bi, int argIndex) { check(bi->zone(), argIndex); }
    131 
    132  void check(const js::Value& v, int argIndex) {
    133    if (v.isObject()) {
    134      check(&v.toObject(), argIndex);
    135    } else if (v.isString()) {
    136      check(v.toString(), argIndex);
    137    } else if (v.isSymbol()) {
    138      check(v.toSymbol(), argIndex);
    139    } else if (v.isBigInt()) {
    140      check(v.toBigInt(), argIndex);
    141    }
    142  }
    143 
    144  // Check the contents of any container class that supports the C++
    145  // iteration protocol, eg GCVector<jsid>.
    146  template <typename Container>
    147  std::enable_if_t<std::is_same_v<decltype(std::declval<Container>().begin()),
    148                                  decltype(std::declval<Container>().end())>>
    149  check(const Container& container, int argIndex) {
    150    for (auto i : container) {
    151      check(i, argIndex);
    152    }
    153  }
    154 
    155  void check(const JS::HandleValueArray& arr, int argIndex) {
    156    for (size_t i = 0; i < arr.length(); i++) {
    157      check(arr[i], argIndex);
    158    }
    159  }
    160 
    161  void check(const CallArgs& args, int argIndex) {
    162    for (Value* p = args.base(); p != args.end(); ++p) {
    163      check(*p, argIndex);
    164    }
    165  }
    166 
    167  void check(jsid id, int argIndex) {
    168    if (id.isAtom()) {
    169      checkAtom(id.toAtom(), argIndex);
    170    } else if (id.isSymbol()) {
    171      checkAtom(id.toSymbol(), argIndex);
    172    } else {
    173      MOZ_ASSERT(!id.isGCThing());
    174    }
    175  }
    176 
    177  void check(JSScript* script, int argIndex) {
    178    JS::AssertCellIsNotGray(script);
    179    if (script) {
    180      check(script->realm(), argIndex);
    181    }
    182  }
    183 
    184  void check(AbstractFramePtr frame, int argIndex);
    185 
    186  void check(const PropertyDescriptor& desc, int argIndex) {
    187    if (desc.hasGetter()) {
    188      check(desc.getter(), argIndex);
    189    }
    190    if (desc.hasSetter()) {
    191      check(desc.setter(), argIndex);
    192    }
    193    if (desc.hasValue()) {
    194      check(desc.value(), argIndex);
    195    }
    196  }
    197 
    198  void check(Handle<mozilla::Maybe<Value>> maybe, int argIndex) {
    199    if (maybe.get().isSome()) {
    200      check(maybe.get().ref(), argIndex);
    201    }
    202  }
    203 
    204  void check(Handle<mozilla::Maybe<PropertyDescriptor>> maybe, int argIndex) {
    205    if (maybe.get().isSome()) {
    206      check(maybe.get().ref(), argIndex);
    207    }
    208  }
    209 };
    210 
    211 }  // namespace js
    212 
    213 template <class... Args>
    214 inline void JSContext::checkImpl(const Args&... args) {
    215  int argIndex = 0;
    216  (..., js::ContextChecks(this).check(args, argIndex++));
    217 }
    218 
    219 template <class... Args>
    220 inline void JSContext::check(const Args&... args) {
    221 #ifdef JS_CRASH_DIAGNOSTICS
    222  if (contextChecksEnabled()) {
    223    checkImpl(args...);
    224  }
    225 #endif
    226 }
    227 
    228 template <class... Args>
    229 inline void JSContext::releaseCheck(const Args&... args) {
    230  if (contextChecksEnabled()) {
    231    checkImpl(args...);
    232  }
    233 }
    234 
    235 template <class... Args>
    236 MOZ_ALWAYS_INLINE void JSContext::debugOnlyCheck(const Args&... args) {
    237 #if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
    238  if (contextChecksEnabled()) {
    239    checkImpl(args...);
    240  }
    241 #endif
    242 }
    243 
    244 namespace js {
    245 
    246 STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
    247 MOZ_ALWAYS_INLINE bool CallNativeImpl(JSContext* cx, NativeImpl impl,
    248                                      const CallArgs& args) {
    249 #ifdef DEBUG
    250  bool alreadyThrowing = cx->isExceptionPending();
    251 #endif
    252  cx->check(args);
    253  bool ok = impl(cx, args);
    254  if (ok) {
    255    cx->check(args.rval());
    256    MOZ_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
    257  }
    258  return ok;
    259 }
    260 
    261 // OOM interrupts don't call the interrupt callbacks, so we don't need
    262 // to worry about if there is an exception pending. We do want to handle
    263 // the interrupt sooner than for other interrupts so we capture a precise
    264 // stack trace.
    265 MOZ_ALWAYS_INLINE bool CheckForOOMStackTraceInterrupt(JSContext* cx) {
    266  if (MOZ_UNLIKELY(cx->hasPendingInterrupt(InterruptReason::OOMStackTrace))) {
    267    return cx->handleInterruptNoCallbacks();
    268  }
    269  return true;
    270 }
    271 
    272 MOZ_ALWAYS_INLINE bool CheckForInterrupt(JSContext* cx) {
    273  MOZ_ASSERT(!cx->isExceptionPending());
    274  // Add an inline fast-path since we have to check for interrupts in some hot
    275  // C++ loops of library builtins.
    276  if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt())) {
    277    return cx->handleInterrupt();
    278  }
    279 
    280  JS_INTERRUPT_POSSIBLY_FAIL();
    281 
    282  return true;
    283 }
    284 
    285 } /* namespace js */
    286 
    287 inline void JSContext::minorGC(JS::GCReason reason) {
    288  runtime()->gc.minorGC(reason);
    289 }
    290 
    291 inline bool JSContext::runningWithTrustedPrincipals() {
    292  if (!realm()) {
    293    return true;
    294  }
    295  if (!runtime()->trustedPrincipals()) {
    296    return false;
    297  }
    298  return realm()->principals() == runtime()->trustedPrincipals();
    299 }
    300 
    301 inline void JSContext::enterRealm(JS::Realm* realm) {
    302  // We should never enter a realm while in the atoms zone.
    303  MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
    304 
    305  realm->enter();
    306  setRealm(realm);
    307 }
    308 
    309 inline void JSContext::enterAtomsZone() {
    310  realm_ = nullptr;
    311  setZone(runtime_->unsafeAtomsZone());
    312 }
    313 
    314 inline void JSContext::setZone(js::Zone* zone) { zone_ = zone; }
    315 
    316 inline void JSContext::enterRealmOf(JSObject* target) {
    317  JS::AssertCellIsNotGray(target);
    318  enterRealm(target->nonCCWRealm());
    319 }
    320 
    321 inline void JSContext::enterRealmOf(JSScript* target) {
    322  JS::AssertCellIsNotGray(target);
    323  enterRealm(target->realm());
    324 }
    325 
    326 inline void JSContext::enterRealmOf(js::Shape* target) {
    327  JS::AssertCellIsNotGray(target);
    328  enterRealm(target->realm());
    329 }
    330 
    331 inline void JSContext::enterNullRealm() {
    332  // We should never enter a realm while in the atoms zone.
    333  MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
    334 
    335  setRealm(nullptr);
    336 }
    337 
    338 inline void JSContext::leaveRealm(JS::Realm* oldRealm) {
    339  // Only call leave() after we've setRealm()-ed away from the current realm.
    340  JS::Realm* startingRealm = realm_;
    341 
    342  // The current realm should be marked as entered-from-C++ at this point.
    343  MOZ_ASSERT_IF(startingRealm, startingRealm->hasBeenEnteredIgnoringJit());
    344 
    345  setRealm(oldRealm);
    346 
    347  if (startingRealm) {
    348    startingRealm->leave();
    349  }
    350 }
    351 
    352 inline void JSContext::leaveAtomsZone(JS::Realm* oldRealm) {
    353  setRealm(oldRealm);
    354 }
    355 
    356 inline void JSContext::setRealm(JS::Realm* realm) {
    357  realm_ = realm;
    358  if (realm) {
    359    // This thread must have exclusive access to the zone.
    360    MOZ_ASSERT(CurrentThreadCanAccessZone(realm->zone()));
    361    MOZ_ASSERT(!realm->zone()->isAtomsZone());
    362    setZone(realm->zone());
    363  } else {
    364    setZone(nullptr);
    365  }
    366 }
    367 
    368 inline void JSContext::setRealmForJitExceptionHandler(JS::Realm* realm) {
    369  // JIT code enters (same-compartment) realms without calling realm->enter()
    370  // so we don't call realm->leave() here.
    371  MOZ_ASSERT(realm->compartment() == compartment());
    372  realm_ = realm;
    373 }
    374 
    375 inline js::RuntimeCaches& JSContext::caches() { return runtime()->caches(); }
    376 
    377 template <typename T, js::AllowGC allowGC, typename... Args>
    378 T* JSContext::newCell(Args&&... args) {
    379  return js::gc::CellAllocator::template NewCell<T, allowGC>(
    380      this, std::forward<Args>(args)...);
    381 }
    382 
    383 #endif /* vm_JSContext_inl_h */