Title: [196494] trunk/Source
Revision
196494
Author
[email protected]
Date
2016-02-12 12:20:41 -0800 (Fri, 12 Feb 2016)

Log Message

Separate out !allowsAccess path in JSDOMWindowCustom getOwnPropertySlot
https://bugs.webkit.org/show_bug.cgi?id=154156

Reviewed by Chris Dumez.

Source/_javascript_Core:

* runtime/CommonIdentifiers.h:
    - added new property names, needed by jsDOMWindowGetOwnPropertySlotDisallowAccess.

Source/WebCore:

JSDOMWindowCustom getOwnPropertySlot currently allows cross-origin access to all
static properties, relying on the property to perform the access check. This is
a little insecure, since it is error prone - someone could easily add a property
to the static table without realizing it would be automatcially exposed.

Instead, add a hard-coded filter to restrict access. As a future implementation
we might consider autogenerating this (the properties are already tagged in IDL,
we might be able to track this in a flag on the static table).

By separating out the handling of the same- and cross-origin access we can
simplify & make the policy being enforced much clearer.

* bindings/js/JSDOMBinding.cpp:
(WebCore::objectToStringFunctionGetter): Deleted.
    - removed objectToStringFunctionGetter - this duplicated functionality of
      nonCachingStaticFunctionGetter.
* bindings/js/JSDOMBinding.h:
(WebCore::objectToStringFunctionGetter): Deleted.
    - removed objectToStringFunctionGetter - this duplicated functionality of
      nonCachingStaticFunctionGetter.
* bindings/js/JSDOMWindowCustom.cpp:
(WebCore::jsDOMWindowGetOwnPropertySlotDisallowAccess):
    - explicitly handle providing access to only the things we do want to allow cross-origin.
(WebCore::JSDOMWindow::getOwnPropertySlot):
(WebCore::JSDOMWindow::getOwnPropertySlotByIndex):
    - push all !allowsAccess handling to jsDOMWindowGetOwnPropertySlotDisallowAccess
(WebCore::childFrameGetter): Deleted.
    - this was just a deoptimiztion - moving access into a callback saved very
      little & caused more work to be duplicated.

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (196493 => 196494)


--- trunk/Source/_javascript_Core/ChangeLog	2016-02-12 20:17:03 UTC (rev 196493)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-02-12 20:20:41 UTC (rev 196494)
@@ -1,3 +1,13 @@
+2016-02-12  Gavin Barraclough  <[email protected]>
+
+        Separate out !allowsAccess path in JSDOMWindowCustom getOwnPropertySlot
+        https://bugs.webkit.org/show_bug.cgi?id=154156
+
+        Reviewed by Chris Dumez.
+
+        * runtime/CommonIdentifiers.h:
+            - added new property names, needed by jsDOMWindowGetOwnPropertySlotDisallowAccess.
+
 2016-02-12  Sukolsak Sakshuwong  <[email protected]>
 
         Update ICU header files to version 52

Modified: trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h (196493 => 196494)


--- trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h	2016-02-12 20:17:03 UTC (rev 196493)
+++ trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h	2016-02-12 20:20:41 UTC (rev 196494)
@@ -121,6 +121,7 @@
     macro(forEach) \
     macro(formatMatcher) \
     macro(forward) \
+    macro(frames) \
     macro(from) \
     macro(fromCharCode) \
     macro(get) \
@@ -154,6 +155,7 @@
     macro(line) \
     macro(locale) \
     macro(localeMatcher) \
+    macro(location) \
     macro(message) \
     macro(minute) \
     macro(month) \
@@ -161,16 +163,18 @@
     macro(name) \
     macro(next) \
     macro(now) \
-    macro(numberingSystem) \
     macro(numInlinedCalls) \
     macro(numInlinedGetByIds) \
     macro(numInlinedPutByIds) \
+    macro(numberingSystem) \
     macro(numeric) \
     macro(of) \
     macro(opcode) \
+    macro(opener) \
     macro(origin) \
     macro(osrExitSites) \
     macro(osrExits) \
+    macro(parent) \
     macro(parse) \
     macro(parseInt) \
     macro(postMessage) \
