Bug 1626687 - Do not handle data URIs larger than 2M on mobile r=necko-reviewers,geckoview-reviewers,valentin,snorp
authorowlishDeveloper <bugzeeeeee@gmail.com>
Wed, 29 Apr 2020 18:07:39 +0000
changeset 526803 a5106c6cd0378090f1059b5f90dbc96b3de87be7
parent 526802 0af74082185edbaace15ee6439d80d4604bed3a4
child 526804 7b28d7381c317ce7eb99836f1ece772416dc2d5d
push id114519
push useristorozhko@mozilla.com
push dateWed, 29 Apr 2020 22:50:11 +0000
treeherderautoland@a5106c6cd037 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnecko-reviewers, geckoview-reviewers, valentin, snorp
bugs1626687
milestone77.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 1626687 - Do not handle data URIs larger than 2M on mobile r=necko-reviewers,geckoview-reviewers,valentin,snorp Differential Revision: https://phabricator.services.mozilla.com/D72955
mobile/android/geckoview/src/androidTest/assets/www/data_uri.html
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt
modules/libpref/init/StaticPrefList.yaml
netwerk/protocol/data/nsDataHandler.cpp
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/www/data_uri.html
@@ -0,0 +1,9 @@
+<html>
+    <head>
+    <title>Link with a giant data URI</title>
+    </head>
+    <body>
+    <a href="insert uri here" id="smallLink">Open small link</a>
+    <a href="insert uri here" id="largeLink">Open large link</a>
+</body>
+</html>
\ No newline at end of file
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
@@ -63,16 +63,17 @@ open class BaseSessionTest(noErrorCollec
         const val FIXED_BOTTOM = "/assets/www/fixedbottom.html"
         const val FIXED_VH = "/assets/www/fixedvh.html"
         const val FIXED_PERCENT = "/assets/www/fixedpercent.html"
         const val STORAGE_TITLE_HTML_PATH = "/assets/www/reflect_local_storage_into_title.html"
         const val HUNG_SCRIPT = "/assets/www/hungScript.html"
         const val PUSH_HTML_PATH = "/assets/www/push/push.html"
         const val OPEN_WINDOW_PATH = "/assets/www/worker/open_window.html"
         const val OPEN_WINDOW_TARGET_PATH = "/assets/www/worker/open_window_target.html"
+        const val DATA_URI_PATH = "/assets/www/data_uri.html"
 
         const val TEST_ENDPOINT = GeckoSessionTestRule.TEST_ENDPOINT
     }
 
     @get:Rule val sessionRule = GeckoSessionTestRule()
 
     @get:Rule val errors = ErrorCollector()
 
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt
@@ -1,29 +1,26 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.geckoview.test
 
