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/. */
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/MemoryChecking.h"
18 #include <algorithm> // std::min
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
40 * Set the first |aNElem| T elements in |aDst| to |aSrc|.
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
) {
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;
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.
95 # error "JS_SWEPT_CODE_PATTERN not defined for this platform"
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).
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.
109 static MOZ_ALWAYS_INLINE
void SetMemCheckKind(void* ptr
, size_t bytes
,
112 case MemCheckKind::MakeUndefined
:
113 MOZ_MAKE_MEM_UNDEFINED(ptr
, bytes
);
115 case MemCheckKind::MakeNoAccess
:
116 MOZ_MAKE_MEM_NOACCESS(ptr
, bytes
);
119 MOZ_CRASH("Invalid kind");
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
136 memset(&poison
, value
, sizeof(poison
));
137 # if defined(JS_PUNBOX64)
138 poison
= poison
& ((uintptr_t(1) << JSVAL_TAG_SHIFT
) - 1);
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
));
154 uint8_t* begin
= static_cast<uint8_t*>(ptr
);
155 mozilla::PodSet(begin
, value
, begin_count
);
156 ptr
= begin
+ begin_count
;
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
);
171 uint8_t* bytes
= static_cast<uint8_t*>(ptr
);
172 uint8_t* end
= bytes
+ num
;
173 mozilla::PodSet(end
- byte_count
, value
, byte_count
);
176 memset(ptr
, value
, num
);
180 // Unconditionally poison a region on memory.
181 static inline void AlwaysPoison(void* ptr
, uint8_t value
, size_t num
,
183 PoisonImpl(ptr
, value
, num
);
184 SetMemCheckKind(ptr
, num
, kind
);
187 #if defined(JS_GC_ALLOW_EXTRA_POISONING)
188 extern bool gExtraPoisoningEnabled
;
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
,
196 #if defined(JS_GC_ALLOW_EXTRA_POISONING)
197 if (js::gExtraPoisoningEnabled
) {
198 PoisonImpl(ptr
, value
, num
);
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
,
209 Poison(ptr
, value
, num
, kind
);
211 SetMemCheckKind(ptr
, num
, kind
);
217 #endif /* util_Poison_h */