Bug 984101 - Expand SpiderMonkey's use of poisoning for diagnostics; r=jonco

--HG--
extra : rebase_source : 312db74b85c9b40db1ccfc98a96206d2e1381703
This commit is contained in:
Terrence Cole 2014-03-20 14:38:50 -07:00
Родитель 49d53e031e
Коммит 12b5b02857
10 изменённых файлов: 58 добавлений и 27 удалений

Просмотреть файл

@ -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)