Bug 950407 - Fix ES6 Proxy forwarding, and apropriately throw on indirect ArrayBuffer __proto__ sets. (r=bholley,Waldo sr=mrbkap)

This commit is contained in:
Eric Faust 2014-01-16 15:09:50 -08:00
parent 6af45f3d96
commit ee4a0c8dfe
6 changed files with 91 additions and 21 deletions

View File

@ -429,6 +429,26 @@ JSObject::setProto(JSContext *cx, JS::HandleObject obj, JS::HandleObject proto,
return js::Proxy::setPrototypeOf(cx, obj, proto, succeeded);
}
/*
* Disallow mutating the [[Prototype]] on ArrayBuffer objects, which
* due to their complicated delegate-object shenanigans can't easily
* have a mutable [[Prototype]].
*/
if (obj->is<js::ArrayBufferObject>()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
"Object", "__proto__ setter", "ArrayBuffer");
return false;
}
/*
* Explicityly disallow mutating the [[Prototype]] of Location objects
* for flash-related security reasons.
*/
if (!strcmp(obj->getClass()->name, "Location")) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL);
return false;
}
/* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))

View File

@ -3250,15 +3250,12 @@ proxy(JSContext *cx, unsigned argc, jsval *vp)
RootedObject handler(cx, NonNullObject(cx, args[1]));
if (!handler)
return false;
RootedObject proto(cx);
if (!JSObject::getProto(cx, target, &proto))
return false;
RootedValue priv(cx, ObjectValue(*target));
ProxyOptions options;
options.setCallable(target->isCallable());
ProxyObject *proxy =
ProxyObject::New(cx, &ScriptedDirectProxyHandler::singleton,
priv, TaggedProto(proto), cx->global(),
priv, TaggedProto(TaggedProto::LazyProto), cx->global(),
options);
if (!proxy)
return false;

View File

@ -0,0 +1,59 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = 'proxy-__proto__.js';
var BUGNUMBER = 950407;
var summary = "Behavior of __proto__ on ES6 proxies";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var protoDesc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
var protoGetter = protoDesc.get;
var protoSetter = protoDesc.set;
function testProxy(target, initialProto)
{
print("Now testing behavior for new Proxy(" + ("" + target) + ", {})");
var pobj = new Proxy(target, {});
// Check [[Prototype]] before attempted mutation
assertEq(Object.getPrototypeOf(pobj), initialProto);
assertEq(protoGetter.call(pobj), initialProto);
// Attempt [[Prototype]] mutation
protoSetter.call(pobj, null);
// Check [[Prototype]] after attempted mutation
assertEq(Object.getPrototypeOf(pobj), null);
assertEq(protoGetter.call(pobj), null);
assertEq(Object.getPrototypeOf(target), null);
}
// Proxy object with non-null [[Prototype]]
var nonNullProto = { toString: function() { return "non-null prototype"; } };
var target = Object.create(nonNullProto);
testProxy(target, nonNullProto);
// Proxy object with null [[Prototype]]
target = Object.create(null);
target.toString = function() { return "null prototype" };
testProxy(target, null);
// Proxy function with [[Call]]
var callForCallOnly = function () { };
callForCallOnly.toString = function() { return "callable target"; };
testProxy(callForCallOnly, Function.prototype);
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -0,0 +1,11 @@
var ab = ArrayBuffer(5);
var p = new Proxy(ab, {});
var ps = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").set;
var threw = 0;
try {
ps.call(p, {});
} catch(ex) {
threw = 1;
}
reportCompare(1, threw, "Setting __proto__ on a proxy to an ArrayBuffer must throw.");

View File

View File

@ -123,17 +123,6 @@ ProtoSetterImpl(JSContext *cx, CallArgs args)
Rooted<JSObject*> obj(cx, &args.thisv().toObject());
/*
* Disallow mutating the [[Prototype]] on ArrayBuffer objects, which
* due to their complicated delegate-object shenanigans can't easily
* have a mutable [[Prototype]].
*/
if (obj->is<ArrayBufferObject>()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
"Object", "__proto__ setter", "ArrayBuffer");
return false;
}
/* Do nothing if __proto__ isn't being set to an object or null. */
if (args.length() == 0 || !args[0].isObjectOrNull()) {
args.rval().setUndefined();
@ -142,12 +131,6 @@ ProtoSetterImpl(JSContext *cx, CallArgs args)
Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull());
unsigned dummy;
RootedId nid(cx, NameToId(cx->names().proto));
RootedValue v(cx);
if (!CheckAccess(cx, obj, nid, JSAccessMode(JSACC_PROTO | JSACC_WRITE), &v, &dummy))
return false;
bool success;
if (!JSObject::setProto(cx, obj, newProto, &success))
return false;