Bug 1325345 - Add compartment asserts to JS_FireOnNewGlobalObject and other APIs. r=jonco,bholley
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 22 Dec 2016 12:25:26 +0100
changeset 374288 ebb038350129268903209a9d4df66d0cc4ad03a7
parent 374287 84ba5ac2326637c96878318434d9fc580be92fb9
child 374289 74717635e168385c1e979141d771a74536e1422a
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, bholley
bugs1325345
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1325345 - Add compartment asserts to JS_FireOnNewGlobalObject and other APIs. r=jonco,bholley
js/src/jsapi.cpp
js/src/jsfriendapi.cpp
js/src/shell/js.cpp
js/src/vm/GlobalObject.cpp
js/xpconnect/src/Sandbox.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -156,16 +156,17 @@ ErrorTakesObjectArgument(unsigned msg)
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, HandleId id,
                                                bool strict)
 {
     static_assert(unsigned(OkCode) == unsigned(JSMSG_NOT_AN_ERROR),
                   "unsigned value of OkCode must not be an error code");
     MOZ_ASSERT(code_ != Uninitialized);
     MOZ_ASSERT(!ok());
+    assertSameCompartment(cx, obj);
 
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT);
     if (code_ == JSMSG_OBJECT_NOT_EXTENSIBLE || code_ == JSMSG_SET_NON_OBJECT_RECEIVER) {
         RootedValue val(cx, ObjectValue(*obj));
         return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val,
                                      nullptr, nullptr, nullptr);
     }
     if (ErrorTakesArguments(code_)) {
@@ -190,16 +191,17 @@ JS::ObjectOpResult::reportStrictErrorOrW
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict)
 {
     MOZ_ASSERT(code_ != Uninitialized);
     MOZ_ASSERT(!ok());
     MOZ_ASSERT(!ErrorTakesArguments(code_));
+    assertSameCompartment(cx, obj);
 
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT);
     return JS_ReportErrorFlagsAndNumberASCII(cx, flags, GetErrorMessage, nullptr, code_);
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::failCantRedefineProp()
 {
@@ -1581,16 +1583,17 @@ JS_IdToValue(JSContext* cx, jsid id, Mut
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::ToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
     MOZ_ASSERT(obj != nullptr);
     MOZ_ASSERT(hint == JSTYPE_VOID || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER);
     vp.setObject(*obj);
     return ToPrimitiveSlow(cx, hint, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS::GetFirstArgumentAsTypeHint(JSContext* cx, CallArgs args, JSType *result)
@@ -1860,16 +1863,17 @@ JS_GlobalObjectTraceHook(JSTracer* trc, 
 
 JS_PUBLIC_API(void)
 JS_FireOnNewGlobalObject(JSContext* cx, JS::HandleObject global)
 {
     // This hook is infallible, because we don't really want arbitrary script
     // to be able to throw errors during delicate global creation routines.
     // This infallibility will eat OOM and slow script, but if that happens
     // we'll likely run up into them again soon in a fallible context.
+    assertSameCompartment(cx, global);
     Rooted<js::GlobalObject*> globalObject(cx, &global->as<GlobalObject>());
     Debugger::onNewGlobalObject(cx, globalObject);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObject(JSContext* cx, const JSClass* jsclasp)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
@@ -1940,16 +1944,17 @@ JS::AssertObjectBelongsToCurrentThread(J
 }
 
 
 /*** Standard internal methods *******************************************************************/
 
 JS_PUBLIC_API(bool)
 JS_GetPrototype(JSContext* cx, HandleObject obj, MutableHandleObject result)
 {
+    assertSameCompartment(cx, obj);
     return GetPrototype(cx, obj, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
@@ -1957,43 +1962,48 @@ JS_SetPrototype(JSContext* cx, HandleObj
 
     return SetPrototype(cx, obj, proto);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary,
                           MutableHandleObject result)
 {
+    assertSameCompartment(cx, obj);
     return GetPrototypeIfOrdinary(cx, obj, isOrdinary, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_IsExtensible(JSContext* cx, HandleObject obj, bool* extensible)
 {
+    assertSameCompartment(cx, obj);
     return IsExtensible(cx, obj, extensible);
 }
 
 JS_PUBLIC_API(bool)
 JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, ObjectOpResult& result)
 {
+    assertSameCompartment(cx, obj);
     return PreventExtensions(cx, obj, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetImmutablePrototype(JSContext *cx, JS::HandleObject obj, bool *succeeded)
 {
+    assertSameCompartment(cx, obj);
     return SetImmutablePrototype(cx, obj, succeeded);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetOwnPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
                                 MutableHandle<PropertyDescriptor> desc)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id);
 
     return GetOwnPropertyDescriptor(cx, obj, id, desc);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
                             MutableHandle<PropertyDescriptor> desc)
 {
@@ -2014,16 +2024,17 @@ JS_GetOwnUCPropertyDescriptor(JSContext*
     RootedId id(cx, AtomToId(atom));
     return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
                              MutableHandle<PropertyDescriptor> desc)
 {
+    assertSameCompartment(cx, obj);
     return GetPropertyDescriptor(cx, obj, id, desc);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
                          MutableHandle<PropertyDescriptor> desc)
 {
     JSAtom* atom = Atomize(cx, name, strlen(name));
@@ -2393,16 +2404,17 @@ JS_DefineUCProperty(JSContext* cx, Handl
     return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
                             getter, setter, attrs, 0);
 }
 
 static bool
 DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
               unsigned attrs, Native getter, Native setter)
 {
+    assertSameCompartment(cx, obj, value);
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
     return DefinePropertyById(cx, obj, id, value,
                               NativeOpWrapper(getter), NativeOpWrapper(setter),
@@ -2459,16 +2471,17 @@ JS_DefineElement(JSContext* cx, HandleOb
                          attrs, getter, setter);
 }
 
 JS_PUBLIC_API(bool)
 JS_HasPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id);
 
     return HasProperty(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(bool)
 JS_HasProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
 {
     JSAtom* atom = Atomize(cx, name, strlen(name));
@@ -3198,16 +3211,19 @@ JS_DefineProperties(JSContext* cx, Handl
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectToCompletePropertyDescriptor(JSContext* cx,
                                        HandleObject obj,
                                        HandleValue descObj,
                                        MutableHandle<PropertyDescriptor> desc)
 {
+    // |obj| can be in a different compartment here. The caller is responsible
+    // for wrapping it (see JS_WrapPropertyDescriptor).
+    assertSameCompartment(cx, descObj);
     if (!ToPropertyDescriptor(cx, descObj, true, desc))
         return false;
     CompletePropertyDescriptor(desc);
     desc.object().set(obj);
     return true;
 }
 
 JS_PUBLIC_API(void)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -65,16 +65,18 @@ JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp, void* data)
 {
     cx->gc.setGrayRootsTracer(traceOp, data);
 }
 
 JS_FRIEND_API(JSObject*)
 JS_FindCompilationScope(JSContext* cx, HandleObject objArg)
 {
+    assertSameCompartment(cx, objArg);
+
     RootedObject obj(cx, objArg);
 
     /*
      * We unwrap wrappers here. This is a little weird, but it's what's being
      * asked of us.
      */
     if (obj->is<WrapperObject>())
         obj = UncheckedUnwrap(obj);
@@ -98,16 +100,17 @@ JS_FRIEND_API(bool)
 JS_SplicePrototype(JSContext* cx, HandleObject obj, HandleObject proto)
 {
     /*
      * Change the prototype of an object which hasn't been used anywhere
      * and does not share its type with another object. Unlike JS_SetPrototype,
      * does not nuke type information for the object.
      */
     CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, proto);
 
     if (!obj->isSingleton()) {
         /*
          * We can see non-singleton objects when trying to splice prototypes
          * due to mutable __proto__ (ugh).
          */
         return JS_SetPrototype(cx, obj, proto);
     }
@@ -132,16 +135,17 @@ JS_NewObjectWithUniqueType(JSContext* cx
     if (!JS_SplicePrototype(cx, obj, proto))
         return nullptr;
     return obj;
 }
 
 JS_FRIEND_API(JSObject*)
 JS_NewObjectWithoutMetadata(JSContext* cx, const JSClass* clasp, JS::Handle<JSObject*> proto)
 {
+    assertSameCompartment(cx, proto);
     AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
     return JS_NewObjectWithGivenProto(cx, clasp, proto);
 }
 
 JS_FRIEND_API(bool)
 JS_GetIsSecureContext(JSCompartment* compartment)
 {
     return compartment->creationOptions().secureContext();
@@ -305,16 +309,17 @@ js::GetBuiltinClass(JSContext* cx, Handl
         *cls = ESClass::Other;
 
     return true;
 }
 
 JS_FRIEND_API(const char*)
 js::ObjectClassName(JSContext* cx, HandleObject obj)
 {
+    assertSameCompartment(cx, obj);
     return GetObjectClassName(cx, obj);
 }
 
 JS_FRIEND_API(JS::Zone*)
 js::GetCompartmentZone(JSCompartment* comp)
 {
     return comp->zone();
 }
@@ -499,16 +504,18 @@ js::FunctionHasNativeReserved(JSObject* 
 {
     MOZ_ASSERT(fun->as<JSFunction>().isNative());
     return fun->as<JSFunction>().isExtended();
 }
 
 JS_FRIEND_API(bool)
 js::GetObjectProto(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> proto)
 {
+    assertSameCompartment(cx, obj);
+
     if (IsProxy(obj))
         return JS_GetPrototype(cx, obj, proto);
 
     proto.set(reinterpret_cast<const shadow::Object*>(obj.get())->group->proto);
     return true;
 }
 
 JS_FRIEND_API(JSObject*)
@@ -647,16 +654,18 @@ JS_FRIEND_API(void)
 JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback)
 {
     cx->setTelemetryCallback(cx, callback);
 }
 
 JS_FRIEND_API(JSObject*)
 JS_CloneObject(JSContext* cx, HandleObject obj, HandleObject protoArg)
 {
+    // |obj| might be in a different compartment.
+    assertSameCompartment(cx, protoArg);
     Rooted<TaggedProto> proto(cx, TaggedProto(protoArg.get()));
     return CloneObject(cx, obj, proto);
 }
 
 #ifdef DEBUG
 
 JS_FRIEND_API(void)
 js::DumpString(JSString* str, FILE* fp)
@@ -1333,16 +1342,17 @@ js::GetAllocationMetadata(JSObject* obj)
     if (map)
         return map->lookup(obj);
     return nullptr;
 }
 
 JS_FRIEND_API(bool)
 js::ReportIsNotFunction(JSContext* cx, HandleValue v)
 {
+    assertSameCompartment(cx, v);
     return ReportIsNotFunction(cx, v, -1);
 }
 
 #ifdef DEBUG
 bool
 js::HasObjectMovedOp(JSObject* obj) {
     return !!GetObjectClass(obj)->extObjectMovedOp();
 }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3170,19 +3170,19 @@ NewSandbox(JSContext* cx, bool lazy)
     {
         JSAutoCompartment ac(cx, obj);
         if (!lazy && !JS_InitStandardClasses(cx, obj))
             return nullptr;
 
         RootedValue value(cx, BooleanValue(lazy));
         if (!JS_SetProperty(cx, obj, "lazy", value))
             return nullptr;
-    }
-
-    JS_FireOnNewGlobalObject(cx, obj);
+
+        JS_FireOnNewGlobalObject(cx, obj);
+    }
 
     if (!cx->compartment()->wrap(cx, &obj))
         return nullptr;
     return obj;
 }
 
 static bool
 EvalInContext(JSContext* cx, unsigned argc, Value* vp)
@@ -7062,19 +7062,19 @@ NewGlobalObject(JSContext* cx, JS::Compa
 
         RootedObject domProto(cx, JS_InitClass(cx, glob, nullptr, &dom_class, dom_constructor,
                                                0, dom_props, dom_methods, nullptr, nullptr));
         if (!domProto)
             return nullptr;
 
         /* Initialize FakeDOMObject.prototype */
         InitDOMObject(domProto);
-    }
-
-    JS_FireOnNewGlobalObject(cx, glob);
+
+        JS_FireOnNewGlobalObject(cx, glob);
+    }
 
     return glob;
 }
 
 static bool
 BindScriptArgs(JSContext* cx, OptionParser* op)
 {
     AutoReportException are(cx);
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -367,21 +367,21 @@ GlobalObject::new_(JSContext* cx, const 
     }
 
     Rooted<GlobalObject*> global(cx);
     {
         AutoCompartment ac(cx, compartment);
         global = GlobalObject::createInternal(cx, clasp);
         if (!global)
             return nullptr;
+
+        if (hookOption == JS::FireOnNewGlobalHook)
+            JS_FireOnNewGlobalObject(cx, global);
     }
 
-    if (hookOption == JS::FireOnNewGlobalHook)
-        JS_FireOnNewGlobalObject(cx, global);
-
     return global;
 }
 
 LexicalEnvironmentObject&
 GlobalObject::lexicalEnvironment() const
 {
     return getReservedSlot(LEXICAL_ENVIRONMENT).toObject().as<LexicalEnvironmentObject>();
 }
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1216,16 +1216,17 @@ xpc::CreateSandboxObject(JSContext* cx, 
         return NS_ERROR_UNEXPECTED;
 
     // Set the location information for the new global, so that tools like
     // about:memory may use that information
     xpc::SetLocationForGlobal(sandbox, options.sandboxName);
 
     xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
 
+    JSAutoCompartment ac(cx, sandbox);
     JS_FireOnNewGlobalObject(cx, sandbox);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
                                     JSObject* objArg, const CallArgs& args, bool* _retval)