mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
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:
parent
225469fc3e
commit
dacd830421
@ -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());
|
||||
|
@ -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")
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user