Fix ECMA DontDelete compliance problems, which create getter/setter security holes (40760, r=shaver).

This commit is contained in:
brendan%mozilla.org 2000-06-02 00:02:46 +00:00
parent ad38ae671a
commit 6ca20f928f
11 changed files with 80 additions and 45 deletions

View File

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

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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}
};

View File

@ -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}
};