Ensure that accesses to implicit XPCNativeWrappers are actually legal. bug 384750, r+sr=jst
authormrbkap@gmail.com
Thu, 05 Jul 2007 13:01:12 -0700
changeset 3162 589dd4564f338f924c42703e39e001d96627abe6
parent 3161 024c13a97e3a8c793f527bad445b61b03b3aaf14
child 3163 c46781038ecbd3e6fb54908b0ff1cd629ad55526
push idunknown
push userunknown
push dateunknown
bugs384750
milestone1.9a7pre
Ensure that accesses to implicit XPCNativeWrappers are actually legal. bug 384750, r+sr=jst
js/src/xpconnect/src/XPCNativeWrapper.cpp
js/src/xpconnect/src/nsXPConnect.cpp
--- a/js/src/xpconnect/src/XPCNativeWrapper.cpp
+++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp
@@ -211,16 +211,42 @@ static inline
 JSBool
 ThrowException(nsresult ex, JSContext *cx)
 {
   XPCThrower::Throw(ex, cx);
 
   return JS_FALSE;
 }
 
+static inline
+JSBool
+EnsureLegalActivity(JSContext *cx, JSObject *obj)
+{
+  jsval flags;
+
+  ::JS_GetReservedSlot(cx, obj, 0, &flags);
+  if (HAS_FLAGS(flags, FLAG_EXPLICIT)) {
+    // Can't make any assertions about the owner of this wrapper.
+    return JS_TRUE;
+  }
+
+  JSStackFrame *frame = nsnull;
+  uint32 fileFlags = JS_GetTopScriptFilenameFlags(cx, NULL);
+  if (!JS_FrameIterator(cx, &frame) ||
+      fileFlags == JSFILENAME_NULL ||
+      (fileFlags & JSFILENAME_SYSTEM)) {
+    // We expect implicit native wrappers in system files.
+    return JS_TRUE;
+  }
+
+  // Otherwise, we're looking at a non-system file with a handle on an
+  // implcit wrapper. This is a bug! Deny access.
+  return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx);
+}
+
 static JSBool
 WrapFunction(JSContext* cx, JSObject* funobj, jsval *rval)
 {
   // If funobj is already a wrapped function, just return it.
   if (JS_GetFunctionNative(cx,
                            JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj))) ==
       XPC_NW_FunctionWrapper) {
     *rval = OBJECT_TO_JSVAL(funobj);
@@ -260,22 +286,27 @@ XPC_NW_AddProperty(JSContext *cx, JSObje
   jsval flags;
   ::JS_GetReservedSlot(cx, obj, 0, &flags);
   if (!HAS_FLAGS(flags, FLAG_RESOLVING)) {
     return JS_TRUE;
   }
 
   // Note: no need to protect *vp from GC here, since it's already in the slot
   // on |obj|.
-  return RewrapIfDeepWrapper(cx, obj, *vp, vp);
+  return EnsureLegalActivity(cx, obj) &&
+         RewrapIfDeepWrapper(cx, obj, *vp, vp);
 }
 
 JS_STATIC_DLL_CALLBACK(JSBool)
 XPC_NW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
+  if (!EnsureLegalActivity(cx, obj)) {
+    return JS_FALSE;
+  }
+
   XPC_NW_BYPASS_BASE(cx, obj,
     // We're being notified of a delete operation on id in this
     // XPCNativeWrapper, so forward to the right high-level hook,
     // OBJ_DELETE_PROPERTY, on the XPCWrappedNative's object.
     {
       jsid interned_id;
 
       if (!::JS_ValueToId(cx, id, &interned_id)) {
@@ -409,16 +440,20 @@ XPC_NW_GetOrSetProperty(JSContext *cx, J
 
   while (!XPCNativeWrapper::IsNativeWrapper(cx, obj)) {
     obj = ::JS_GetPrototype(cx, obj);
     if (!obj) {
       return ThrowException(NS_ERROR_UNEXPECTED, cx);
     }
   }
 
+  if (!EnsureLegalActivity(cx, obj)) {
+    return JS_FALSE;
+  }
+
   XPCWrappedNative *wrappedNative =
     XPCNativeWrapper::GetWrappedNative(cx, obj);
 
   if (!wrappedNative) {
     return ThrowException(NS_ERROR_INVALID_ARG, cx);
   }
 
   JSObject *nativeObj = wrappedNative->GetFlatJSObject();
@@ -618,16 +653,20 @@ JS_STATIC_DLL_CALLBACK(JSBool)
 XPC_NW_Enumerate(JSContext *cx, JSObject *obj)
 {
   // We are being notified of a for-in loop or similar operation on this
   // XPCNativeWrapper, so forward to the correct high-level object hook,
   // OBJ_ENUMERATE on the XPCWrappedNative's object, called via the
   // JS_Enumerate API.  Then reflect properties named by the enumerated
   // identifiers from the wrapped native to the native wrapper.
 
+  if (!EnsureLegalActivity(cx, obj)) {
+    return JS_FALSE;
+  }
+
   XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(cx, obj);
   if (!wn) {
     return JS_TRUE;
   }
 
   JSIdArray *ida = JS_Enumerate(cx, wn->GetFlatJSObject());
   if (!ida) {
     return JS_FALSE;
@@ -677,16 +716,20 @@ XPC_NW_NewResolve(JSContext *cx, JSObjec
   // couldn't get at those values anyway.  Also, we always deal with
   // wrappedJSObject and toString before looking at our scriptable hooks, so no
   // need to mess with our flags yet.
   if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) ||
       id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) {
     return JS_TRUE;
   }
 
+  if (!EnsureLegalActivity(cx, obj)) {
+    return JS_FALSE;
+  }
+
   // We can't use XPC_NW_BYPASS here, because we need to do a full
   // OBJ_LOOKUP_PROPERTY on the wrapped native's object, in order to
   // trigger reflection along the wrapped native prototype chain.
   // All we need to do is define the property in obj if it exists in
   // the wrapped native's object.
 
   if (ShouldBypassNativeWrapper(cx, obj)) {
     XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(cx, obj);
@@ -888,18 +931,21 @@ XPC_NW_NewResolve(JSContext *cx, JSObjec
   *objp = obj;
 
   return JS_TRUE;
 }
 
 JS_STATIC_DLL_CALLBACK(JSBool)
 XPC_NW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
 {
+  if (!EnsureLegalActivity(cx, obj)) {
+    return JS_FALSE;
+  }
+
   XPC_NW_BYPASS(cx, obj, convert, (cx, obj, type, vp));
-
   return JS_TRUE;
 }
 
 JS_STATIC_DLL_CALLBACK(void)
 XPC_NW_Finalize(JSContext *cx, JSObject *obj)
 {
   // We must not use obj's private data here since it's likely that it
   // has already been finalized.
@@ -1251,16 +1297,20 @@ XPC_NW_toString(JSContext *cx, JSObject 
 {
   while (!XPCNativeWrapper::IsNativeWrapper(cx, obj)) {
     obj = ::JS_GetPrototype(cx, obj);
     if (!obj) {
       return ThrowException(NS_ERROR_UNEXPECTED, cx);
     }
   }
 
+  if (!EnsureLegalActivity(cx, obj)) {
+    return JS_FALSE;
+  }
+
   // Check whether toString was overridden in any object along
   // the wrapped native's object's prototype chain.
   XPCJSRuntime *rt = nsXPConnect::GetRuntime();
   if (!rt)
     return JS_FALSE;
 
   jsid id = rt->GetStringID(XPCJSRuntime::IDX_TO_STRING);
   jsval idAsVal;
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -831,23 +831,41 @@ nsXPConnect::Traverse(void *p, nsCycleCo
 // nsIXPConnect interface methods...
 
 inline nsresult UnexpectedFailure(nsresult rv)
 {
     NS_ERROR("This is not supposed to fail!");
     return rv;
 }
 
+class SaveFrame
+{
+public:
+    SaveFrame(JSContext *cx)
+        : mJSContext(cx) {
+        mFrame = JS_SaveFrameChain(mJSContext);
+    }
+
+    ~SaveFrame() {
+        JS_RestoreFrameChain(mJSContext, mFrame);
+    }
+
+private:
+    JSContext *mJSContext;
+    JSStackFrame *mFrame;
+};
+
 /* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */
 NS_IMETHODIMP
 nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj)
 {
     NS_ASSERTION(aJSContext, "bad param");
     NS_ASSERTION(aGlobalJSObj, "bad param");
 
+    SaveFrame sf(aJSContext);
     XPCCallContext ccx(NATIVE_CALLER, aJSContext);
     if(!ccx.IsValid())
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     if(!xpc_InitJSxIDClassObjects())
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     if(!xpc_InitWrappedNativeJSOps())
@@ -975,16 +993,17 @@ nsXPConnect::InitClassesWithNewWrappedGl
                                     globalJSObj;
     if(protoJSObject)
     {
         if(protoJSObject != globalJSObj)
             JS_SetParent(aJSContext, protoJSObject, globalJSObj);
         JS_SetPrototype(aJSContext, protoJSObject, scope->GetPrototypeJSObject());
     }
 
+    SaveFrame sf(ccx);
     if(!nsXPCComponents::AttachNewComponentsObject(ccx, scope, globalJSObj))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     if (!XPCNativeWrapper::AttachNewConstructorObject(ccx, globalJSObj))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     if (!XPC_SJOW_AttachNewConstructorObject(ccx, globalJSObj))
         return UnexpectedFailure(NS_ERROR_FAILURE);