@@ -182,14 +186,15 @@
     macro(replace) \
     macro(resolve) \
     macro(second) \
+    macro(self) \
     macro(sensitivity) \
     macro(set) \
     macro(showModalDialog) \
     macro(size) \
     macro(slice) \
     macro(source) \
+    macro(sourceCode) \
     macro(sourceURL) \
-    macro(sourceCode) \
     macro(stack) \
     macro(subarray) \
     macro(target) \
@@ -204,6 +209,7 @@
     macro(toLocaleString) \
     macro(toPrecision) \
     macro(toString) \
+    macro(top) \
     macro(usage) \
     macro(value) \
     macro(valueOf) \

Modified: trunk/Source/WebCore/ChangeLog (196493 => 196494)


--- trunk/Source/WebCore/ChangeLog	2016-02-12 20:17:03 UTC (rev 196493)
+++ trunk/Source/WebCore/ChangeLog	2016-02-12 20:20:41 UTC (rev 196494)
@@ -1,3 +1,40 @@
+2016-02-12  Gavin Barraclough  <[email protected]>
+
+        Separate out !allowsAccess path in JSDOMWindowCustom getOwnPropertySlot
+        https://bugs.webkit.org/show_bug.cgi?id=154156
+
+        Reviewed by Chris Dumez.
+
+        JSDOMWindowCustom getOwnPropertySlot currently allows cross-origin access to all
+        static properties, relying on the property to perform the access check. This is
+        a little insecure, since it is error prone - someone could easily add a property
+        to the static table without realizing it would be automatcially exposed.
+
+        Instead, add a hard-coded filter to restrict access. As a future implementation
+        we might consider autogenerating this (the properties are already tagged in IDL,
+        we might be able to track this in a flag on the static table).
+
+        By separating out the handling of the same- and cross-origin access we can
+        simplify & make the policy being enforced much clearer.
+
+        * bindings/js/JSDOMBinding.cpp:
+        (WebCore::objectToStringFunctionGetter): Deleted.
+            - removed objectToStringFunctionGetter - this duplicated functionality of
+              nonCachingStaticFunctionGetter.
+        * bindings/js/JSDOMBinding.h:
+        (WebCore::objectToStringFunctionGetter): Deleted.
+            - removed objectToStringFunctionGetter - this duplicated functionality of
+              nonCachingStaticFunctionGetter.
+        * bindings/js/JSDOMWindowCustom.cpp:
+        (WebCore::jsDOMWindowGetOwnPropertySlotDisallowAccess):
+            - explicitly handle providing access to only the things we do want to allow cross-origin.
+        (WebCore::JSDOMWindow::getOwnPropertySlot):
+        (WebCore::JSDOMWindow::getOwnPropertySlotByIndex):
+            - push all !allowsAccess handling to jsDOMWindowGetOwnPropertySlotDisallowAccess
+        (WebCore::childFrameGetter): Deleted.
+            - this was just a deoptimiztion - moving access into a callback saved very
+              little & caused more work to be duplicated.
+
 2016-02-12  Sukolsak Sakshuwong  <[email protected]>
 
         Update ICU header files to version 52

Modified: trunk/Source/WebCore/bindings/js/JSDOMBinding.cpp (196493 => 196494)


--- trunk/Source/WebCore/bindings/js/JSDOMBinding.cpp	2016-02-12 20:17:03 UTC (rev 196493)
+++ trunk/Source/WebCore/bindings/js/JSDOMBinding.cpp	2016-02-12 20:20:41 UTC (rev 196494)
@@ -334,11 +334,6 @@
     frame->document()->domWindow()->printErrorMessage(message);
 }
 
