mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Fix ECMA DontDelete compliance problems, which create getter/setter security holes (40760, r=shaver).
This commit is contained in:
parent
ad38ae671a
commit
6ca20f928f
@ -950,7 +950,7 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj)
|
||||
*/
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, obj,
|
||||
(jsid)cx->runtime->atomState.typeAtoms[JSTYPE_VOID],
|
||||
JSVAL_VOID, NULL, NULL, 0, NULL)) {
|
||||
JSVAL_VOID, NULL, NULL, JSPROP_PERMANENT, NULL)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
@ -1539,7 +1539,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
* pushobj
|
||||
* newinit
|
||||
* exception
|
||||
* initprop <atom> marked SRC_CATCH
|
||||
* initcatchvar <atom>
|
||||
* enterwith
|
||||
* [< catchguard code >] if there's a catchguard
|
||||
* [ifeq <offset to next catch block>] " "
|
||||
@ -1595,12 +1595,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* setprop <atomIndex> */
|
||||
/* initcatchvar <atomIndex> */
|
||||
ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList);
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
|
||||
EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale));
|
||||
EMIT_ATOM_INDEX_OP(JSOP_INITCATCHVAR, ALE_INDEX(ale));
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
|
||||
js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) {
|
||||
return JS_FALSE;
|
||||
|
@ -745,11 +745,11 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
}
|
||||
|
||||
/* If resolving "prototype" in a clone, clone the parent's prototype. */
|
||||
if (proto)
|
||||
if (proto) {
|
||||
proto = js_NewObject(cx, OBJ_GET_CLASS(cx, proto), proto, NULL);
|
||||
else {
|
||||
} else {
|
||||
/*
|
||||
* Handle the wacky case of a user function Object() - trying to
|
||||
* Handle the wacky case of a user function Object() -- trying to
|
||||
* build a prototype for that will recur back here ad perniciem.
|
||||
*/
|
||||
if (fun->atom != cx->runtime->atomState.ObjectAtom)
|
||||
@ -759,13 +759,13 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* ECMA says that constructor.prototype is DontEnum for user-defined
|
||||
* functions, but DontEnum | ReadOnly | DontDelete for native "system"
|
||||
* constructors such as Object or Function. So lazily set the former
|
||||
* here in fun_resolve, but eagerly define the latter in JS_InitClass,
|
||||
* with the right attributes.
|
||||
* ECMA says that constructor.prototype is DontEnum | DontDelete for
|
||||
* user-defined functions, but DontEnum | ReadOnly | DontDelete for
|
||||
* native "system" constructors such as Object or Function. So lazily
|
||||
* set the former here in fun_resolve, but eagerly define the latter
|
||||
* in JS_InitClass, with the right attributes.
|
||||
*/
|
||||
if (!js_SetClassPrototype(cx, obj, proto, 0)) {
|
||||
if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT)) {
|
||||
cx->newborn[GCX_OBJECT] = NULL;
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
@ -979,14 +979,14 @@ out:
|
||||
}
|
||||
#endif /* JS_HAS_EXPORT_IMPORT */
|
||||
|
||||
static JSBool
|
||||
CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
JSBool *foundp)
|
||||
JSBool
|
||||
js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
JSBool *foundp)
|
||||
{
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
JSBool ok;
|
||||
uintN oldAttrs;
|
||||
uintN oldAttrs, report;
|
||||
JSBool isFunction;
|
||||
jsval value;
|
||||
|
||||
@ -1000,17 +1000,22 @@ CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
|
||||
if ((oldAttrs & JSPROP_READONLY) == (attrs & JSPROP_READONLY) &&
|
||||
!JS_HAS_STRICT_OPTION(cx)) {
|
||||
/* If either property is readonly, we have an error. */
|
||||
report = ((oldAttrs | attrs) & JSPROP_READONLY)
|
||||
? JSREPORT_ERROR
|
||||
: JSREPORT_WARNING | JSREPORT_STRICT;
|
||||
|
||||
if (report != JSREPORT_ERROR) {
|
||||
/*
|
||||
* Allow redefinition if const-ness isn't changing. Also insist that
|
||||
* the new value is not a getter or setter, or if it is, that the old
|
||||
* property was not permanent.
|
||||
* Allow redeclaration of variables and functions, but insist that the
|
||||
* new value is not a getter or setter -- or if it is, insist that the
|
||||
* property being replaced is not permanent.
|
||||
*/
|
||||
if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
|
||||
return JS_TRUE;
|
||||
if (!(oldAttrs & JSPROP_PERMANENT))
|
||||
return JS_TRUE;
|
||||
report = JSREPORT_ERROR;
|
||||
}
|
||||
|
||||
isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
|
||||
@ -1019,10 +1024,7 @@ CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
return JS_FALSE;
|
||||
isFunction = JSVAL_IS_FUNCTION(cx, value);
|
||||
}
|
||||
return JS_ReportErrorFlagsAndNumber(cx,
|
||||
((oldAttrs | attrs) & JSPROP_READONLY)
|
||||
? JSREPORT_ERROR
|
||||
: JSREPORT_WARNING | JSREPORT_STRICT,
|
||||
return JS_ReportErrorFlagsAndNumber(cx, report,
|
||||
js_GetErrorMessage, NULL,
|
||||
JSMSG_REDECLARED_VAR,
|
||||
isFunction
|
||||
@ -2821,7 +2823,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
|
||||
/* Lookup id in order to check for redeclaration problems. */
|
||||
id = (jsid)atom;
|
||||
ok = CheckRedeclaration(cx, obj, id, attrs, &defined);
|
||||
ok = js_CheckRedeclaration(cx, obj, id, attrs, &defined);
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
@ -2885,7 +2887,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* here at runtime as well as at compile-time, to handle eval
|
||||
* as well as multiple HTML script tags.
|
||||
*/
|
||||
ok = CheckRedeclaration(cx, parent, id, attrs, &cond);
|
||||
ok = js_CheckRedeclaration(cx, parent, id, attrs, &cond);
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
@ -3138,7 +3140,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
attrs |= JSPROP_ENUMERATE;
|
||||
|
||||
/* Check for a readonly or permanent property of the same name. */
|
||||
ok = CheckRedeclaration(cx, obj, id, attrs, &cond);
|
||||
ok = js_CheckRedeclaration(cx, obj, id, attrs, &cond);
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
@ -3281,6 +3283,27 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
ok = JS_FALSE;
|
||||
/* let the code at out try to catch the exception. */
|
||||
goto out;
|
||||
|
||||
case JSOP_INITCATCHVAR:
|
||||
/* Pop the property's value into rval. */
|
||||
JS_ASSERT(sp - newsp >= 2);
|
||||
rval = POP();
|
||||
|
||||
/* Get the immediate catch variable name into id. */
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
id = (jsid)atom;
|
||||
|
||||
/* Find the object being initialized at top of stack. */
|
||||
lval = sp[-1];
|
||||
JS_ASSERT(JSVAL_IS_OBJECT(lval));
|
||||
obj = JSVAL_TO_OBJECT(lval);
|
||||
|
||||
/* Define obj[id] to contain rval and to be permanent. */
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL,
|
||||
JSPROP_PERMANENT, NULL);
|
||||
if (!ok)
|
||||
goto out;
|
||||
break;
|
||||
#endif /* JS_HAS_EXCEPTIONS */
|
||||
|
||||
#if JS_HAS_INSTANCEOF
|
||||
|
@ -255,6 +255,10 @@ extern JSBool
|
||||
js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun,
|
||||
JSStackFrame *down, uintN flags, jsval *result);
|
||||
|
||||
extern JSBool
|
||||
js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
||||
JSBool *foundp);
|
||||
|
||||
extern JSBool
|
||||
js_Interpret(JSContext *cx, jsval *result);
|
||||
|
||||
|
@ -467,14 +467,14 @@ js_InitNumberClass(JSContext *cx, JSObject *obj)
|
||||
|
||||
/* ECMA 15.1.1.1 */
|
||||
if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN),
|
||||
NULL, NULL, 0)) {
|
||||
NULL, NULL, JSPROP_PERMANENT)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ECMA 15.1.1.2 */
|
||||
if (!JS_DefineProperty(cx, obj, "Infinity",
|
||||
DOUBLE_TO_JSVAL(rt->jsPositiveInfinity),
|
||||
NULL, NULL, 0)) {
|
||||
NULL, NULL, JSPROP_PERMANENT)) {
|
||||
return NULL;
|
||||
}
|
||||
return proto;
|
||||
|
@ -968,10 +968,11 @@ obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
static JSBool
|
||||
obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
jsval *rval)
|
||||
{
|
||||
jsval fval;
|
||||
jsid id;
|
||||
JSBool found;
|
||||
|
||||
fval = argv[1];
|
||||
if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
|
||||
@ -983,7 +984,8 @@ obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
|
||||
if (!JS_ValueToId(cx, argv[0], &id))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, &found))
|
||||
return JS_FALSE;
|
||||
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
|
||||
(JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
|
||||
JSPROP_GETTER, NULL);
|
||||
@ -991,10 +993,11 @@ obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
|
||||
static JSBool
|
||||
obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
jsval *rval)
|
||||
{
|
||||
jsval fval;
|
||||
jsid id;
|
||||
JSBool found;
|
||||
|
||||
fval = argv[1];
|
||||
if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
|
||||
@ -1006,7 +1009,8 @@ obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
|
||||
if (!JS_ValueToId(cx, argv[0], &id))
|
||||
return JS_FALSE;
|
||||
|
||||
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, &found))
|
||||
return JS_FALSE;
|
||||
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
|
||||
NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
|
||||
JSPROP_SETTER, NULL);
|
||||
@ -2697,8 +2701,8 @@ js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
|
||||
{
|
||||
/*
|
||||
* Use the given attributes for the prototype property of the constructor,
|
||||
* as user-defined constructors have a DontEnum prototype (it can be reset
|
||||
* or even deleted), while native "system" constructors require DontEnum |
|
||||
* as user-defined constructors have a DontEnum | DontDelete prototype (it
|
||||
* may be reset), while native or "system" constructors require DontEnum |
|
||||
* ReadOnly | DontDelete.
|
||||
*/
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, ctor,
|
||||
|
@ -997,9 +997,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
js_printf(jp, "%s",
|
||||
ATOM_BYTES(GET_ATOM(cx, jp->script, pc)));
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
pc += 4; /* initprop, enterwith */
|
||||
pc += 4; /* initcatchvar, enterwith */
|
||||
if (len) {
|
||||
js_printf(jp, " : ");
|
||||
js_printf(jp, " if ");
|
||||
DECOMPILE_CODE(pc, len - 3); /* don't decompile ifeq */
|
||||
js_printf(jp, "%s", POP_STR());
|
||||
pc += len;
|
||||
@ -2036,6 +2036,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
break;
|
||||
|
||||
case JSOP_INITPROP:
|
||||
case JSOP_INITCATCHVAR:
|
||||
rval = POP_STR();
|
||||
atom = GET_ATOM(cx, jp->script, pc);
|
||||
xval = ATOM_BYTES(atom);
|
||||
|
@ -265,3 +265,6 @@ OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 12, JOF_CONST)
|
||||
|
||||
/* ECMA ed. 3 named function expression. */
|
||||
OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 12, JOF_CONST)
|
||||
|
||||
/* Like JSOP_INITPROP, but specialized to make a DontDelete property for ECMA ed. 3 catch variables. */
|
||||
OPDEF(JSOP_INITCATCHVAR,130, "initcatchvar",NULL, 3, 1, 0, 0, JOF_CONST|JOF_PROP)
|
||||
|
@ -2187,11 +2187,11 @@ enum regexp_tinyid {
|
||||
};
|
||||
|
||||
static JSPropertySpec regexp_props[] = {
|
||||
{"source", REGEXP_SOURCE, JSPROP_ENUMERATE | JSPROP_READONLY,0,0},
|
||||
{"global", REGEXP_GLOBAL, JSPROP_ENUMERATE | JSPROP_READONLY,0,0},
|
||||
{"ignoreCase", REGEXP_IGNORE_CASE, JSPROP_ENUMERATE | JSPROP_READONLY,0,0},
|
||||
{"lastIndex", REGEXP_LAST_INDEX, JSPROP_ENUMERATE,0,0},
|
||||
{"multiline", REGEXP_MULTILINE, JSPROP_ENUMERATE | JSPROP_READONLY,0,0},
|
||||
{"source", REGEXP_SOURCE, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,0,0},
|
||||
{"global", REGEXP_GLOBAL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,0,0},
|
||||
{"ignoreCase", REGEXP_IGNORE_CASE, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,0,0},
|
||||
{"lastIndex", REGEXP_LAST_INDEX, JSPROP_ENUMERATE | JSPROP_PERMANENT,0,0},
|
||||
{"multiline", REGEXP_MULTILINE, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,0,0},
|
||||
{0,0,0,0,0}
|
||||
};
|
||||
|
||||
|
@ -290,7 +290,7 @@ enum string_tinyid {
|
||||
};
|
||||
|
||||
static JSPropertySpec string_props[] = {
|
||||
{js_length_str, STRING_LENGTH, JSPROP_READONLY,0,0},
|
||||
{js_length_str, STRING_LENGTH, JSPROP_READONLY|JSPROP_PERMANENT,0,0},
|
||||
{0,0,0,0,0}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user