-import org.mozilla.geckoview.GeckoSession
-import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
-import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
-import org.mozilla.geckoview.test.util.Callbacks
-
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
-import androidx.test.filters.LargeTest
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
 import org.hamcrest.Matchers.*
 import org.junit.Assume.assumeThat
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.NullDelegate
+import org.mozilla.geckoview.GeckoSession
+import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.*
+import org.mozilla.geckoview.test.util.Callbacks
+import org.mozilla.geckoview.test.util.UiThreadUtils
 
 @RunWith(AndroidJUnit4::class)
 @MediumTest
 class ProgressDelegateTest : BaseSessionTest() {
 
     @Test fun loadProgress() {
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
         sessionRule.waitForPageStop()
@@ -385,9 +382,46 @@ class ProgressDelegateTest : BaseSession
         sessionRule.waitForPageStop()
 
         sessionRule.waitUntilCalled(object : Callbacks.ProgressDelegate {
             @AssertCalled(count = 1)
             override fun onSessionStateChange(session: GeckoSession, sessionState: GeckoSession.SessionState) {
             }
         })
     }
+
+    @Test(expected = UiThreadUtils.TimeoutException::class)
+    fun handlingLargeDataURIs() {
+        sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
+            @AssertCalled(count = 1)
+            override fun onPageStart(session: GeckoSession, url: String) {
+            }
+        });
+
+        val dataBytes = ByteArray(3 * 1024 * 1024)
+        val uri = GeckoSession.createDataUri(dataBytes, "*/*")
+
+        sessionRule.session.loadTestPath(DATA_URI_PATH)
+        sessionRule.session.waitForPageStop()
+
+        sessionRule.session.evaluateJS("document.querySelector('#largeLink').href = \"$uri\"")
+        sessionRule.session.evaluateJS("document.querySelector('#largeLink').click()")
+        sessionRule.session.waitForPageStop()
+    }
+
+    @Test fun handlingSmallDataURIs() {
+        sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
+            @AssertCalled(count = 2)
+            override fun onPageStart(session: GeckoSession, url: String) {
+            }
+        });
+
+        val dataBytes = this.getTestBytes("/assets/www/images/test.gif")
+        val uri = GeckoSession.createDataUri(dataBytes, "image/*")
+
+        sessionRule.session.loadTestPath(DATA_URI_PATH)
+        sessionRule.session.waitForPageStop()
+
+        sessionRule.session.evaluateJS("document.querySelector('#smallLink').href = \"$uri\"")
+        sessionRule.session.evaluateJS("document.querySelector('#smallLink').click()")
+        sessionRule.session.waitForPageStop()
+    }
 }
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -7433,16 +7433,21 @@
   value: false
   mirror: always
 
 - name: network.cookie.thirdparty.nonsecureSessionOnly
   type: bool
   value: false
   mirror: always
 
+- name: network.data.max-uri-length-mobile
+  type: RelaxedAtomicUint32
+  value: 2 * 1024 * 1024
+  mirror: always
+
 # false=real referer, true=spoof referer (use target URI as referer).
 - name: network.http.referer.spoofSource
   type: bool
   value: false
   mirror: always
 
 # Check whether we need to hide referrer when leaving a .onion domain.
 # false=allow onion referer, true=hide onion referer (use empty referer).
--- a/netwerk/protocol/data/nsDataHandler.cpp
+++ b/netwerk/protocol/data/nsDataHandler.cpp
@@ -8,16 +8,20 @@
 #include "nsNetCID.h"
 #include "nsError.h"
 #include "nsIOService.h"
 #include "DataChannelChild.h"
 #include "plstr.h"
 #include "nsSimpleURI.h"
 #include "mozilla/dom/MimeType.h"
 
+#ifdef ANDROID
+#  include "mozilla/StaticPrefs_network.h"
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_ISUPPORTS(nsDataHandler, nsIProtocolHandler, nsISupportsWeakReference)
 
 nsresult nsDataHandler::Create(nsISupports* aOuter, const nsIID& aIID,
                                void** aResult) {
   RefPtr<nsDataHandler> ph = new nsDataHandler();
   return ph->QueryInterface(aIID, aResult);
@@ -51,16 +55,22 @@ nsDataHandler::GetProtocolFlags(uint32_t
                                                   const char* aCharset,
                                                   nsIURI* aBaseURI,
                                                   nsIURI** result) {
   nsresult rv;
   nsCOMPtr<nsIURI> uri;
 
   nsCString spec(aSpec);
 
+#ifdef ANDROID
+  // Due to heap limitations on mobile, limits the size of data URL
+  if (spec.Length() > StaticPrefs::network_data_max_uri_length_mobile())
+    return NS_ERROR_OUT_OF_MEMORY;
+#endif
+
   if (aBaseURI && !spec.IsEmpty() && spec[0] == '#') {
     // Looks like a reference instead of a fully-specified URI.
     // --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
     rv = NS_MutateURI(aBaseURI).SetRef(spec).Finalize(uri);
   } else {
     // Otherwise, we'll assume |spec| is a fully-specified data URI
     nsAutoCString contentType;
     bool base64;