-EncodedJSValue objectToStringFunctionGetter(ExecState* exec, EncodedJSValue, PropertyName propertyName)
-{
-    return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 0, propertyName.publicName(), objectProtoFuncToString));
-}
-
 Structure* getCachedDOMStructure(JSDOMGlobalObject& globalObject, const ClassInfo* classInfo)
 {
     JSDOMStructureMap& structures = globalObject.structures();

Modified: trunk/Source/WebCore/bindings/js/JSDOMBinding.h (196493 => 196494)


--- trunk/Source/WebCore/bindings/js/JSDOMBinding.h	2016-02-12 20:17:03 UTC (rev 196493)
+++ trunk/Source/WebCore/bindings/js/JSDOMBinding.h	2016-02-12 20:20:41 UTC (rev 196494)
@@ -661,7 +661,6 @@
 bool shouldAllowAccessToDOMWindow(JSC::ExecState*, DOMWindow&, String& message);
 
 void printErrorMessageForFrame(Frame*, const String& message);
-JSC::EncodedJSValue objectToStringFunctionGetter(JSC::ExecState*, JSC::EncodedJSValue, JSC::PropertyName);
 
 inline String propertyNameToString(JSC::PropertyName propertyName)
 {

Modified: trunk/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp (196493 => 196494)


--- trunk/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp	2016-02-12 20:17:03 UTC (rev 196493)
+++ trunk/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp	2016-02-12 20:20:41 UTC (rev 196494)
@@ -66,11 +66,6 @@
         visitor.addOpaqueRoot(frame);
 }
 
-static EncodedJSValue childFrameGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName)
-{
-    return JSValue::encode(toJS(exec, jsCast<JSDOMWindow*>(JSValue::decode(thisValue))->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))->document()->domWindow()));
-}
-
 static EncodedJSValue namedItemGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName)
 {
     JSDOMWindowBase* thisObj = jsCast<JSDOMWindow*>(JSValue::decode(thisValue));
@@ -117,6 +112,107 @@
 }
 #endif
 
+static bool jsDOMWindowGetOwnPropertySlotCrossOrigin(JSDOMWindow* thisObject, ExecState* exec, PropertyName propertyName, PropertySlot& slot, String& errorMessage)
+{
+    // These are the functions we allow access to cross-origin (DoNotCheckSecurity in IDL).
+    // Always provide the original function, on a fresh uncached function object.
+    if (propertyName == exec->propertyNames().blur) {
+        slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>);
+        return true;
+    }
+    if (propertyName == exec->propertyNames().close) {
+        slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
+        return true;
+    }
+    if (propertyName == exec->propertyNames().focus) {
+        slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>);
+        return true;
+    }
+    if (propertyName == exec->propertyNames().postMessage) {
+        slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>);
+        return true;
+    }
+
+    // Allow access to toString() cross-domain, but always Object.prototype.toString.
+    if (propertyName == exec->propertyNames().toString) {
+        slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<objectProtoFuncToString, 0>);
+        return true;
+    }
+
+    // When accessing cross-origin known Window properties, we always use the original property getter,
+    // even if the property was removed / redefined. As of early 2016, this matches Firefox and Chrome's
+    // behavior.
+    if (auto* entry = JSDOMWindow::info()->staticPropHashTable->entry(propertyName)) {
+        // Only allow access to these specific properties.
+        if (propertyName == exec->propertyNames().location
+            || propertyName == exec->propertyNames().closed
+            || propertyName == exec->propertyNames().length
+            || propertyName == exec->propertyNames().self
+            || propertyName == exec->propertyNames().window
+            || propertyName == exec->propertyNames().frames
+            || propertyName == exec->propertyNames().opener
+            || propertyName == exec->propertyNames().parent
+            || propertyName == exec->propertyNames().top) {
+            slot.setCacheableCustom(thisObject, ReadOnly | DontDelete | DontEnum, entry->propertyGetter());
+            return true;
+        }
+
+        // For any other entries in the static property table, deny access. (Early return also prevents
+        // named getter from returning frames with matching names - this seems a little questionable, see
+        // FIXME comment on prototype search below.)
+        thisObject->printErrorMessage(errorMessage);
+        slot.setUndefined();
+        return true;
+    }
+
+    // Do prototype lookup early so that functions and attributes in the prototype can have
+    // precedence over the index and name getters.
+    // FIXME: This seems like a silly idea. It only serves to suppress named property access
+    // to frames that happen to have names corresponding to properties on the prototype.
+    // This seems to only serve to leak some information cross-origin.
+    JSValue proto = thisObject->prototype();
+    if (proto.isObject() && asObject(proto)->getPropertySlot(exec, propertyName, slot)) {
+        thisObject->printErrorMessage(errorMessage);
+        slot.setUndefined();
+        return true;
+    }
+
+#if ENABLE(USER_MESSAGE_HANDLERS)
+    if (propertyName == exec->propertyNames().webkit && thisObject->wrapped().shouldHaveWebKitNamespaceForWorld(thisObject->world())) {
+        slot.setCacheableCustom(thisObject, ReadOnly | DontDelete | DontEnum, jsDOMWindowWebKit);
+        return true;
+    }
+#endif
+
+    // After this point it is no longer valid to cache any results because of
+    // the impure nature of the property accesses which follow. We can move this 
+    // statement further down when we add ways to mitigate these impurities with, 
+    // for example, watchpoints.
+    slot.disableCaching();
+
+    // Check for child frames by name before built-in properties to
+    // match Mozilla. This does not match IE, but some sites end up
+    // naming frames things that conflict with window properties that
+    // are in Moz but not IE. Since we have some of these, we have to do
+    // it the Moz way.
+    if (auto* scopedChild = thisObject->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
+        slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, scopedChild->document()->domWindow()));
+        return true;
+    }
+
+    // allow window[1] or parent[1] etc. (#56983)
+    Optional<uint32_t> index = parseIndex(propertyName);
+    if (index && index.value() < thisObject->wrapped().frame()->tree().scopedChildCount()) {
+        slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum,
+            toJS(exec, thisObject->wrapped().frame()->tree().scopedChild(index.value())->document()->domWindow()));
+        return true;
+    }
+
+    thisObject->printErrorMessage(errorMessage);
+    slot.setUndefined();
+    return true;
+}
+
 bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
