Bug 1159469 - Make sure public jsapi Map/Set calls deal with compartments/proxies; r=bz r=jorendorff

This commit is contained in:
Kyle Machulis 2015-05-29 15:31:36 -07:00
parent f863dc86c7
commit 235976fe9b
2 changed files with 176 additions and 64 deletions

View File

@ -2220,6 +2220,7 @@ static
bool
forEach(const char* funcName, JSContext *cx, HandleObject obj, HandleValue callbackFn, HandleValue thisArg)
{
CHECK_REQUEST(cx);
RootedId forEachId(cx, NameToId(cx->names().forEach));
RootedFunction forEachFunc(cx, JS::GetSelfHostedFunction(cx, funcName, forEachId, 2));
if (!forEachFunc)
@ -2234,6 +2235,77 @@ forEach(const char* funcName, JSContext *cx, HandleObject obj, HandleValue callb
return Invoke(cx, args);
}
// Handles Clear/Size for public jsapi map/set access
template<typename RetT>
RetT
CallObjFunc(RetT(*ObjFunc)(JSContext*, HandleObject), JSContext* cx, HandleObject obj)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj);
// Always unwrap, in case this is an xray or cross-compartment wrapper.
RootedObject unwrappedObj(cx);
unwrappedObj = UncheckedUnwrap(obj);
// Enter the compartment of the backing object before calling functions on
// it.
JSAutoCompartment ac(cx, unwrappedObj);
return ObjFunc(cx, unwrappedObj);
}
// Handles Has/Delete for public jsapi map/set access
bool
CallObjFunc(bool(*ObjFunc)(JSContext *cx, HandleObject obj, HandleValue key, bool *rval),
JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, key);
// Always unwrap, in case this is an xray or cross-compartment wrapper.
RootedObject unwrappedObj(cx);
unwrappedObj = UncheckedUnwrap(obj);
JSAutoCompartment ac(cx, unwrappedObj);
// If we're working with a wrapped map/set, rewrap the key into the
// compartment of the unwrapped map/set.
RootedValue wrappedKey(cx, key);
if (obj != unwrappedObj) {
if (!JS_WrapValue(cx, &wrappedKey))
return false;
}
return ObjFunc(cx, unwrappedObj, wrappedKey, rval);
}
// Handles iterator generation for public jsapi map/set access
template<typename Iter>
bool
CallObjFunc(bool(*ObjFunc)(JSContext* cx, Iter kind,
HandleObject obj, MutableHandleValue iter),
JSContext *cx, Iter iterType, HandleObject obj, MutableHandleValue rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj);
// Always unwrap, in case this is an xray or cross-compartment wrapper.
RootedObject unwrappedObj(cx);
unwrappedObj = UncheckedUnwrap(obj);
{
// Retrieve the iterator while in the unwrapped map/set's compartment,
// otherwise we'll crash on a compartment assert.
JSAutoCompartment ac(cx, unwrappedObj);
if (!ObjFunc(cx, iterType, unwrappedObj, rval))
return false;
}
// If the caller is in a different compartment than the map/set, rewrap the
// iterator object into the caller's compartment.
if (obj != unwrappedObj) {
if (!JS_WrapValue(cx, rval))
return false;
}
return true;
}
/*** JS public APIs **********************************************************/
JS_PUBLIC_API(JSObject*)
@ -2245,78 +2317,106 @@ JS::NewMapObject(JSContext* cx)
JS_PUBLIC_API(uint32_t)
JS::MapSize(JSContext* cx, HandleObject obj)
{
CHECK_REQUEST(cx);
return MapObject::size(cx, obj);
return CallObjFunc<uint32_t>(&MapObject::size, cx, obj);
}
JS_PUBLIC_API(bool)
JS::MapGet(JSContext* cx, HandleObject obj,
HandleValue key, MutableHandleValue rval)
JS::MapGet(JSContext* cx, HandleObject obj, HandleValue key, MutableHandleValue rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, key, rval);
return MapObject::get(cx, obj, key, rval);
assertSameCompartment(cx, obj, key, rval);
// Unwrap the object, and enter its compartment. If object isn't wrapped,
// this is essentially a noop.
RootedObject unwrappedObj(cx);
unwrappedObj = UncheckedUnwrap(obj);
{
JSAutoCompartment ac(cx, unwrappedObj);
RootedValue wrappedKey(cx, key);
// If we passed in a wrapper, wrap our key into its compartment now.
if (obj != unwrappedObj) {
if (!JS_WrapValue(cx, &wrappedKey))
return false;
}
if (!MapObject::get(cx, unwrappedObj, wrappedKey, rval))
return false;
}
// If we passed in a wrapper, wrap our return value on the way out.
if (obj != unwrappedObj) {
if (!JS_WrapValue(cx, rval))
return false;
}
return true;
}
JS_PUBLIC_API(bool)
JS::MapSet(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, key, val);
// Unwrap the object, and enter its compartment. If object isn't wrapped,
// this is essentially a noop.
RootedObject unwrappedObj(cx);
unwrappedObj = UncheckedUnwrap(obj);
{
JSAutoCompartment ac(cx, unwrappedObj);
// If we passed in a wrapper, wrap both key and value before adding to
// the map
RootedValue wrappedKey(cx, key);
RootedValue wrappedValue(cx, val);
if (obj != unwrappedObj) {
if (!JS_WrapValue(cx, &wrappedKey) ||
!JS_WrapValue(cx, &wrappedValue)) {
return false;
}
}
return MapObject::set(cx, unwrappedObj, wrappedKey, wrappedValue);
}
}
JS_PUBLIC_API(bool)
JS::MapHas(JSContext* cx, HandleObject obj, HandleValue key, bool* rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, key);
return MapObject::has(cx, obj, key, rval);
return CallObjFunc(MapObject::has, cx, obj, key, rval);
}
JS_PUBLIC_API(bool)
JS::MapDelete(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
JS::MapDelete(JSContext *cx, HandleObject obj, HandleValue key, bool* rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, key);
return MapObject::delete_(cx, obj, key, rval);
}
JS_PUBLIC_API(bool)
JS::MapSet(JSContext *cx, HandleObject obj,
HandleValue key, HandleValue val)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, key, val);
return MapObject::set(cx, obj, key, val);
return CallObjFunc(MapObject::delete_, cx, obj, key, rval);
}
JS_PUBLIC_API(bool)
JS::MapClear(JSContext* cx, HandleObject obj)
{
CHECK_REQUEST(cx);
return MapObject::clear(cx, obj);
return CallObjFunc(&MapObject::clear, cx, obj);
}
JS_PUBLIC_API(bool)
JS::MapKeys(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, rval);
return MapObject::iterator(cx, MapObject::Keys, obj, rval);
return CallObjFunc(&MapObject::iterator, cx, MapObject::Keys, obj, rval);
}
JS_PUBLIC_API(bool)
JS::MapValues(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, rval);
return MapObject::iterator(cx, MapObject::Values, obj, rval);
return CallObjFunc(&MapObject::iterator, cx, MapObject::Values, obj, rval);
}
JS_PUBLIC_API(bool)
JS::MapEntries(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, rval);
return MapObject::iterator(cx, MapObject::Entries, obj, rval);
return CallObjFunc(&MapObject::iterator, cx, MapObject::Entries, obj, rval);
}
JS_PUBLIC_API(bool)
JS::MapForEach(JSContext *cx, HandleObject obj, HandleValue callbackFn, HandleValue thisVal)
{
CHECK_REQUEST(cx);
return forEach("MapForEach", cx, obj, callbackFn, thisVal);
}
@ -2329,66 +2429,70 @@ JS::NewSetObject(JSContext *cx)
JS_PUBLIC_API(uint32_t)
JS::SetSize(JSContext *cx, HandleObject obj)
{
CHECK_REQUEST(cx);
return SetObject::size(cx, obj);
return CallObjFunc<uint32_t>(&SetObject::size, cx, obj);
}
JS_PUBLIC_API(bool)
JS::SetHas(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
JS::SetAdd(JSContext *cx, HandleObject obj, HandleValue key)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, key);
return SetObject::has(cx, obj, key, rval);
assertSameCompartment(cx, obj, key);
// Unwrap the object, and enter its compartment. If object isn't wrapped,
// this is essentially a noop.
RootedObject unwrappedObj(cx);
unwrappedObj = UncheckedUnwrap(obj);
{
JSAutoCompartment ac(cx, unwrappedObj);
// If we passed in a wrapper, wrap key before adding to the set
RootedValue wrappedKey(cx, key);
if (obj != unwrappedObj) {
if (!JS_WrapValue(cx, &wrappedKey))
return false;
}
return SetObject::add(cx, unwrappedObj, wrappedKey);
}
}
JS_PUBLIC_API(bool)
JS::SetHas(JSContext* cx, HandleObject obj, HandleValue key, bool* rval)
{
return CallObjFunc(SetObject::has, cx, obj, key, rval);
}
JS_PUBLIC_API(bool)
JS::SetDelete(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, key);
return SetObject::delete_(cx, obj, key, rval);
return CallObjFunc(SetObject::delete_, cx, obj, key, rval);
}
JS_PUBLIC_API(bool)
JS::SetAdd(JSContext *cx, HandleObject obj,
HandleValue key)
JS::SetClear(JSContext* cx, HandleObject obj)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, key);
return SetObject::add(cx, obj, key);
return CallObjFunc(&SetObject::clear, cx, obj);
}
JS_PUBLIC_API(bool)
JS::SetClear(JSContext *cx, HandleObject obj)
{
CHECK_REQUEST(cx);
return SetObject::clear(cx, obj);
}
JS_PUBLIC_API(bool)
JS::SetKeys(JSContext *cx, HandleObject obj, MutableHandleValue rval)
JS::SetKeys(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
return SetValues(cx, obj, rval);
}
JS_PUBLIC_API(bool)
JS::SetValues(JSContext *cx, HandleObject obj, MutableHandleValue rval)
JS::SetValues(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, rval);
return SetObject::iterator(cx, SetObject::Values, obj, rval);
return CallObjFunc(&SetObject::iterator, cx, SetObject::Values, obj, rval);
}
JS_PUBLIC_API(bool)
JS::SetEntries(JSContext *cx, HandleObject obj, MutableHandleValue rval)
JS::SetEntries(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, rval);
return SetObject::iterator(cx, SetObject::Entries, obj, rval);
return CallObjFunc(&SetObject::iterator, cx, SetObject::Entries, obj, rval);
}
JS_PUBLIC_API(bool)
JS::SetForEach(JSContext *cx, HandleObject obj, HandleValue callbackFn, HandleValue thisVal)
{
CHECK_REQUEST(cx);
return forEach("SetForEach", cx, obj, callbackFn, thisVal);
}

View File

@ -98,10 +98,16 @@ class MapObject : public NativeObject {
static bool has(JSContext* cx, unsigned argc, Value* vp);
static MapObject* create(JSContext* cx);
// Publicly exposed Map calls for JSAPI access (webidl maplike/setlike
// interfaces, etc.)
static uint32_t size(JSContext *cx, HandleObject obj);
static bool get(JSContext *cx, HandleObject obj, HandleValue key, MutableHandleValue rval);
static bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);
static bool delete_(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);
// Set call for public JSAPI exposure. Does not actually return map object
// as stated in spec, expects caller to return a value. for instance, with
// webidl maplike/setlike, should return interface object.
static bool set(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val);
static bool clear(JSContext *cx, HandleObject obj);
static bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj, MutableHandleValue iter);
@ -151,6 +157,8 @@ class SetObject : public NativeObject {
static bool add(JSContext *cx, HandleObject obj, HandleValue key);
static bool has(JSContext *cx, unsigned argc, Value *vp);
// Publicly exposed Set calls for JSAPI access (webidl maplike/setlike
// interfaces, etc.)
static SetObject* create(JSContext *cx);
static uint32_t size(JSContext *cx, HandleObject obj);
static bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);