Bug 1885489 - Part 5: Add SnapshotIterator::readInt32(). r=iain
[gecko.git] / js / src / util / Poison.h
blob2726fa13d32fdc9caa67cc7141227fab2cbe1012
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/. */
7 /*
8 * Memory poisoning.
9 */
11 #ifndef util_Poison_h
12 #define util_Poison_h
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/MemoryChecking.h"
18 #include <algorithm> // std::min
19 #include <stddef.h>
20 #include <stdint.h>
21 #include <string.h>
23 #include "jstypes.h"
25 #include "js/Value.h"
26 #include "util/DiagnosticAssertions.h"
29 * Allow extra GC poisoning to be enabled in crash-diagnostics and zeal
30 * builds. Except in debug builds, this must be enabled by setting the
31 * JSGC_EXTRA_POISONING environment variable.
33 #if defined(JS_CRASH_DIAGNOSTICS) || defined(JS_GC_ZEAL)
34 # define JS_GC_ALLOW_EXTRA_POISONING 1
35 #endif
37 namespace mozilla {
39 /**
40 * Set the first |aNElem| T elements in |aDst| to |aSrc|.
42 template <typename T>
43 static MOZ_ALWAYS_INLINE void PodSet(T* aDst, const T& aSrc, size_t aNElem) {
44 for (const T* dstend = aDst + aNElem; aDst < dstend; ++aDst) {
45 *aDst = aSrc;
49 } /* namespace mozilla */
52 * Patterns used by SpiderMonkey to overwrite unused memory. If you are
53 * accessing an object with one of these patterns, you probably have a dangling
54 * pointer. These values should be odd.
56 const uint8_t JS_FRESH_NURSERY_PATTERN = 0x2F;
57 const uint8_t JS_SWEPT_NURSERY_PATTERN = 0x2B;
58 const uint8_t JS_ALLOCATED_NURSERY_PATTERN = 0x2D;
59 const uint8_t JS_NOTINUSE_TRAILER_PATTERN = 0x43;
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_RESET_VALUE_PATTERN = 0xBB;
69 const uint8_t JS_POISONED_JSSCRIPT_DATA_PATTERN = 0xDB;
70 const uint8_t JS_OOB_PARSE_NODE_PATTERN = 0xFF;
71 const uint8_t JS_LIFO_UNDEFINED_PATTERN = 0xcd;
72 const uint8_t JS_LIFO_UNINITIALIZED_PATTERN = 0xce;
74 // Even ones
75 const uint8_t JS_SCOPE_DATA_TRAILING_NAMES_PATTERN = 0xCC;
78 * Ensure JS_SWEPT_CODE_PATTERN is a byte pattern that will crash immediately
79 * when executed, so either an undefined instruction or an instruction that's
80 * illegal in user mode.
82 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || \
83 defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32)
84 # define JS_SWEPT_CODE_PATTERN 0xED // IN instruction, crashes in user mode.
85 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
86 # define JS_SWEPT_CODE_PATTERN 0xA3 // undefined instruction
87 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
88 # define JS_SWEPT_CODE_PATTERN 0x01 // undefined instruction
89 #elif defined(JS_CODEGEN_LOONG64)
90 # define JS_SWEPT_CODE_PATTERN 0x01 // undefined instruction
91 #elif defined(JS_CODEGEN_RISCV64)
92 # define JS_SWEPT_CODE_PATTERN \
93 0x29 // illegal sb instruction, crashes in user mode.
94 #else
95 # error "JS_SWEPT_CODE_PATTERN not defined for this platform"
96 #endif
98 enum class MemCheckKind : uint8_t {
99 // Marks a region as poisoned. Memory sanitizers like ASan will crash when
100 // accessing it (both reads and writes).
101 MakeNoAccess,
103 // Marks a region as having undefined contents. In ASan builds this just
104 // unpoisons the memory. MSan and Valgrind can also use this to find
105 // reads of uninitialized memory.
106 MakeUndefined,
109 static MOZ_ALWAYS_INLINE void SetMemCheckKind(void* ptr, size_t bytes,
110 MemCheckKind kind) {
111 switch (kind) {
112 case MemCheckKind::MakeUndefined:
113 MOZ_MAKE_MEM_UNDEFINED(ptr, bytes);
114 return;
115 case MemCheckKind::MakeNoAccess:
116 MOZ_MAKE_MEM_NOACCESS(ptr, bytes);
117 return;
119 MOZ_CRASH("Invalid kind");
122 namespace js {
124 static inline void PoisonImpl(void* ptr, uint8_t value, size_t num) {
125 // Without a valid Value tag, a poisoned Value may look like a valid
126 // floating point number. To ensure that we crash more readily when
127 // observing a poisoned Value, we make the poison an invalid ObjectValue.
128 // Unfortunately, this adds about 2% more overhead, so we can only enable
129 // it in debug.
130 #if defined(DEBUG)
131 if (!num) {
132 return;
135 uintptr_t poison;
136 memset(&poison, value, sizeof(poison));
137 # if defined(JS_PUNBOX64)
138 poison = poison & ((uintptr_t(1) << JSVAL_TAG_SHIFT) - 1);
139 # endif
140 JS::Value v = js::PoisonedObjectValue(poison);
142 # if defined(JS_NUNBOX32)
143 // On 32-bit arch, `ptr` is 4 bytes aligned, and it's less than
144 // `sizeof(JS::Value)` == 8 bytes.
146 // `mozilla::PodSet` with `v` requires the pointer to be 8 bytes aligned if
147 // `value_count > 0`.
149 // If the pointer isn't 8 bytes aligned, fill the leading 1-4 bytes
150 // separately here, so that either the pointer is 8 bytes aligned, or
151 // we have no more bytes to fill.
152 uintptr_t begin_count = std::min(num, uintptr_t(ptr) % sizeof(JS::Value));
153 if (begin_count) {
154 uint8_t* begin = static_cast<uint8_t*>(ptr);
155 mozilla::PodSet(begin, value, begin_count);
156 ptr = begin + begin_count;
157 num -= begin_count;
159 if (!num) {
160 return;
163 # endif
165 MOZ_ASSERT(uintptr_t(ptr) % sizeof(JS::Value) == 0);
167 size_t value_count = num / sizeof(v);
168 size_t byte_count = num % sizeof(v);
169 mozilla::PodSet(reinterpret_cast<JS::Value*>(ptr), v, value_count);
170 if (byte_count) {
171 uint8_t* bytes = static_cast<uint8_t*>(ptr);
172 uint8_t* end = bytes + num;
173 mozilla::PodSet(end - byte_count, value, byte_count);
175 #else // !DEBUG
176 memset(ptr, value, num);
177 #endif // !DEBUG
180 // Unconditionally poison a region on memory.
181 static inline void AlwaysPoison(void* ptr, uint8_t value, size_t num,
182 MemCheckKind kind) {
183 PoisonImpl(ptr, value, num);
184 SetMemCheckKind(ptr, num, kind);
187 #if defined(JS_GC_ALLOW_EXTRA_POISONING)
188 extern bool gExtraPoisoningEnabled;
189 #endif
191 // Conditionally poison a region of memory in debug builds and nightly builds
192 // when enabled by setting the JSGC_EXTRA_POISONING environment variable. Used
193 // by the GC in places where poisoning has a performance impact.
194 static inline void Poison(void* ptr, uint8_t value, size_t num,
195 MemCheckKind kind) {
196 #if defined(JS_GC_ALLOW_EXTRA_POISONING)
197 if (js::gExtraPoisoningEnabled) {
198 PoisonImpl(ptr, value, num);
200 #endif
201 SetMemCheckKind(ptr, num, kind);
204 // Poison a region of memory in debug builds. Can be disabled by setting the
205 // JSGC_EXTRA_POISONING environment variable.
206 static inline void DebugOnlyPoison(void* ptr, uint8_t value, size_t num,
207 MemCheckKind kind) {
208 #if defined(DEBUG)
209 Poison(ptr, value, num, kind);
210 #else
211 SetMemCheckKind(ptr, num, kind);
212 #endif
215 } // namespace js
217 #endif /* util_Poison_h */