@@ -151,11 +247,12 @@
     // because we always allow access to some function, just different ones depending whether access
     // is allowed.
     String errorMessage;
-    bool allowsAccess = shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage);
+    if (!shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage))
+        return jsDOMWindowGetOwnPropertySlotCrossOrigin(thisObject, exec, propertyName, slot, errorMessage);
     
     // Look for overrides before looking at any of our own properties, but ignore overrides completely
     // if this is cross-domain access.
-    if (allowsAccess && JSGlobalObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
+    if (JSGlobalObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
         return true;
     
     // We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the
@@ -179,12 +276,6 @@
             slot.setUndefined();
             return true;
         }
-    } else if (propertyName == exec->propertyNames().toString) {
-        // Allow access to toString() cross-domain, but always Object.prototype.toString.
-        if (!allowsAccess) {
-            slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, objectToStringFunctionGetter);
-            return true;
-        }
     }
 
 #if ENABLE(INDEXED_DATABASE)
@@ -194,7 +285,7 @@
     // and have to handle them specially here.
     // Once https://webkit.org/b/145669 is resolved, they can once again be auto generated.
     if (RuntimeEnabledFeatures::sharedFeatures().indexedDBEnabled() && (propertyName == exec->propertyNames().indexedDB || propertyName == exec->propertyNames().webkitIndexedDB)) {
-        slot.setCustom(thisObject, allowsAccess ? DontDelete | ReadOnly | CustomAccessor : ReadOnly | DontDelete | DontEnum, jsDOMWindowIndexedDB);
+        slot.setCustom(thisObject, DontDelete | ReadOnly | CustomAccessor, jsDOMWindowIndexedDB);
         return true;
     }
 #endif
@@ -202,16 +293,16 @@
     // When accessing cross-origin known Window properties, we always use the original property getter,
     // even if the property was removed / redefined. As of early 2016, this matches Firefox and Chrome's
     // behavior.
