diff --git a/configure.in b/configure.in index c4a3716d526e..3556352f7ec7 100644 --- a/configure.in +++ b/configure.in @@ -7593,6 +7593,18 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi +dnl ======================================================== +dnl JS opt-mode assertions and minidump instrumentation +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(js-diagnostics, +[ --enable-js-diagnostics + Enable JS diagnostic assertions and breakpad data], + JS_CRASH_DIAGNOSTICS=1, + JS_CRASH_DIAGNOSTICS= ) +if test -n "$JS_CRASH_DIAGNOSTICS"; then + AC_DEFINE(JS_CRASH_DIAGNOSTICS) +fi + dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== diff --git a/js/src/configure.in b/js/src/configure.in index 42a31ae154a6..74dcc1b14cbd 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -5022,6 +5022,18 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi +dnl ======================================================== +dnl JS opt-mode assertions and minidump instrumentation +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(js-diagnostics, +[ --enable-js-diagnostics + Enable JS diagnostic assertions and breakpad data], + JS_CRASH_DIAGNOSTICS=1, + JS_CRASH_DIAGNOSTICS= ) +if test -n "$JS_CRASH_DIAGNOSTICS"; then + AC_DEFINE(JS_CRASH_DIAGNOSTICS) +fi + dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b9077db2984d..b1f71676206c 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -562,8 +562,6 @@ JSCompartment::purge(JSContext *cx) #endif #ifdef JS_METHODJIT - js::CheckCompartmentScripts(this); - for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; script = (JSScript *)script->links.next) { diff --git a/js/src/jscrashreport.cpp b/js/src/jscrashreport.cpp index 9a840b0cd1cd..ecc7c3f1185c 100644 --- a/js/src/jscrashreport.cpp +++ b/js/src/jscrashreport.cpp @@ -245,37 +245,37 @@ static Stack gGCStack(JS_CRASH_STACK_GC); static Stack gErrorStack(JS_CRASH_STACK_ERROR); static Ring gRingBuffer(JS_CRASH_RING); +void +SnapshotGCStack() +{ + if (gInitialized) + gGCStack.snapshot(); +} + +void +SnapshotErrorStack() +{ + if (gInitialized) + gErrorStack.snapshot(); +} + +void +SaveCrashData(uint64 tag, void *ptr, size_t size) +{ + if (gInitialized) + gRingBuffer.push(tag, ptr, size); +} + } /* namespace crash */ } /* namespace js */ using namespace js; using namespace js::crash; -JS_FRIEND_API(void) -js_SnapshotGCStack() -{ - if (gInitialized) - gGCStack.snapshot(); -} - -JS_FRIEND_API(void) -js_SnapshotErrorStack() -{ - if (gInitialized) - gErrorStack.snapshot(); -} - -JS_FRIEND_API(void) -js_SaveCrashData(uint64 tag, void *ptr, size_t size) -{ - if (gInitialized) - gRingBuffer.push(tag, ptr, size); -} - JS_PUBLIC_API(void) JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback) { -#if 1 +#ifdef JS_CRASH_DIAGNOSTICS if (!gInitialized) { gInitialized = true; (*callback)(&gGCStack, sizeof(gGCStack)); diff --git a/js/src/jscrashreport.h b/js/src/jscrashreport.h index 293cd4396f02..378cbc94104f 100644 --- a/js/src/jscrashreport.h +++ b/js/src/jscrashreport.h @@ -42,18 +42,46 @@ #define jscrashreport_h___ #include "jstypes.h" +#include "jsutil.h" -JS_BEGIN_EXTERN_C +namespace js { +namespace crash { -JS_FRIEND_API(void) -js_SnapshotGCStack(); +void +SnapshotGCStack(); -JS_FRIEND_API(void) -js_SnapshotErrorStack(); +void +SnapshotErrorStack(); -JS_FRIEND_API(void) -js_SaveCrashData(uint64 tag, void *ptr, size_t size); +void +SaveCrashData(uint64 tag, void *ptr, size_t size); -JS_END_EXTERN_C +template +class StackBuffer { + private: + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + volatile char buffer[size + 4]; + + public: + StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + + buffer[0] = marker; + buffer[1] = '['; + + for (size_t i = 0; i < size; i++) { + if (data) + buffer[i + 2] = ((char *)data)[i]; + else + buffer[i + 2] = 0; + } + + buffer[size - 2] = ']'; + buffer[size - 1] = marker; + } +}; + +} /* namespace crash */ +} /* namespace js */ #endif /* jscrashreport_h___ */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 76d5939b4200..5e2e6776f877 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -241,7 +241,7 @@ Arena::finalize(JSContext *cx) if (!newFreeSpanStart) newFreeSpanStart = thing; t->finalize(cx); - memset(t, JS_FREE_PATTERN, sizeof(T)); + JS_POISON(t, JS_FREE_PATTERN, sizeof(T)); } } } @@ -2399,9 +2399,6 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes); } #endif - - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) - js::CheckCompartmentScripts(*c); } #ifdef JS_THREADSAFE @@ -2693,7 +2690,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) GCCrashData crashData; crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT; crashData.isCompartment = !!comp; - js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); + crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); GCTIMER_BEGIN(rt, comp); @@ -2744,7 +2741,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) rt->gcChunkAllocationSinceLastGC = false; GCTIMER_END(gckind == GC_LAST_CONTEXT); - js_SnapshotGCStack(); + crash::SnapshotGCStack(); } namespace js { diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 52ff83b6381e..93766e5cf26f 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -100,13 +100,6 @@ PushMarkStack(GCMarker *gcmarker, JSShortString *thing); static inline void PushMarkStack(GCMarker *gcmarker, JSString *thing); -static void -volatile_memcpy(volatile unsigned char *dst, const void *src, size_t n) -{ - for (size_t i = 0; i < n; i++) - dst[i] = ((char *)src)[i]; -} - template void Mark(JSTracer *trc, T *thing) @@ -122,15 +115,9 @@ Mark(JSTracer *trc, T *thing) JS_ASSERT(thing->compartment()); JS_ASSERT(thing->compartment()->rt == rt); - if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment && - thing->compartment() != rt->atomsCompartment) - { - volatile unsigned char dbg[sizeof(T) + 2]; - dbg[0] = 0xab; - dbg[1] = 0xcd; - volatile_memcpy(dbg + 2, thing, sizeof(T)); - JS_Assert("compartment mismatch in GC", __FILE__, __LINE__); - } + JS_OPT_ASSERT_IF(rt->gcCheckCompartment, + thing->compartment() == rt->gcCheckCompartment || + thing->compartment() == rt->atomsCompartment); /* * Don't mark things outside a compartment if we are in a per-compartment diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 25d22af52dd0..010299ede142 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3417,24 +3417,6 @@ CopySlots(JSContext *cx, JSObject *from, JSObject *to) return true; } -static void -CheckProxy(JSObject *obj) -{ - if (!obj->isProxy()) - return; - - JSProxyHandler *handler = obj->getProxyHandler(); - if (handler->isCrossCompartment()) - return; - - Value priv = obj->getProxyPrivate(); - if (!priv.isObject()) - return; - - if (priv.toObject().compartment() != obj->compartment()) - JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__); -} - JSObject * JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) { @@ -3472,8 +3454,6 @@ JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) return NULL; } - CheckProxy(clone); - return clone; } @@ -3586,11 +3566,6 @@ JSObject::swap(JSContext *cx, JSObject *other) TradeGuts(this, otherClone); TradeGuts(other, thisClone); - CheckProxy(this); - CheckProxy(other); - CheckProxy(thisClone); - CheckProxy(otherClone); - return true; } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 076e7b242c85..2d6673289955 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1184,11 +1184,6 @@ NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObje else clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass; - if (!handler->isCrossCompartment() && priv.isObject()) { - if (priv.toObject().compartment() != cx->compartment) - JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__); - } - JSObject *obj = NewNonFunction(cx, clasp, proto, parent); if (!obj || !obj->ensureInstanceReservedSlots(cx, 0)) return NULL; diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 8cf1b6a52432..5f7b2769cfdd 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -92,10 +92,6 @@ class JS_FRIEND_API(JSProxyHandler) { return false; } - virtual bool isCrossCompartment() { - return false; - } - inline void *family() { return mFamily; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 2a54d36aebbc..7e0ca1327a50 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -46,6 +46,7 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" +#include "jscrashreport.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" @@ -287,44 +288,26 @@ Bindings::trace(JSTracer *trc) } /* namespace js */ -static void -volatile_memcpy(volatile char *dst, void *src, size_t n) -{ - for (size_t i = 0; i < n; i++) - dst[i] = ((char *)src)[i]; -} - static void CheckScript(JSScript *script, JSScript *prev) { - volatile char dbg1[sizeof(JSScript)], dbg2[sizeof(JSScript)]; +#ifdef JS_CRASH_DIAGNOSTICS if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) { - volatile_memcpy(dbg1, script, sizeof(JSScript)); - if (prev) - volatile_memcpy(dbg2, prev, sizeof(JSScript)); + crash::StackBuffer buf1(script); + crash::StackBuffer buf2(prev); + JS_OPT_ASSERT(false); } - JS_OPT_ASSERT(script->cookie1 == JS_SCRIPT_COOKIE && script->cookie2 == JS_SCRIPT_COOKIE); +#endif } static void CheckScriptOwner(JSScript *script, JSObject *owner) { - if (script->ownerObject != owner) { - volatile char scriptData[sizeof(JSScript)]; - volatile char owner1Data[sizeof(JSObject)], owner2Data[sizeof(JSObject)]; - volatile char savedOwner[sizeof(JSObject *)]; - - volatile_memcpy(scriptData, script, sizeof(JSScript)); - volatile_memcpy(savedOwner, &owner, sizeof(JSObject *)); - if (script->ownerObject != JS_NEW_SCRIPT && script->ownerObject != JS_CACHED_SCRIPT) - volatile_memcpy(owner1Data, script->ownerObject, sizeof(JSObject)); - if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) - volatile_memcpy(owner2Data, owner, sizeof(JSObject)); - } +#ifdef JS_CRASH_DIAGNOSTICS JS_OPT_ASSERT(script->ownerObject == owner); - if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) JS_OPT_ASSERT(script->compartment == owner->compartment()); +#endif } #if JS_HAS_XDR @@ -981,8 +964,10 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom return NULL; PodZero(script); +#ifdef JS_CRASH_DIAGNOSTICS script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE; script->ownerObject = JS_NEW_SCRIPT; +#endif script->length = length; script->version = version; new (&script->bindings) Bindings(cx, emptyCallShape); @@ -1286,8 +1271,10 @@ JSScript::totalSize() void JSScript::setOwnerObject(JSObject *owner) { +#ifdef JS_CRASH_DIAGNOSTICS CheckScriptOwner(this, JS_NEW_SCRIPT); ownerObject = owner; +#endif } /* @@ -1328,22 +1315,6 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) JS_ClearScriptTraps(cx, script); } -namespace js { - -void -CheckCompartmentScripts(JSCompartment *comp) -{ - JSScript *prev = NULL; - for (JSScript *script = (JSScript *)comp->scripts.next; - &script->links != &comp->scripts; - prev = script, script = (JSScript *)script->links.next) - { - CheckScript(script, prev); - } -} - -} /* namespace js */ - static void DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) { @@ -1408,7 +1379,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) if (script->sourceMap) cx->free_(script->sourceMap); - memset(script, 0xdb, script->totalSize()); + JS_POISON(script, 0xdb, sizeof(JSScript)); *(uint32 *)script = caller; cx->free_(script); } @@ -1443,9 +1414,8 @@ js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner) if (owner) CheckScriptOwner(script, owner); - JSRuntime *rt = trc->context->runtime; - if (rt->gcCheckCompartment && script->compartment != rt->gcCheckCompartment) - JS_Assert("compartment mismatch in GC", __FILE__, __LINE__); + DebugOnly rt = trc->context->runtime; + JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment); JSAtomMap *map = &script->atomMap; MarkAtomRange(trc, map->length, map->vector, "atomMap"); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index f0cad899a9c7..8464dbd8652e 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -449,7 +449,9 @@ struct JSScript { jsbytecode *code; /* bytecodes and their immediate operands */ uint32 length; /* length of code vector */ +#ifdef JS_CRASH_DIAGNOSTICS uint32 cookie1; +#endif private: uint16 version; /* JS version under which script was compiled */ @@ -507,7 +509,9 @@ struct JSScript { JSPrincipals *principals;/* principals for this script */ jschar *sourceMap; /* source map file or null */ +#ifdef JS_CRASH_DIAGNOSTICS JSObject *ownerObject; +#endif void setOwnerObject(JSObject *owner); @@ -541,7 +545,9 @@ struct JSScript { /* array of execution counters for every JSOp in the script, by runmode */ JSPCCounters pcCounters; +#ifdef JS_CRASH_DIAGNOSTICS uint32 cookie2; +#endif public: #ifdef JS_METHODJIT @@ -747,19 +753,6 @@ js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner); extern void js_DestroyCachedScript(JSContext *cx, JSScript *script); -namespace js { - -/* - * This diagnostic function checks that a compartment's list of scripts - * contains only valid scripts. It also searches for the target script - * in the list. If expected is true, it asserts that the target script - * is found. If expected is false, it asserts that it's not found. - */ -void -CheckCompartmentScripts(JSCompartment *comp); - -} /* namespace js */ - extern void js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner); diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 4b1682be3f80..28bb73a77970 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -45,7 +45,6 @@ #define jsutil_h___ #include "jstypes.h" -#include "jscrashreport.h" #include "mozilla/Util.h" #include #include @@ -62,12 +61,24 @@ JS_BEGIN_EXTERN_C #define JS_FREE_PATTERN 0xDA +#ifdef JS_CRASH_DIAGNOSTICS + +#define JS_POISON(p, val, size) memset((p), (val), (size)) + #define JS_OPT_ASSERT(expr) \ ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) #define JS_OPT_ASSERT_IF(cond, expr) \ ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) +#else + +#define JS_POISON(p, val, size) ((void) 0) +#define JS_OPT_ASSERT(expr) ((void) 0) +#define JS_OPT_ASSERT_IF(cond, expr) ((void) 0) + +#endif /* JS_CRASH_DIAGNOSTICS */ + #ifdef DEBUG #define JS_ASSERT(expr) \ @@ -227,12 +238,6 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */ #define JS_OOM_POSSIBLY_FAIL() do {} while(0) #endif -static JS_INLINE void *js_record_oom(void *p) { - if (!p) - js_SnapshotErrorStack(); - return p; -} - /* * SpiderMonkey code should not be calling these allocation functions directly. * Instead, all calls should go through JSRuntime, JSContext or OffTheBooks. @@ -240,17 +245,17 @@ static JS_INLINE void *js_record_oom(void *p) { */ static JS_INLINE void* js_malloc(size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(malloc(bytes)); + return malloc(bytes); } static JS_INLINE void* js_calloc(size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(calloc(bytes, 1)); + return calloc(bytes, 1); } static JS_INLINE void* js_realloc(void* p, size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(realloc(p, bytes)); + return realloc(p, bytes); } static JS_INLINE void js_free(void* p) { diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index e7e2fcfb9b59..369518afa410 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -155,10 +155,6 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { virtual void trace(JSTracer *trc, JSObject *wrapper); - virtual bool isCrossCompartment() { - return true; - } - static JSCrossCompartmentWrapper singleton; };