js_UnbrandAndClearSlots leaks, use JS_ClearScope and throw if compile-N-go scripts are run after JS_ClearScope (630072, r=brendan).

This commit is contained in:
Andreas Gal 2011-02-13 20:55:33 -08:00
parent 225469fc3e
commit dacd830421
8 changed files with 32 additions and 59 deletions

View File

@ -3186,17 +3186,7 @@ nsJSContext::ClearScope(void *aGlobalObj, PRBool aClearFromProtoChain)
JS_ClearPendingException(mContext);
}
// Hack fix for bug 611653. Originally, this always called JS_ClearScope,
// which was required to avoid leaks. But for native objects, the JS
// engine has an optimization that requires that permanent properties of
// the global object are never deleted. So instead, we call a new special
// API that clears the values of the global, thus avoiding leaks without
// deleting any properties.
if (obj->isNative()) {
js_UnbrandAndClearSlots(mContext, obj);
} else {
JS_ClearScope(mContext, obj);
}
JS_ClearScope(mContext, obj);
if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
JS_ClearScope(mContext, &obj->getProxyExtra().toObject());

View File

@ -348,4 +348,4 @@ MSG_DEF(JSMSG_CANT_CLONE_OBJECT, 265, 0, JSEXN_TYPEERR, "can't clone object
MSG_DEF(JSMSG_NON_NATIVE_SCOPE, 266, 0, JSEXN_TYPEERR, "non-native scope object")
MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
MSG_DEF(JSMSG_INVALID_FOR_IN_INIT, 268, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer")
MSG_DEF(JSMSG_CLEARED_SCOPE, 269, 0, JSEXN_TYPEERR, "attempt to run compile-and-go script on a cleared scope")

View File

@ -2965,8 +2965,8 @@ JS_NewGlobalObject(JSContext *cx, JSClass *clasp)
/* Construct a regexp statics object for this global object. */
JSObject *res = regexp_statics_construct(cx, obj);
if (!res ||
!js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_REGEXP_STATICS,
ObjectValue(*res))) {
!js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_REGEXP_STATICS, ObjectValue(*res)) ||
!js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_FLAGS, Int32Value(0))) {
return NULL;
}
@ -3840,11 +3840,25 @@ JS_ClearScope(JSContext *cx, JSObject *obj)
/* Clear cached class objects on the global object. */
if (obj->isGlobal()) {
/* This can return false but that doesn't mean it failed. */
obj->unbrand(cx);
for (int key = JSProto_Null; key < JSProto_LIMIT * 3; key++)
JS_SetReservedSlot(cx, obj, key, JSVAL_VOID);
/* Clear regexp statics. */
RegExpStatics::extractFrom(obj)->clear();
/* Clear the CSP eval-is-allowed cache. */
JS_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_EVAL_ALLOWED, JSVAL_VOID);
/*
* Mark global as cleared. If we try to execute any compile-and-go
* scripts from here on, we will throw.
*/
int32 flags = obj->getReservedSlot(JSRESERVED_GLOBAL_FLAGS).toInt32();
flags |= JSGLOBAL_FLAGS_CLEARED;
JS_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_FLAGS, Jsvalify(Int32Value(flags)));
}
js_InitRandom(cx);

View File

@ -1962,12 +1962,16 @@ struct JSClass {
#define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6))
/* Additional global reserved slots, beyond those for standard prototypes. */
#define JSRESERVED_GLOBAL_SLOTS_COUNT 5
#define JSRESERVED_GLOBAL_SLOTS_COUNT 6
#define JSRESERVED_GLOBAL_THIS (JSProto_LIMIT * 3)
#define JSRESERVED_GLOBAL_THROWTYPEERROR (JSRESERVED_GLOBAL_THIS + 1)
#define JSRESERVED_GLOBAL_REGEXP_STATICS (JSRESERVED_GLOBAL_THROWTYPEERROR + 1)
#define JSRESERVED_GLOBAL_FUNCTION_NS (JSRESERVED_GLOBAL_REGEXP_STATICS + 1)
#define JSRESERVED_GLOBAL_EVAL_ALLOWED (JSRESERVED_GLOBAL_FUNCTION_NS + 1)
#define JSRESERVED_GLOBAL_FLAGS (JSRESERVED_GLOBAL_EVAL_ALLOWED + 1)
/* Global flags. */
#define JSGLOBAL_FLAGS_CLEARED 0x1
/*
* ECMA-262 requires that most constructors used internally create objects

View File

@ -619,6 +619,15 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
{
JS_ASSERT(script);
/* FIXME: Once bug 470510 is fixed, make this an assert. */
if (script->compileAndGo) {
int32 flags = fp->scopeChain().getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_FLAGS).toInt32();
if (flags & JSGLOBAL_FLAGS_CLEARED) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
return false;
}
}
#ifdef JS_METHODJIT_SPEW
JMCheckLogging();
#endif

View File

@ -4467,40 +4467,6 @@ JSObject::freeSlot(JSContext *cx, uint32 slot)
return false;
}
JS_FRIEND_API(void)
js_UnbrandAndClearSlots(JSContext *cx, JSObject *obj)
{
JS_ASSERT(obj->isNative());
JS_ASSERT(obj->isGlobal());
/* This can return false but that doesn't mean it failed. */
obj->unbrand(cx);
/*
* Clear the prototype cache. We must not clear the other global
* reserved slots, as other code will crash if they are arbitrarily
* reset (e.g., regexp statics).
*/
for (int key = JSProto_Null; key < JSRESERVED_GLOBAL_THIS; key++)
JS_SetReservedSlot(cx, obj, key, JSVAL_VOID);
/*
* Clear the non-reserved slots.
*/
ClearValueRange(obj->slots + JSCLASS_RESERVED_SLOTS(obj->clasp),
obj->capacity - JSCLASS_RESERVED_SLOTS(obj->clasp),
obj->clasp == &js_ArrayClass);
/*
* We just overwrote all slots to undefined, so the freelist has
* been trashed. We need to clear the head pointer or else we will
* crash later. This leaks slots but the object is all but dead
* anyway.
*/
if (obj->hasPropertyTable())
obj->lastProperty()->getTable()->freelist = SHAPE_INVALID_SLOT;
}
/* JSBOXEDWORD_INT_MAX as a string */
#define JSBOXEDWORD_INT_MAX_STRING "1073741823"

View File

@ -1768,15 +1768,6 @@ extern JSBool
js_SetNativeAttributes(JSContext *cx, JSObject *obj, js::Shape *shape,
uintN attrs);
/*
* Hack fix for bug 611653: Do not use for any other purpose.
*
* Unbrand and set all slot values to undefined (except reserved slots that
* are not used for cached prototypes).
*/
JS_FRIEND_API(void)
js_UnbrandAndClearSlots(JSContext *cx, JSObject *obj);
namespace js {
/*

View File

@ -297,7 +297,6 @@ struct Shape : public JSObjectMap
friend class js::PropertyTree;
friend class js::Bindings;
friend bool IsShapeAboutToBeFinalized(JSContext *cx, const js::Shape *shape);
friend JS_FRIEND_API(void) ::js_UnbrandAndClearSlots(JSContext *cx, JSObject *obj);
/*
* numLinearSearches starts at zero and is incremented initially on each