tor-browser

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

ExecutableAllocator.h (6348B)


      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 (C) 2008 Apple Inc. All rights reserved.
      5 *
      6 * Redistribution and use in source and binary forms, with or without
      7 * modification, are permitted provided that the following conditions
      8 * are met:
      9 * 1. Redistributions of source code must retain the above copyright
     10 *    notice, this list of conditions and the following disclaimer.
     11 * 2. Redistributions in binary form must reproduce the above copyright
     12 *    notice, this list of conditions and the following disclaimer in the
     13 *    documentation and/or other materials provided with the distribution.
     14 *
     15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26 */
     27 
     28 #ifndef jit_ExecutableAllocator_h
     29 #define jit_ExecutableAllocator_h
     30 
     31 #include "mozilla/EnumeratedArray.h"
     32 
     33 #include <stddef.h>  // for ptrdiff_t
     34 
     35 #include "jit/ProcessExecutableMemory.h"
     36 #include "js/AllocPolicy.h"
     37 #include "js/HashTable.h"
     38 #include "js/TypeDecls.h"
     39 #include "js/Vector.h"
     40 
     41 namespace JS {
     42 struct CodeSizes;
     43 }  // namespace JS
     44 
     45 namespace js {
     46 namespace jit {
     47 
     48 enum class CodeKind : uint8_t { Ion, Baseline, RegExp, Other, Count };
     49 
     50 class ExecutableAllocator;
     51 
     52 // These are reference-counted. A new one starts with a count of 1.
     53 class ExecutablePool {
     54  friend class ExecutableAllocator;
     55 
     56 private:
     57  struct Allocation {
     58    char* pages;
     59    size_t size;
     60  };
     61 
     62  ExecutableAllocator* m_allocator;
     63  char* m_freePtr;
     64  char* m_end;
     65  Allocation m_allocation;
     66 
     67  // Reference count for automatic reclamation.
     68  unsigned m_refCount : 31;
     69 
     70  // Flag that can be used by algorithms operating on pools.
     71  bool m_mark : 1;
     72 
     73  // Number of bytes currently allocated for each CodeKind.
     74  mozilla::EnumeratedArray<CodeKind, size_t, size_t(CodeKind::Count)>
     75      m_codeBytes;
     76 
     77 public:
     78  void release(bool willDestroy = false);
     79  void release(size_t n, CodeKind kind);
     80 
     81  void addRef();
     82 
     83  ExecutablePool(ExecutableAllocator* allocator, Allocation a)
     84      : m_allocator(allocator),
     85        m_freePtr(a.pages),
     86        m_end(m_freePtr + a.size),
     87        m_allocation(a),
     88        m_refCount(1),
     89        m_mark(false) {
     90    for (size_t& count : m_codeBytes) {
     91      count = 0;
     92    }
     93  }
     94 
     95  ~ExecutablePool();
     96 
     97  void mark() {
     98    MOZ_ASSERT(!m_mark);
     99    m_mark = true;
    100  }
    101  void unmark() {
    102    MOZ_ASSERT(m_mark);
    103    m_mark = false;
    104  }
    105  bool isMarked() const { return m_mark; }
    106 
    107 private:
    108  ExecutablePool(const ExecutablePool&) = delete;
    109  void operator=(const ExecutablePool&) = delete;
    110 
    111  void* alloc(size_t n, CodeKind kind);
    112 
    113  size_t available() const;
    114 
    115  // Returns the number of bytes that are currently in use (referenced by
    116  // live JitCode objects).
    117  size_t usedCodeBytes() const {
    118    size_t res = 0;
    119    for (size_t count : m_codeBytes) {
    120      res += count;
    121    }
    122    return res;
    123  }
    124 };
    125 
    126 struct JitPoisonRange {
    127  jit::ExecutablePool* pool;
    128  void* start;
    129  size_t size;
    130 
    131  JitPoisonRange(jit::ExecutablePool* pool, void* start, size_t size)
    132      : pool(pool), start(start), size(size) {}
    133 };
    134 
    135 using JitPoisonRangeVector = Vector<JitPoisonRange, 0, SystemAllocPolicy>;
    136 
    137 class ExecutableAllocator {
    138 public:
    139  ExecutableAllocator() = default;
    140  ~ExecutableAllocator();
    141 
    142  void purge();
    143 
    144  // alloc() returns a pointer to some memory, and also (by reference) a
    145  // pointer to reference-counted pool. The caller owns a reference to the
    146  // pool; i.e. alloc() increments the count before returning the object.
    147  void* alloc(JSContext* cx, size_t n, ExecutablePool** poolp, CodeKind type);
    148 
    149  void releasePoolPages(ExecutablePool* pool);
    150 
    151  void addSizeOfCode(JS::CodeSizes* sizes) const;
    152 
    153 private:
    154  static const size_t OVERSIZE_ALLOCATION = size_t(-1);
    155 
    156  static size_t roundUpAllocationSize(size_t request, size_t granularity);
    157 
    158  // On OOM, this will return an Allocation where pages is nullptr.
    159  ExecutablePool::Allocation systemAlloc(size_t n);
    160  static void systemRelease(const ExecutablePool::Allocation& alloc);
    161 
    162  ExecutablePool* createPool(size_t n);
    163  ExecutablePool* poolForSize(size_t n);
    164 
    165  static void reprotectPool(JSRuntime* rt, ExecutablePool* pool,
    166                            ProtectionSetting protection,
    167                            MustFlushICache flushICache);
    168 
    169 public:
    170  [[nodiscard]] static bool makeWritable(void* start, size_t size) {
    171    return ReprotectRegion(start, size, ProtectionSetting::Writable,
    172                           MustFlushICache::No);
    173  }
    174 
    175  [[nodiscard]] static bool makeExecutableAndFlushICache(void* start,
    176                                                         size_t size) {
    177    return ReprotectRegion(start, size, ProtectionSetting::Executable,
    178                           MustFlushICache::Yes);
    179  }
    180 
    181  static void poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges);
    182 
    183 private:
    184  ExecutableAllocator(const ExecutableAllocator&) = delete;
    185  void operator=(const ExecutableAllocator&) = delete;
    186 
    187  // These are strong references;  they keep pools alive.
    188  static const size_t maxSmallPools = 4;
    189  using SmallExecPoolVector =
    190      js::Vector<ExecutablePool*, maxSmallPools, js::SystemAllocPolicy>;
    191  SmallExecPoolVector m_smallPools;
    192 
    193  // All live pools are recorded here, just for stats purposes.  These are
    194  // weak references;  they don't keep pools alive.  When a pool is destroyed
    195  // its reference is removed from m_pools.
    196  using ExecPoolHashSet =
    197      js::HashSet<ExecutablePool*, js::DefaultHasher<ExecutablePool*>,
    198                  js::SystemAllocPolicy>;
    199  ExecPoolHashSet m_pools;  // All pools, just for stats purposes.
    200 };
    201 
    202 }  // namespace jit
    203 }  // namespace js
    204 
    205 #endif /* jit_ExecutableAllocator_h */