tor-browser

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

GuardFuse.h (3931B)


      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_GuardFuse_h
      8 #define vm_GuardFuse_h
      9 
     10 #include "mozilla/Assertions.h"
     11 
     12 #include <stddef.h>
     13 
     14 #include "jstypes.h"
     15 
     16 struct JS_PUBLIC_API JSContext;
     17 
     18 namespace js {
     19 
     20 // [SMDOC] Fuses
     21 // A Fuse is a data structure in memory that asserts some property of the
     22 // system.
     23 //
     24 // A fuse may be popped; this indicates that the property the fuse was asserting
     25 // no longer holds.
     26 //
     27 // There are two models of Fuse:
     28 // 1. Guard fuses are used as guards at various points through the engine to
     29 //    allow dynamic choice of a fallback path vs. fast path. A guard is checked
     30 //    explicitly before each action.
     31 // 2. An Invalidating Fuse instead performs some cleanup actions invalidating
     32 //    Ion compiled under the assumption the fuse is intact.
     33 //
     34 // Fuses are designed to be checked easily for validity -- a single load and
     35 // compare should suffice.
     36 //
     37 //
     38 // In order to try make bugs in Fuses fuzzable, each fuse has a
     39 // `checkInvariants` callback. This callback should return `true` iff the
     40 // invariant the fuse asserts still holds. We can then assert that if a fuse is
     41 // intact, then its invariant should hold. This can be triggered by a shell
     42 // testing function, or (not implemented) triggered on a regular basis ala
     43 // JS_GC_ZEAL.
     44 //
     45 // Fuses are agnostic about how they are popped. To support watching for a few
     46 // operations we expect to be important to fuses, see:
     47 //
     48 //   - Watchtower::watchPropertyModification
     49 //   - Watchtower::watchProtoChange
     50 //   - Watchtower::watchPropertyModification.
     51 //
     52 // and ObjectFlags::HasFuseProperty. As well, see MGuardFuse::aliasSet, which
     53 // should be updated if there is any modification to the possible set of places
     54 // fuses could be popped.
     55 //
     56 // In order to support relationships, the popFuse method is virtual and can be
     57 // overridden by subclasses. See RealmFuses.h for examples of how RealmScoped
     58 // fuses are implemented there through overrides of popFuse.
     59 class GuardFuse {
     60 public:
     61  GuardFuse() = default;
     62  GuardFuse(GuardFuse&&) = delete;
     63 
     64  virtual const char* name() = 0;
     65 
     66  // Basic fuse interface: Takes a JSContext argument for subclasses.
     67  virtual void popFuse(JSContext* cx) {
     68    MOZ_ASSERT_IF(fuse_, fuse_ == PoppedFuseValue);
     69    fuse_ = PoppedFuseValue;
     70  }
     71 
     72  bool intact() {
     73    MOZ_ASSERT_IF(fuse_, fuse_ == PoppedFuseValue);
     74    return fuse_ == 0;
     75  }
     76 
     77  GuardFuse* self() { return this; }
     78 
     79  // Code-Generation Fuse interface
     80  size_t* fuseRef() { return &fuse_; }
     81  static int32_t fuseOffset() { return offsetof(GuardFuse, fuse_); }
     82 
     83  // Invariant Maintenance Interface: If an invariant doesn't hold, we should
     84  // crash the process.
     85  //
     86  // Since a fuse is a statement about an invariant in the system, if the fuse
     87  // is intact, the invariant must hold. However, the converse is not true:
     88  // a fuse may be popped while its invariant still holds (and for testing
     89  // purposes we explicitly require this; see popAllFusesInRealm).
     90  virtual void assertInvariant(JSContext* cx) {
     91    if (intact()) {
     92      if (!checkInvariant(cx)) {
     93        fprintf(stderr, "Fuse %s failed invariant check\n", name());
     94        MOZ_CRASH("Failed invariant check");
     95      }
     96    }
     97  }
     98 
     99  // Return true iff the invariant asserted by a particular fuse holds; this can
    100  // safely return false if the fuse is popped.
    101  virtual bool checkInvariant(JSContext* cx) = 0;
    102 
    103 private:
    104  // Use a bit pattern to mark a popped fuse -- May be useful in the future for
    105  // disambiguating between a real popped fuse and a bit-flip.
    106  static constexpr size_t PoppedFuseValue = 0x808;
    107 
    108  size_t fuse_ = 0;
    109 };
    110 
    111 }  // namespace js
    112 #endif  // vm_GuardFuse_h