Block a compromised renderer from reusing request ids.

BUG=578882

Review URL: https://codereview.chromium.org/1608573002

Cr-Commit-Position: refs/heads/master@{#372547}
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index c515609..6714e9a 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -23,6 +23,7 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/interstitial_page.h"
 #include "content/public/browser/interstitial_page_delegate.h"
+#include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/appcache_info.h"
 #include "content/public/common/browser_side_navigation_policy.h"
@@ -37,6 +38,7 @@
 #include "ipc/ipc_security_test_util.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/url_request/url_request_slow_download_job.h"
 
 using IPC::IpcSecurityTestUtil;
 
@@ -44,6 +46,11 @@
 
 namespace {
 
+// This request id is used by tests that craft a
+// ResourceHostMsg_RequestResource. The id is sufficiently large that it doesn't
+// collide with ids used by previous navigation requests.
+const int kRequestIdNotPreviouslyUsed = 10000;
+
 // This is a helper function for the tests which attempt to create a
 // duplicate RenderViewHost or RenderWidgetHost. It tries to create two objects
 // with the same process and routing ids, which causes a collision.
@@ -98,13 +105,11 @@
   return next_rfh->render_view_host();
 }
 
-ResourceHostMsg_Request CreateXHRRequestWithOrigin(const char* origin) {
+ResourceHostMsg_Request CreateXHRRequest(const char* url) {
   ResourceHostMsg_Request request;
   request.method = "GET";
-  request.url = GURL("http://bar.com/simple_page.html");
-  request.first_party_for_cookies = GURL(origin);
+  request.url = GURL(url);
   request.referrer_policy = blink::WebReferrerPolicyDefault;
-  request.headers = base::StringPrintf("Origin: %s\r\n", origin);
   request.load_flags = 0;
   request.origin_pid = 0;
   request.resource_type = RESOURCE_TYPE_XHR;
@@ -120,6 +125,49 @@
   return request;
 }
 
+ResourceHostMsg_Request CreateXHRRequestWithOrigin(const char* origin) {
+  ResourceHostMsg_Request request =
+      CreateXHRRequest("http://bar.com/simple_page.html");
+  request.first_party_for_cookies = GURL(origin);
+  request.headers = base::StringPrintf("Origin: %s\r\n", origin);
+  return request;
+}
+
+void TryCreateDuplicateRequestIds(Shell* shell, bool block_loaders) {
+  NavigateToURL(shell, GURL("http://foo.com/simple_page.html"));
+  RenderFrameHost* rfh = shell->web_contents()->GetMainFrame();
+
+  if (block_loaders) {
+    // Test the case where loaders are placed into blocked_loaders_map_.
+    int child_id = rfh->GetProcess()->GetID();
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::Bind(&ResourceDispatcherHost::BlockRequestsForRoute,
+                   base::Unretained(ResourceDispatcherHost::Get()), child_id,
+                   rfh->GetRoutingID()));
+  }
+
+  // URLRequestSlowDownloadJob waits for another request to kFinishDownloadUrl
+  // to finish all pending requests. It is never sent, so the following URL
+  // blocks indefinitely, which is good because the request stays alive and the
+  // test can try to reuse the request id without a race.
+  const char* blocking_url = net::URLRequestSlowDownloadJob::kUnknownSizeUrl;
+  ResourceHostMsg_Request request(CreateXHRRequest(blocking_url));
+
+  // Use the same request id twice.
+  RenderProcessHostWatcher process_killed(
+      rfh->GetProcess(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  IPC::IpcSecurityTestUtil::PwnMessageReceived(
+      rfh->GetProcess()->GetChannel(),
+      ResourceHostMsg_RequestResource(rfh->GetRoutingID(),
+                                      kRequestIdNotPreviouslyUsed, request));
+  IPC::IpcSecurityTestUtil::PwnMessageReceived(
+      rfh->GetProcess()->GetChannel(),
+      ResourceHostMsg_RequestResource(rfh->GetRoutingID(),
+                                      kRequestIdNotPreviouslyUsed, request));
+  process_killed.Wait();
+}
+
 }  // namespace
 
 
@@ -145,6 +193,12 @@
             ",EXCLUDE localhost");
   }
 
+  void SetUpOnMainThread() override {
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::Bind(&net::URLRequestSlowDownloadJob::AddUrlHandler));
+  }
+
  protected:
   // Tests that a given file path sent in a ViewHostMsg_RunFileChooser will
   // cause renderer to be killed.
@@ -384,7 +438,8 @@
         RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
     IPC::IpcSecurityTestUtil::PwnMessageReceived(
         web_rfh->GetProcess()->GetChannel(),
-        ResourceHostMsg_RequestResource(web_rfh->GetRoutingID(), 1,
+        ResourceHostMsg_RequestResource(web_rfh->GetRoutingID(),
+                                        kRequestIdNotPreviouslyUsed,
                                         chrome_origin_msg));
     web_process_killed.Wait();
   }
@@ -404,7 +459,8 @@
         RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
     IPC::IpcSecurityTestUtil::PwnMessageReceived(
         web_rfh->GetProcess()->GetChannel(),
-        ResourceHostMsg_RequestResource(web_rfh->GetRoutingID(), 1,
+        ResourceHostMsg_RequestResource(web_rfh->GetRoutingID(),
+                                        kRequestIdNotPreviouslyUsed,
                                         embedder_isolated_origin_msg));
     web_process_killed.Wait();
     SetBrowserClientForTesting(old_client);
@@ -418,7 +474,8 @@
         RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
     IPC::IpcSecurityTestUtil::PwnMessageReceived(
         web_rfh->GetProcess()->GetChannel(),
-        ResourceHostMsg_RequestResource(web_rfh->GetRoutingID(), 1,
+        ResourceHostMsg_RequestResource(web_rfh->GetRoutingID(),
+                                        kRequestIdNotPreviouslyUsed,
                                         invalid_origin_msg));
     web_process_killed.Wait();
   }
@@ -431,10 +488,20 @@
         RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
     IPC::IpcSecurityTestUtil::PwnMessageReceived(
         web_rfh->GetProcess()->GetChannel(),
-        ResourceHostMsg_RequestResource(web_rfh->GetRoutingID(), 1,
+        ResourceHostMsg_RequestResource(web_rfh->GetRoutingID(),
+                                        kRequestIdNotPreviouslyUsed,
                                         invalid_scheme_origin_msg));
     web_process_killed.Wait();
   }
 }
 
+// Renderer process should not be able to create multiple requests with the same
+// id.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, InvalidRequestId) {
+  // Existing loader in pending_loaders_.
+  TryCreateDuplicateRequestIds(shell(), false);
+  // Existing loader in blocked_loaders_map_.
+  TryCreateDuplicateRequestIds(shell(), true);
+}
+
 }  // namespace content