Don't wrap everything that comes out of a wrapped function, if that function is same-origin. bug 390946, r+sr+a=jst
authormrbkap@gmail.com
Wed, 15 Aug 2007 14:09:27 -0700
changeset 4687 66450e210584986bd2e1a97e93596fb91e11e684
parent 4686 63d6d60758296328de3fe31f40579fa4071a3174
child 4688 0755bf96b21095577809147b6fe84f633a2beaf3
push idunknown
push userunknown
push dateunknown
bugs390946
milestone1.9a8pre
Don't wrap everything that comes out of a wrapped function, if that function is same-origin. bug 390946, r+sr+a=jst
js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
js/src/xpconnect/src/XPCWrapper.cpp
--- a/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
+++ b/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
@@ -260,20 +260,23 @@ IsWrapperSameOrigin(JSContext *cx, JSObj
     return NS_OK;
   }
 
   // Now, we have our two principals, compare them!
   return ssm->CheckSameOriginPrincipal(subjectPrin, objectPrin);
 }
 
 static JSBool
+WrapSameOriginProp(JSContext *cx, JSObject *outerObj, jsval *vp);
+
+static JSBool
 XPC_XOW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                         jsval *rval)
 {
-  JSObject *wrappedObj;
+  JSObject *wrappedObj, *outerObj = obj;
 
   obj = GetWrapper(cx, obj);
   if (!obj || (wrappedObj = GetWrappedObject(cx, obj)) == nsnull) {
     return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
   }
 
   JSObject *funObj = JSVAL_TO_OBJECT(argv[-2]);
   jsval funToCall;
@@ -281,52 +284,83 @@ XPC_XOW_FunctionWrapper(JSContext *cx, J
     return JS_FALSE;
   }
 
   JSFunction *fun = JS_ValueToFunction(cx, funToCall);
   if (!fun) {
     return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
   }
 
+  nsresult rv = IsWrapperSameOrigin(cx, JSVAL_TO_OBJECT(funToCall));
+  if (NS_FAILED(rv) && rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) {
+    return ThrowException(rv, cx);
+  }
+
   JSNative native = JS_GetFunctionNative(cx, fun);
   NS_ASSERTION(native, "How'd we get here with a scripted function?");
 
   // A trick! Calling the native directly doesn't push the native onto the
   // JS stack, so interested onlookers will only see us, meaning that they
   // will compute *our* subject principal.
 
   argv[-2] = funToCall;
   argv[-1] = OBJECT_TO_JSVAL(wrappedObj);
   if (!native(cx, wrappedObj, argc, argv, rval)) {
     return JS_FALSE;
   }
 
+  if (NS_SUCCEEDED(rv)) {
+    return WrapSameOriginProp(cx, outerObj, rval);
+  }
+
   return XPC_XOW_RewrapIfNeeded(cx, obj, rval);
 }
 
+static JSBool
+WrapSameOriginProp(JSContext *cx, JSObject *outerObj, jsval *vp)
+{
+  // Don't call XPC_XOW_RewrapIfNeeded for same origin properties. We only
+  // need to wrap window, document and location.
+  if (JSVAL_IS_PRIMITIVE(*vp)) {
+    return JS_TRUE;
+  }
+
+  JSObject *wrappedObj = JSVAL_TO_OBJECT(*vp);
+  const char *name = JS_GET_CLASS(cx, wrappedObj)->name;
+  if (XPC_XOW_ClassNeedsXOW(name)) {
+    return XPC_XOW_WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp);
+  }
+
+  if (JS_ObjectIsFunction(cx, wrappedObj) &&
+      JS_GetFunctionNative(cx, reinterpret_cast<JSFunction *>
+                                               (JS_GetPrivate(cx, wrappedObj))) ==
+      XPCWrapper::sEvalNative) {
+    return XPC_XOW_WrapFunction(cx, outerObj, wrappedObj, vp);
+  }
+
+  return JS_TRUE;
+}
+
 JSBool
 XPC_XOW_WrapFunction(JSContext *cx, JSObject *outerObj, JSObject *funobj,
                      jsval *rval)
 {
   jsval funobjVal = OBJECT_TO_JSVAL(funobj);
-  JSNative native = JS_GetFunctionNative(cx, JS_ValueToFunction(cx, funobjVal));
+  JSFunction *wrappedFun = reinterpret_cast<JSFunction *>(JS_GetPrivate(cx, funobj));
+  JSNative native = JS_GetFunctionNative(cx, wrappedFun);
   if (!native || native == XPC_XOW_FunctionWrapper) {
     *rval = funobjVal;
     return JS_TRUE;
   }
 
-  JSFunction *wrappedFun = JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj));
-  NS_ASSERTION(wrappedFun, "We were told this was a function");
-
   JSFunction *funWrapper =
     JS_NewFunction(cx, XPC_XOW_FunctionWrapper,
                    JS_GetFunctionArity(wrappedFun), 0,
                    JS_GetGlobalForObject(cx, outerObj),
-                   "Wrapped function");
-                   // XXX JS_GetFunctionName(wrappedFun));
+                   JS_GetFunctionName(wrappedFun));
   if (!funWrapper) {
     return JS_FALSE;
   }
 
   JSObject *funWrapperObj = JS_GetFunctionObject(funWrapper);
   if (!JS_SetReservedSlot(cx, funWrapperObj, 0, funobjVal)) {
     return JS_FALSE;
   }
@@ -361,16 +395,17 @@ XPC_XOW_RewrapIfNeeded(JSContext *cx, JS
 JSBool
 XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp)
 {
   // Our argument should be a wrapped native object.
   JSObject *wrappedObj;
   XPCWrappedNative *wn;
   if (!JSVAL_IS_OBJECT(*vp) ||
       !(wrappedObj = JSVAL_TO_OBJECT(*vp)) ||
+      JS_GET_CLASS(cx, wrappedObj) == &sXPC_XOW_JSClass.base ||
       !(wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj))) {
     return JS_TRUE;
   }
 
   XPCJSRuntime *rt = nsXPConnect::GetRuntime();
   XPCCallContext ccx(NATIVE_CALLER, cx);
   NS_ENSURE_TRUE(ccx.IsValid(), JS_FALSE);
 
@@ -510,16 +545,27 @@ XPC_XOW_DelProperty(JSContext *cx, JSObj
 static JSBool
 XPC_XOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp,
                          JSBool isSet)
 {
   if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) {
     return JS_TRUE;
   }
 
+  // Don't do anything if we already resolved to a wrapped function in
+  // NewResolve. In practice, this means that this is a wrapped eval
+  // function.
+  jsval v = *vp;
+  if (!JSVAL_IS_PRIMITIVE(v) &&
+      JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v)) &&
+      JS_GetFunctionNative(cx, JS_ValueToFunction(cx, v)) ==
+      XPC_XOW_FunctionWrapper) {
+    return JS_TRUE;
+  }
+
   XPCCallContext ccx(JS_CALLER, cx);
   if (!ccx.IsValid()) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
   AUTO_MARK_JSVAL(ccx, vp);
 
   JSObject *wrappedObj = GetWrappedObject(cx, obj);
@@ -604,35 +650,17 @@ XPC_XOW_GetOrSetProperty(JSContext *cx, 
       if (proto == wrappedObj) {
         JS_SetPrototype(cx, wrappedObj, oldProto);
         JS_ReportError(cx, "cyclic __proto__ value");
         return JS_FALSE;
       }
     }
   }
 
-  // Don't call XPC_XOW_RewrapIfNeeded for same origin properties. We only
-  // need to wrap window, document and location.
-  if (JSVAL_IS_PRIMITIVE(*vp)) {
-    return JS_TRUE;
-  }
-
-  wrappedObj = JSVAL_TO_OBJECT(*vp);
-  if (JS_ObjectIsFunction(cx, wrappedObj) &&
-      JS_GetFunctionNative(cx, JS_ValueToFunction(cx, *vp)) ==
-      XPCWrapper::sEvalNative) {
-    return XPC_XOW_WrapFunction(cx, obj, wrappedObj, vp);
-  }
-
-  const char *name = JS_GET_CLASS(cx, wrappedObj)->name;
-  if (XPC_XOW_ClassNeedsXOW(name)) {
-    return XPC_XOW_WrapObject(cx, JS_GetGlobalForObject(cx, obj), vp);
-  }
-
-  return JS_TRUE;
+  return WrapSameOriginProp(cx, obj, vp);
 }
 
 JS_STATIC_DLL_CALLBACK(JSBool)
 XPC_XOW_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
   return XPC_XOW_GetOrSetProperty(cx, obj, id, vp, JS_FALSE);
 }
 
--- a/js/src/xpconnect/src/XPCWrapper.cpp
+++ b/js/src/xpconnect/src/XPCWrapper.cpp
@@ -135,68 +135,66 @@ XPCWrapper::Enumerate(JSContext *cx, JSO
 }
 
 // static
 JSBool
 XPCWrapper::NewResolve(JSContext *cx, JSObject *wrapperObj,
                        JSObject *innerObj, jsval id, uintN flags,
                        JSObject **objp, JSBool preserveVal)
 {
-  jschar *chars = nsnull;
-  size_t length;
-  JSBool hasProp, ok;
   jsval v = JSVAL_VOID;
 
-  if (JSVAL_IS_STRING(id)) {
-    JSString *str = JSVAL_TO_STRING(id);
-
-    chars = ::JS_GetStringChars(str);
-    length = ::JS_GetStringLength(str);
+  jsid interned_id;
+  if (!::JS_ValueToId(cx, id, &interned_id)) {
+    return JS_FALSE;
+  }
 
-    ok = ::JS_HasUCProperty(cx, innerObj, chars, length, &hasProp);
-    if (preserveVal && ok && hasProp) {
-      ok = ::JS_LookupUCProperty(cx, innerObj, chars, length, &v);
-    }
-  } else if (JSVAL_IS_INT(id)) {
-    ok = ::JS_HasElement(cx, innerObj, JSVAL_TO_INT(id), &hasProp);
-    if (preserveVal && ok && hasProp) {
-      ok = ::JS_LookupElement(cx, innerObj, JSVAL_TO_INT(id), &v);
-    }
-  } else {
-    // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=381662
-    // A non-string and non-int id is being resolved. We don't deal
-    // with those yet, return early.
+  JSProperty *prop;
+  JSObject *innerObjp;
+  if (!OBJ_LOOKUP_PROPERTY(cx, innerObj, interned_id, &innerObjp, &prop)) {
+    return JS_FALSE;
+  }
 
-    return ThrowException(NS_ERROR_INVALID_ARG, cx);
+  if (!prop) {
+    // Nothing to define.
+    return JS_TRUE;
   }
 
-  if (!ok || !hasProp) {
-    // An error occured, or the property was not found. Return
-    // early. This is safe even in the case of a set operation since
-    // if the property doesn't exist there's no chance of a setter
-    // being called or any other code being run as a result of the
-    // set.
+  JSBool isXOW = (JS_GET_CLASS(cx, wrapperObj) == &sXPC_XOW_JSClass.base);
+  if (preserveVal || isXOW) {
+    JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(prop);
+    if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(innerObjp))) {
+      v = OBJ_GET_SLOT(cx, innerObjp, sprop->slot);
+    }
+  }
+
+  OBJ_DROP_PROPERTY(cx, innerObjp, prop);
 
-    return ok;
+  // Hack alert: we only do this for same-origin calls on XOWs: we want
+  // to preserve 'eval' function wrapper on the wrapper object itself
+  // to preserve eval's identity.
+  if (!preserveVal && isXOW && !JSVAL_IS_PRIMITIVE(v)) {
+    JSObject *obj = JSVAL_TO_OBJECT(v);
+    if (JS_ObjectIsFunction(cx, obj)) {
+      JSFunction *fun = reinterpret_cast<JSFunction *>(JS_GetPrivate(cx, obj));
+      if (JS_GetFunctionNative(cx, fun) == sEvalNative &&
+          !WrapFunction(cx, wrapperObj, obj, &v, JS_FALSE)) {
+        return JS_FALSE;
+      }
+    }
   }
 
   jsval oldSlotVal;
   if (!::JS_GetReservedSlot(cx, wrapperObj, sResolvingSlot, &oldSlotVal) ||
-      !::JS_SetReservedSlot(cx, wrapperObj, sResolvingSlot,
-                            BOOLEAN_TO_JSVAL(JS_TRUE))) {
+      !::JS_SetReservedSlot(cx, wrapperObj, sResolvingSlot, JSVAL_TRUE)) {
     return JS_FALSE;
   }
 
-  if (chars) {
-    ok = ::JS_DefineUCProperty(cx, wrapperObj, chars, length, v,
-                               nsnull, nsnull, JSPROP_ENUMERATE);
-  } else {
-    ok = ::JS_DefineElement(cx, wrapperObj, JSVAL_TO_INT(id), v,
-                            nsnull, nsnull, JSPROP_ENUMERATE);
-  }
+  JSBool ok = OBJ_DEFINE_PROPERTY(cx, wrapperObj, interned_id, v, nsnull,
+                                  nsnull, JSPROP_ENUMERATE, nsnull);
 
   if (ok && (ok = ::JS_SetReservedSlot(cx, wrapperObj, sResolvingSlot,
                                        oldSlotVal))) {
     *objp = wrapperObj;
   }
 
   return ok;
 }