-    if (!thisObject->staticFunctionsReified() || !allowsAccess) {
+    if (!thisObject->staticFunctionsReified()) {
         if (auto* entry = JSDOMWindow::info()->staticPropHashTable->entry(propertyName)) {
-            slot.setCacheableCustom(thisObject, allowsAccess ? entry->attributes() : ReadOnly | DontDelete | DontEnum, entry->propertyGetter());
+            slot.setCacheableCustom(thisObject, entry->attributes(), entry->propertyGetter());
             return true;
         }
     }
 
 #if ENABLE(USER_MESSAGE_HANDLERS)
     if (propertyName == exec->propertyNames().webkit && thisObject->wrapped().shouldHaveWebKitNamespaceForWorld(thisObject->world())) {
-        slot.setCacheableCustom(thisObject, allowsAccess ? DontDelete | ReadOnly : ReadOnly | DontDelete | DontEnum, jsDOMWindowWebKit);
+        slot.setCacheableCustom(thisObject, DontDelete | ReadOnly, jsDOMWindowWebKit);
         return true;
     }
 #endif
@@ -219,15 +310,8 @@
     // Do prototype lookup early so that functions and attributes in the prototype can have
     // precedence over the index and name getters.  
     JSValue proto = thisObject->prototype();
-    if (proto.isObject()) {
-        if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) {
-            if (!allowsAccess) {
-                thisObject->printErrorMessage(errorMessage);
-                slot.setUndefined();
-            }
-            return true;
-        }
-    }
+    if (proto.isObject() && asObject(proto)->getPropertySlot(exec, propertyName, slot))
+        return true;
 
     // After this point it is no longer valid to cache any results because of
     // the impure nature of the property accesses which follow. We can move this 
@@ -240,8 +324,8 @@
     // naming frames things that conflict with window properties that
     // are in Moz but not IE. Since we have some of these, we have to do
     // it the Moz way.
-    if (thisObject->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
-        slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, childFrameGetter);
+    if (auto* scopedChild = thisObject->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
+        slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, scopedChild->document()->domWindow()));
         return true;
     }
 
@@ -256,12 +340,6 @@
         return true;
     }
 
-    if (!allowsAccess) {
-        thisObject->printErrorMessage(errorMessage);
-        slot.setUndefined();
-        return true;
-    }
-
     // Allow shortcuts like 'Image1' instead of document.images.Image1
     Document* document = thisObject->wrapped().frame()->document();
     if (is<HTMLDocument>(*document)) {
@@ -286,26 +364,27 @@
         return true;
     }
 
+    Identifier propertyName = Identifier::from(exec, index);
+    
     // We need to check for cross-domain access here without printing the generic warning message
     // because we always allow access to some function, just different ones depending whether access
     // is allowed.
     String errorMessage;
-    bool allowsAccess = shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage);
+    if (!shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), errorMessage))
+        return jsDOMWindowGetOwnPropertySlotCrossOrigin(thisObject, exec, propertyName, slot, errorMessage);
 
     // Look for overrides before looking at any of our own properties, but ignore overrides completely
     // if this is cross-domain access.
-    if (allowsAccess && JSGlobalObject::getOwnPropertySlotByIndex(thisObject, exec, index, slot))
+    if (JSGlobalObject::getOwnPropertySlotByIndex(thisObject, exec, index, slot))
         return true;
     
-    Identifier propertyName = Identifier::from(exec, index);
-    
     // Check for child frames by name before built-in properties to
     // match Mozilla. This does not match IE, but some sites end up
     // naming frames things that conflict with window properties that
     // are in Moz but not IE. Since we have some of these, we have to do
     // it the Moz way.
-    if (thisObject->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
-        slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, childFrameGetter);
+    if (auto* scopedChild = thisObject->wrapped().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) {
+        slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, scopedChild->document()->domWindow()));
         return true;
     }
     
@@ -313,13 +392,8 @@
     // precedence over the index and name getters.  
     JSValue proto = thisObject->prototype();
     if (proto.isObject()) {
-        if (asObject(proto)->getPropertySlot(exec, index, slot)) {
-            if (!allowsAccess) {
-                thisObject->printErrorMessage(errorMessage);
-                slot.setUndefined();
-            }
+        if (asObject(proto)->getPropertySlot(exec, index, slot))
             return true;
-        }
     }
 
     // FIXME: Search the whole frame hierarchy somewhere around here.
@@ -332,12 +406,6 @@
         return true;
     }
 
-    if (!allowsAccess) {
-        thisObject->printErrorMessage(errorMessage);
-        slot.setUndefined();
-        return true;
-    }
-
     // Allow shortcuts like 'Image1' instead of document.images.Image1
     Document* document = thisObject->wrapped().frame()->document();
     if (is<HTMLDocument>(*document)) {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to