tor-browser

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

Poison.h (6982B)


      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 /*
      8 * Memory poisoning.
      9 */
     10 
     11 #ifndef util_Poison_h
     12 #define util_Poison_h
     13 
     14 #include "mozilla/Assertions.h"
     15 #include "mozilla/Attributes.h"
     16 #include "mozilla/MemoryChecking.h"
     17 
     18 #include <algorithm>  // std::min
     19 #include <stddef.h>
     20 #include <stdint.h>
     21 #include <string.h>
     22 
     23 #include "jstypes.h"
     24 
     25 #include "js/Prefs.h"
     26 #include "js/Value.h"
     27 #include "util/DiagnosticAssertions.h"
     28 
     29 /*
     30 * Allow extra GC poisoning to be enabled in crash-diagnostics and zeal
     31 * builds. Except in debug builds, this must be enabled by setting the
     32 * javascript.options.extra_gc_poisoning pref.
     33 */
     34 #if defined(JS_CRASH_DIAGNOSTICS) || defined(JS_GC_ZEAL)
     35 #  define JS_GC_ALLOW_EXTRA_POISONING 1
     36 #endif
     37 
     38 namespace mozilla {
     39 
     40 /**
     41 * Set the first |aNElem| T elements in |aDst| to |aSrc|.
     42 */
     43 template <typename T>
     44 static MOZ_ALWAYS_INLINE void PodSet(T* aDst, const T& aSrc, size_t aNElem) {
     45  for (const T* dstend = aDst + aNElem; aDst < dstend; ++aDst) {
     46    *aDst = aSrc;
     47  }
     48 }
     49 
     50 } /* namespace mozilla */
     51 
     52 /*
     53 * Patterns used by SpiderMonkey to overwrite unused memory. If you are
     54 * accessing an object with one of these patterns, you probably have a dangling
     55 * pointer. These values should be odd.
     56 */
     57 const uint8_t JS_FRESH_NURSERY_PATTERN = 0x2F;
     58 const uint8_t JS_SWEPT_NURSERY_PATTERN = 0x2B;
     59 const uint8_t JS_ALLOCATED_NURSERY_PATTERN = 0x2D;
     60 const uint8_t JS_FRESH_TENURED_PATTERN = 0x4F;
     61 const uint8_t JS_MOVED_TENURED_PATTERN = 0x49;
     62 const uint8_t JS_SWEPT_TENURED_PATTERN = 0x4B;
     63 const uint8_t JS_ALLOCATED_TENURED_PATTERN = 0x4D;
     64 const uint8_t JS_FREED_HEAP_PTR_PATTERN = 0x6B;
     65 const uint8_t JS_FREED_CHUNK_PATTERN = 0x8B;
     66 const uint8_t JS_FREED_ARENA_PATTERN = 0x9B;
     67 const uint8_t JS_FRESH_MARK_STACK_PATTERN = 0x9F;
     68 const uint8_t JS_FREED_BUFFER_PATTERN = 0xAB;
     69 const uint8_t JS_ALLOCATED_BUFFER_PATTERN = 0xAD;
     70 const uint8_t JS_RESET_VALUE_PATTERN = 0xBB;
     71 const uint8_t JS_POISONED_JSSCRIPT_DATA_PATTERN = 0xDB;
     72 const uint8_t JS_OOB_PARSE_NODE_PATTERN = 0xFF;
     73 const uint8_t JS_LIFO_UNDEFINED_PATTERN = 0xcd;
     74 const uint8_t JS_LIFO_UNINITIALIZED_PATTERN = 0xce;
     75 
     76 // Even ones
     77 const uint8_t JS_SCOPE_DATA_TRAILING_NAMES_PATTERN = 0xCC;
     78 
     79 /*
     80 * Ensure JS_SWEPT_CODE_PATTERN is a byte pattern that will crash immediately
     81 * when executed, so either an undefined instruction or an instruction that's
     82 * illegal in user mode.
     83 */
     84 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || \
     85    defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32)
     86 #  define JS_SWEPT_CODE_PATTERN 0xED  // IN instruction, crashes in user mode.
     87 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
     88 #  define JS_SWEPT_CODE_PATTERN 0xA3  // undefined instruction
     89 #elif defined(JS_CODEGEN_MIPS64)
     90 #  define JS_SWEPT_CODE_PATTERN 0x01  // undefined instruction
     91 #elif defined(JS_CODEGEN_LOONG64)
     92 #  define JS_SWEPT_CODE_PATTERN 0x01  // undefined instruction
     93 #elif defined(JS_CODEGEN_RISCV64)
     94 #  define JS_SWEPT_CODE_PATTERN \
     95    0x29  // illegal sb instruction, crashes in user mode.
     96 #else
     97 #  error "JS_SWEPT_CODE_PATTERN not defined for this platform"
     98 #endif
     99 
    100 enum class MemCheckKind : uint8_t {
    101  // Marks a region as poisoned. Memory sanitizers like ASan will crash when
    102  // accessing it (both reads and writes).
    103  MakeNoAccess,
    104 
    105  // Marks a region as having undefined contents. In ASan builds this just
    106  // unpoisons the memory. MSan and Valgrind can also use this to find
    107  // reads of uninitialized memory.
    108  MakeUndefined,
    109 };
    110 
    111 static MOZ_ALWAYS_INLINE void SetMemCheckKind(void* ptr, size_t bytes,
    112                                              MemCheckKind kind) {
    113  switch (kind) {
    114    case MemCheckKind::MakeUndefined:
    115      MOZ_MAKE_MEM_UNDEFINED(ptr, bytes);
    116      return;
    117    case MemCheckKind::MakeNoAccess:
    118      MOZ_MAKE_MEM_NOACCESS(ptr, bytes);
    119      return;
    120  }
    121  MOZ_CRASH("Invalid kind");
    122 }
    123 
    124 namespace js {
    125 
    126 static inline void PoisonImpl(void* ptr, uint8_t value, size_t num) {
    127  // Without a valid Value tag, a poisoned Value may look like a valid
    128  // floating point number. To ensure that we crash more readily when
    129  // observing a poisoned Value, we make the poison an invalid ObjectValue.
    130  // Unfortunately, this adds about 2% more overhead, so we can only enable
    131  // it in debug.
    132 #if defined(DEBUG)
    133  if (!num) {
    134    return;
    135  }
    136 
    137  uintptr_t poison;
    138  memset(&poison, value, sizeof(poison));
    139 #  if defined(JS_PUNBOX64)
    140  poison = poison & ((uintptr_t(1) << JSVAL_TAG_SHIFT) - 1);
    141 #  endif
    142  JS::Value v = js::PoisonedObjectValue(poison);
    143 
    144 #  if defined(JS_NUNBOX32)
    145  // On 32-bit arch, `ptr` is 4 bytes aligned, and it's less than
    146  // `sizeof(JS::Value)` == 8 bytes.
    147  //
    148  // `mozilla::PodSet` with `v` requires the pointer to be 8 bytes aligned if
    149  // `value_count > 0`.
    150  //
    151  // If the pointer isn't 8 bytes aligned, fill the leading 1-4 bytes
    152  // separately here, so that either the pointer is 8 bytes aligned, or
    153  // we have no more bytes to fill.
    154  uintptr_t begin_count = std::min(num, uintptr_t(ptr) % sizeof(JS::Value));
    155  if (begin_count) {
    156    uint8_t* begin = static_cast<uint8_t*>(ptr);
    157    mozilla::PodSet(begin, value, begin_count);
    158    ptr = begin + begin_count;
    159    num -= begin_count;
    160 
    161    if (!num) {
    162      return;
    163    }
    164  }
    165 #  endif
    166 
    167  MOZ_ASSERT(uintptr_t(ptr) % sizeof(JS::Value) == 0);
    168 
    169  size_t value_count = num / sizeof(v);
    170  size_t byte_count = num % sizeof(v);
    171  mozilla::PodSet(reinterpret_cast<JS::Value*>(ptr), v, value_count);
    172  if (byte_count) {
    173    uint8_t* bytes = static_cast<uint8_t*>(ptr);
    174    uint8_t* end = bytes + num;
    175    mozilla::PodSet(end - byte_count, value, byte_count);
    176  }
    177 #else   // !DEBUG
    178  memset(ptr, value, num);
    179 #endif  // !DEBUG
    180 }
    181 
    182 // Unconditionally poison a region on memory.
    183 static inline void AlwaysPoison(void* ptr, uint8_t value, size_t num,
    184                                MemCheckKind kind) {
    185  PoisonImpl(ptr, value, num);
    186  SetMemCheckKind(ptr, num, kind);
    187 }
    188 
    189 // Conditionally poison a region of memory in debug builds and nightly builds
    190 // when enabled by setting the javascript.options.extra_gc_poisoning pref. Used
    191 // by the GC in places where poisoning has a performance impact.
    192 static inline void Poison(void* ptr, uint8_t value, size_t num,
    193                          MemCheckKind kind) {
    194 #if defined(JS_GC_ALLOW_EXTRA_POISONING)
    195  if (JS::Prefs::extra_gc_poisoning()) {
    196    PoisonImpl(ptr, value, num);
    197  }
    198 #endif
    199  SetMemCheckKind(ptr, num, kind);
    200 }
    201 
    202 // Poison a region of memory in debug builds only.
    203 static inline void DebugOnlyPoison(void* ptr, uint8_t value, size_t num,
    204                                   MemCheckKind kind) {
    205 #if defined(DEBUG)
    206  Poison(ptr, value, num, kind);
    207 #else
    208  SetMemCheckKind(ptr, num, kind);
    209 #endif
    210 }
    211 
    212 }  // namespace js
    213 
    214 #endif /* util_Poison_h */