зеркало из https://github.com/mozilla/gecko-dev.git
Bug 984101 - Expand SpiderMonkey's use of poisoning for diagnostics; r=jonco
--HG-- extra : rebase_source : 312db74b85c9b40db1ccfc98a96206d2e1381703
This commit is contained in:
Родитель
49d53e031e
Коммит
12b5b02857
|
@ -35,10 +35,18 @@ namespace mozilla {}
|
||||||
namespace js {}
|
namespace js {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pattern used to overwrite freed memory. If you are accessing an object with
|
* Patterns used by SpiderMonkey to overwrite unused memory. If you are
|
||||||
* this pattern, you probably have a dangling pointer.
|
* accessing an object with one of these pattern, you probably have a dangling
|
||||||
|
* pointer.
|
||||||
*/
|
*/
|
||||||
#define JS_FREE_PATTERN 0xDA
|
#define JS_FRESH_NURSERY_PATTERN 0x2F
|
||||||
|
#define JS_SWEPT_NURSERY_PATTERN 0x2B
|
||||||
|
#define JS_ALLOCATED_NURSERY_PATTERN 0x2D
|
||||||
|
#define JS_FRESH_TENURED_PATTERN 0x4F
|
||||||
|
#define JS_SWEPT_TENURED_PATTERN 0x4B
|
||||||
|
#define JS_ALLOCATED_TENURED_PATTERN 0x4D
|
||||||
|
#define JS_SWEPT_CODE_PATTERN 0x3b
|
||||||
|
#define JS_SWEPT_FRAME_PATTERN 0x5b
|
||||||
|
|
||||||
#define JS_ASSERT(expr) MOZ_ASSERT(expr)
|
#define JS_ASSERT(expr) MOZ_ASSERT(expr)
|
||||||
#define JS_ASSERT_IF(cond, expr) MOZ_ASSERT_IF(cond, expr)
|
#define JS_ASSERT_IF(cond, expr) MOZ_ASSERT_IF(cond, expr)
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
|
|
||||||
#if ENABLE_ASSEMBLER
|
#if ENABLE_ASSEMBLER
|
||||||
|
|
||||||
|
#include "jsutil.h"
|
||||||
|
|
||||||
// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid
|
// ASSERT_VALID_CODE_POINTER checks that ptr is a non-null pointer, and that it is a valid
|
||||||
// instruction address on the platform (for example, check any alignment requirements).
|
// instruction address on the platform (for example, check any alignment requirements).
|
||||||
#if WTF_CPU_ARM_THUMB2
|
#if WTF_CPU_ARM_THUMB2
|
||||||
|
@ -199,7 +201,7 @@ public:
|
||||||
if (!m_executablePool)
|
if (!m_executablePool)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memset(m_code.executableAddress(), JS_FREE_PATTERN, m_allocSize);
|
JS_POISON(m_code.executableAddress(), JS_SWEPT_CODE_PATTERN, m_allocSize);
|
||||||
|
|
||||||
m_code = MacroAssemblerCodePtr();
|
m_code = MacroAssemblerCodePtr();
|
||||||
|
|
||||||
|
|
|
@ -288,6 +288,7 @@ struct FreeSpan
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
checkSpan();
|
checkSpan();
|
||||||
|
JS_POISON(reinterpret_cast<void *>(thing), JS_ALLOCATED_TENURED_PATTERN, thingSize);
|
||||||
return reinterpret_cast<void *>(thing);
|
return reinterpret_cast<void *>(thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,17 +97,36 @@ static void MarkChildren(JSTracer *trc, jit::JitCode *code);
|
||||||
|
|
||||||
/*** Object Marking ***/
|
/*** Object Marking ***/
|
||||||
|
|
||||||
#ifdef DEBUG
|
#if defined(DEBUG)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static inline bool
|
static inline bool
|
||||||
IsThingPoisoned(T *thing)
|
IsThingPoisoned(T *thing)
|
||||||
{
|
{
|
||||||
const uint8_t pb = JS_FREE_PATTERN;
|
static_assert(sizeof(T) >= sizeof(FreeSpan) + sizeof(uint32_t),
|
||||||
const uint32_t pw = pb | (pb << 8) | (pb << 16) | (pb << 24);
|
"Ensure it is well defined to look past any free span that "
|
||||||
JS_STATIC_ASSERT(sizeof(T) >= sizeof(FreeSpan) + sizeof(uint32_t));
|
"may be embedded in the thing's header when freed.");
|
||||||
uint32_t *p =
|
const uint8_t poisonBytes[] = {
|
||||||
reinterpret_cast<uint32_t *>(reinterpret_cast<FreeSpan *>(thing) + 1);
|
JS_FRESH_NURSERY_PATTERN,
|
||||||
return *p == pw;
|
JS_SWEPT_NURSERY_PATTERN,
|
||||||
|
JS_ALLOCATED_NURSERY_PATTERN,
|
||||||
|
JS_FRESH_TENURED_PATTERN,
|
||||||
|
JS_SWEPT_TENURED_PATTERN,
|
||||||
|
JS_ALLOCATED_TENURED_PATTERN,
|
||||||
|
JS_SWEPT_CODE_PATTERN,
|
||||||
|
JS_SWEPT_FRAME_PATTERN
|
||||||
|
};
|
||||||
|
const int numPoisonBytes = sizeof(poisonBytes) / sizeof(poisonBytes[0]);
|
||||||
|
uint32_t *p = reinterpret_cast<uint32_t *>(reinterpret_cast<FreeSpan *>(thing) + 1);
|
||||||
|
// Note: all free patterns are odd to make the common, not-poisoned case a single test.
|
||||||
|
if ((*p & 1) == 0)
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < numPoisonBytes; ++i) {
|
||||||
|
const uint8_t pb = poisonBytes[i];
|
||||||
|
const uint32_t pw = pb | (pb << 8) | (pb << 16) | (pb << 24);
|
||||||
|
if (*p == pw)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -62,9 +62,7 @@ js::Nursery::init()
|
||||||
currentStart_ = start();
|
currentStart_ = start();
|
||||||
rt->gcNurseryEnd_ = chunk(LastNurseryChunk).end();
|
rt->gcNurseryEnd_ = chunk(LastNurseryChunk).end();
|
||||||
numActiveChunks_ = 1;
|
numActiveChunks_ = 1;
|
||||||
#ifdef JS_GC_ZEAL
|
JS_POISON(heap, JS_FRESH_NURSERY_PATTERN, NurserySize);
|
||||||
JS_POISON(heap, FreshNursery, NurserySize);
|
|
||||||
#endif
|
|
||||||
setCurrentChunk(0);
|
setCurrentChunk(0);
|
||||||
updateDecommittedRegion();
|
updateDecommittedRegion();
|
||||||
|
|
||||||
|
@ -170,9 +168,7 @@ js::Nursery::allocate(size_t size)
|
||||||
void *thing = (void *)position();
|
void *thing = (void *)position();
|
||||||
position_ = position() + size;
|
position_ = position() + size;
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
JS_POISON(thing, JS_ALLOCATED_NURSERY_PATTERN, size);
|
||||||
JS_POISON(thing, AllocatedThing, size);
|
|
||||||
#endif
|
|
||||||
return thing;
|
return thing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -877,7 +873,7 @@ js::Nursery::sweep(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
#ifdef JS_GC_ZEAL
|
||||||
/* Poison the nursery contents so touching a freed object will crash. */
|
/* Poison the nursery contents so touching a freed object will crash. */
|
||||||
JS_POISON((void *)start(), SweptNursery, NurserySize - sizeof(JSRuntime *));
|
JS_POISON((void *)start(), JS_SWEPT_NURSERY_PATTERN, NurserySize);
|
||||||
for (int i = 0; i < NumNurseryChunks; ++i)
|
for (int i = 0; i < NumNurseryChunks; ++i)
|
||||||
chunk(i).trailer.runtime = runtime();
|
chunk(i).trailer.runtime = runtime();
|
||||||
|
|
||||||
|
@ -890,6 +886,11 @@ js::Nursery::sweep(JSRuntime *rt)
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
#ifdef JS_CRASH_DIAGNOSTICS
|
||||||
|
JS_POISON((void *)start(), JS_SWEPT_NURSERY_PATTERN, allocationEnd() - start());
|
||||||
|
for (int i = 0; i < numActiveChunks_; ++i)
|
||||||
|
chunk(i).trailer.runtime = runtime();
|
||||||
|
#endif
|
||||||
setCurrentChunk(0);
|
setCurrentChunk(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,7 +901,9 @@ js::Nursery::sweep(JSRuntime *rt)
|
||||||
void
|
void
|
||||||
js::Nursery::growAllocableSpace()
|
js::Nursery::growAllocableSpace()
|
||||||
{
|
{
|
||||||
|
#ifdef JS_GC_ZEAL
|
||||||
MOZ_ASSERT_IF(runtime()->gcZeal_ == ZealGenerationalGCValue, numActiveChunks_ == NumNurseryChunks);
|
MOZ_ASSERT_IF(runtime()->gcZeal_ == ZealGenerationalGCValue, numActiveChunks_ == NumNurseryChunks);
|
||||||
|
#endif
|
||||||
numActiveChunks_ = Min(numActiveChunks_ * 2, NumNurseryChunks);
|
numActiveChunks_ = Min(numActiveChunks_ * 2, NumNurseryChunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -293,9 +293,6 @@ class Nursery
|
||||||
* In debug and zeal builds, these bytes indicate the state of an unused
|
* In debug and zeal builds, these bytes indicate the state of an unused
|
||||||
* segment of nursery-allocated memory.
|
* segment of nursery-allocated memory.
|
||||||
*/
|
*/
|
||||||
static const uint8_t FreshNursery = 0x2a;
|
|
||||||
static const uint8_t SweptNursery = 0x2b;
|
|
||||||
static const uint8_t AllocatedThing = 0x2c;
|
|
||||||
void enterZealMode() {
|
void enterZealMode() {
|
||||||
if (isEnabled())
|
if (isEnabled())
|
||||||
numActiveChunks_ = NumNurseryChunks;
|
numActiveChunks_ = NumNurseryChunks;
|
||||||
|
|
|
@ -742,7 +742,7 @@ JitCode::finalize(FreeOp *fop)
|
||||||
// Don't do this if the Ion code is protected, as the signal handler will
|
// Don't do this if the Ion code is protected, as the signal handler will
|
||||||
// deadlock trying to reacquire the interrupt lock.
|
// deadlock trying to reacquire the interrupt lock.
|
||||||
if (fop->runtime()->jitRuntime() && !fop->runtime()->jitRuntime()->ionCodeProtected())
|
if (fop->runtime()->jitRuntime() && !fop->runtime()->jitRuntime()->ionCodeProtected())
|
||||||
memset(code_, JS_FREE_PATTERN, bufferSize_);
|
memset(code_, JS_SWEPT_CODE_PATTERN, bufferSize_);
|
||||||
code_ = nullptr;
|
code_ = nullptr;
|
||||||
|
|
||||||
// Code buffers are stored inside JSC pools.
|
// Code buffers are stored inside JSC pools.
|
||||||
|
|
|
@ -484,7 +484,7 @@ Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
|
||||||
if (!newFreeSpanStart)
|
if (!newFreeSpanStart)
|
||||||
newFreeSpanStart = thing;
|
newFreeSpanStart = thing;
|
||||||
t->finalize(fop);
|
t->finalize(fop);
|
||||||
JS_POISON(t, JS_FREE_PATTERN, thingSize);
|
JS_POISON(t, JS_SWEPT_TENURED_PATTERN, thingSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,6 +493,7 @@ Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
|
||||||
JS_ASSERT(newListTail == &newListHead);
|
JS_ASSERT(newListTail == &newListHead);
|
||||||
JS_ASSERT(!newFreeSpanStart ||
|
JS_ASSERT(!newFreeSpanStart ||
|
||||||
newFreeSpanStart == thingsStart(thingKind));
|
newFreeSpanStart == thingsStart(thingKind));
|
||||||
|
JS_POISON(data, JS_SWEPT_TENURED_PATTERN, sizeof(data));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +782,7 @@ Chunk::prepareToBeFreed(JSRuntime *rt)
|
||||||
void
|
void
|
||||||
Chunk::init(JSRuntime *rt)
|
Chunk::init(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
JS_POISON(this, JS_FREE_PATTERN, ChunkSize);
|
JS_POISON(this, JS_FRESH_TENURED_PATTERN, ChunkSize);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We clear the bitmap to guard against xpc_IsGrayGCThing being called on
|
* We clear the bitmap to guard against xpc_IsGrayGCThing being called on
|
||||||
|
|
|
@ -1472,8 +1472,8 @@ FinalizeGenerator(FreeOp *fop, JSObject *obj)
|
||||||
gen->state == JSGEN_OPEN);
|
gen->state == JSGEN_OPEN);
|
||||||
// If gen->state is JSGEN_CLOSED, gen->fp may be nullptr.
|
// If gen->state is JSGEN_CLOSED, gen->fp may be nullptr.
|
||||||
if (gen->fp)
|
if (gen->fp)
|
||||||
JS_POISON(gen->fp, JS_FREE_PATTERN, sizeof(InterpreterFrame));
|
JS_POISON(gen->fp, JS_SWEPT_FRAME_PATTERN, sizeof(InterpreterFrame));
|
||||||
JS_POISON(gen, JS_FREE_PATTERN, sizeof(JSGenerator));
|
JS_POISON(gen, JS_SWEPT_FRAME_PATTERN, sizeof(JSGenerator));
|
||||||
fop->free_(gen);
|
fop->free_(gen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -278,7 +278,7 @@ ClearAllBitArrayElements(size_t *array, size_t length)
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
# define JS_CRASH_DIAGNOSTICS 1
|
# define JS_CRASH_DIAGNOSTICS 1
|
||||||
#endif
|
#endif
|
||||||
#ifdef JS_CRASH_DIAGNOSTICS
|
#if defined(JS_CRASH_DIAGNOSTICS) || defined(JS_GC_ZEAL)
|
||||||
# define JS_POISON(p, val, size) memset((p), (val), (size))
|
# define JS_POISON(p, val, size) memset((p), (val), (size))
|
||||||
#else
|
#else
|
||||||
# define JS_POISON(p, val, size) ((void) 0)
|
# define JS_POISON(p, val, size) ((void) 0)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче