Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(131)

Side by Side Diff: content/browser/loader/cross_site_resource_handler_browsertest.cc

Issue 1384113002: CrossSiteResourceHandler: cancel request if the RFH is gone (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@no_isolate_apps
Patch Set: Fix comment. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/browser/loader/cross_site_resource_handler.cc ('k') | content/content_tests.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/callback.h"
6 #include "base/command_line.h"
7 #include "base/memory/weak_ptr.h"
8 #include "base/single_thread_task_runner.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "content/browser/frame_host/render_frame_host_impl.h"
11 #include "content/browser/loader/resource_dispatcher_host_impl.h"
12 #include "content/browser/web_contents/web_contents_impl.h"
13 #include "content/common/frame_messages.h"
14 #include "content/public/browser/resource_dispatcher_host.h"
15 #include "content/public/browser/resource_dispatcher_host_delegate.h"
16 #include "content/public/browser/resource_throttle.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/test/browser_test_utils.h"
19 #include "content/public/test/content_browser_test.h"
20 #include "content/public/test/content_browser_test_utils.h"
21 #include "content/public/test/test_utils.h"
22 #include "content/shell/browser/shell.h"
23 #include "content/shell/browser/shell_resource_dispatcher_host_delegate.h"
24 #include "ipc/ipc_security_test_util.h"
25 #include "net/dns/mock_host_resolver.h"
26 #include "net/test/embedded_test_server/embedded_test_server.h"
27 #include "net/url_request/url_request.h"
28
29 namespace content {
30
31 namespace {
32
33 // A ResourceDispatchHostDelegate that uses ResourceThrottles to pause a
34 // targeted request temporarily, to run a chunk of test code.
35 class TestResourceDispatcherHostDelegate
36 : public ShellResourceDispatcherHostDelegate {
37 public:
38 using RequestDeferredHook = base::Callback<void(const base::Closure& resume)>;
39 TestResourceDispatcherHostDelegate() : throttle_created_(false) {}
40
41 void RequestBeginning(net::URLRequest* request,
42 ResourceContext* resource_context,
43 AppCacheService* appcache_service,
44 ResourceType resource_type,
45 ScopedVector<ResourceThrottle>* throttles) override {
46 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
47 ShellResourceDispatcherHostDelegate::RequestBeginning(
48 request, resource_context, appcache_service, resource_type, throttles);
49 // Expect only a single request for the tracked url.
nasko 2015/10/05 18:01:54 nit: Empty line between code and new comment line.
ncarter (slow) 2015/10/05 18:31:37 Done.
50 ASSERT_FALSE(throttle_created_);
nasko 2015/10/05 18:01:54 I don't see how throttle_created_ can actually bec
ncarter (slow) 2015/10/05 18:31:37 Good catch; I think I lose my copy+paste privilege
51 // If this is a request for the tracked URL, add a throttle to track it.
52 if (request->url() == tracked_url_) {
53 throttles->push_back(
54 new DeferStartForTestCallback(request, this, run_on_start_));
55 }
56 }
57
58 // Starts tracking a URL. The request for previously tracked URL, if any,
59 // must have been made and deleted before calling this function.
60 void SetTrackedURL(const GURL& tracked_url,
61 const RequestDeferredHook& run_on_start) {
62 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
63 // Should not currently be tracking any URL.
64 ASSERT_FALSE(run_loop_);
65
66 // Create a RunLoop that will be stopped once the request for the tracked
67 // URL has been destroyed, to allow tracking the URL while also waiting for
68 // other events.
69 run_loop_.reset(new base::RunLoop());
70
71 BrowserThread::PostTask(
72 BrowserThread::IO, FROM_HERE,
73 base::Bind(&TestResourceDispatcherHostDelegate::SetTrackedURLOnIOThread,
74 base::Unretained(this), tracked_url, run_on_start,
75 run_loop_->QuitClosure()));
76 }
77
78 // Waits until the tracked URL has been requested, and the request for it has
79 // been destroyed.
80 bool WaitForTrackedURLAndGetCompleted() {
81 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
82 run_loop_->Run();
83 run_loop_.reset();
84 return tracked_request_completed_;
85 }
86
87 private:
88 // A ResourceThrottle which defers the request at WillStartRequest time until
89 // a test-supplied callback completes. Notifies |tracker| when the request is
90 // destroyed.
91 class DeferStartForTestCallback : public ResourceThrottle {
nasko 2015/10/05 18:01:54 Shouldn't this class bear some mention of Throttle
ncarter (slow) 2015/10/05 18:31:37 Done.
92 public:
93 DeferStartForTestCallback(net::URLRequest* request,
94 TestResourceDispatcherHostDelegate* tracker,
95 const RequestDeferredHook& run_on_start)
96 : request_(request),
97 tracker_(tracker),
98 run_on_start_(run_on_start),
99 weak_factory_(this) {}
nasko 2015/10/05 18:01:54 It seems that this constructor should be setting t
ncarter (slow) 2015/10/05 18:31:37 Done.
100
101 void WillStartRequest(bool* defer) override {
102 *defer = true;
103 base::Closure resume_request_on_io_thread = base::Bind(
104 base::IgnoreResult(&BrowserThread::PostTask), BrowserThread::IO,
105 FROM_HERE, base::Bind(&DeferStartForTestCallback::Resume,
106 weak_factory_.GetWeakPtr()));
107 BrowserThread::PostTask(
108 BrowserThread::UI, FROM_HERE,
109 base::Bind(run_on_start_, resume_request_on_io_thread));
110 }
111
112 ~DeferStartForTestCallback() override {
113 // If the request is deleted without being cancelled, its status will
114 // indicate it succeeded, so have to check if the request is still pending
115 // as well.
116 tracker_->OnTrackedRequestDestroyed(!request_->is_pending() &&
117 request_->status().is_success());
118 }
119
120 // ResourceThrottle implementation:
121 const char* GetNameForLogging() const override {
122 return "DeferStartForTestCallback";
123 }
124
125 private:
126 void Resume() { controller()->Resume(); }
127 net::URLRequest* request_;
128 TestResourceDispatcherHostDelegate* tracker_;
129 RequestDeferredHook run_on_start_;
130 base::WeakPtrFactory<DeferStartForTestCallback> weak_factory_;
131
132 DISALLOW_COPY_AND_ASSIGN(DeferStartForTestCallback);
133 };
134
135 void SetTrackedURLOnIOThread(const GURL& tracked_url,
136 const RequestDeferredHook& run_on_start,
137 const base::Closure& run_loop_quit_closure) {
138 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
139 throttle_created_ = false;
140 tracked_url_ = tracked_url;
141 run_on_start_ = run_on_start;
142 run_loop_quit_closure_ = run_loop_quit_closure;
143 }
144
145 void OnTrackedRequestDestroyed(bool completed) {
146 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
147 tracked_request_completed_ = completed;
148 tracked_url_ = GURL();
149 run_on_start_ = RequestDeferredHook();
150
151 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
152 run_loop_quit_closure_);
153 }
154
155 // These live on the IO thread.
156 GURL tracked_url_;
157 bool throttle_created_;
158 base::Closure run_loop_quit_closure_;
159 RequestDeferredHook run_on_start_;
160
161 // This lives on the UI thread.
162 scoped_ptr<base::RunLoop> run_loop_;
163
164 // Set on the IO thread while |run_loop_| is non-NULL, read on the UI thread
165 // after deleting run_loop_.
166 bool tracked_request_completed_;
167
168 DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate);
169 };
170
171 class CrossSiteResourceHandlerTest : public ContentBrowserTest {
172 public:
173 CrossSiteResourceHandlerTest() : old_delegate_(NULL) {}
174
175 // ContentBrowserTest implementation:
176 void SetUpOnMainThread() override {
177 BrowserThread::PostTask(
178 BrowserThread::IO, FROM_HERE,
179 base::Bind(
180 &CrossSiteResourceHandlerTest::InjectResourceDispatcherHostDelegate,
181 base::Unretained(this)));
182 host_resolver()->AddRule("*", "127.0.0.1");
183 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
184 content::SetupCrossSiteRedirector(embedded_test_server());
185 }
186
187 void TearDownOnMainThread() override {
188 BrowserThread::PostTask(
189 BrowserThread::IO, FROM_HERE,
190 base::Bind(&CrossSiteResourceHandlerTest::
191 RestoreResourceDispatcherHostDelegate,
192 base::Unretained(this)));
193 }
194
195 protected:
196 void SetUpCommandLine(base::CommandLine* command_line) override {
197 IsolateAllSitesForTesting(command_line);
198 }
199
200 void InjectResourceDispatcherHostDelegate() {
201 DCHECK_CURRENTLY_ON(BrowserThread::IO);
202 old_delegate_ = ResourceDispatcherHostImpl::Get()->delegate();
203 ResourceDispatcherHostImpl::Get()->SetDelegate(&tracking_delegate_);
204 }
205
206 void RestoreResourceDispatcherHostDelegate() {
207 DCHECK_CURRENTLY_ON(BrowserThread::IO);
208 ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate_);
209 old_delegate_ = NULL;
nasko 2015/10/05 18:01:54 nullptr
ncarter (slow) 2015/10/05 18:31:37 Done, everywhere.
210 }
211
212 TestResourceDispatcherHostDelegate& tracking_delegate() {
213 return tracking_delegate_;
214 }
215
216 private:
217 TestResourceDispatcherHostDelegate tracking_delegate_;
218 ResourceDispatcherHostDelegate* old_delegate_;
219 };
220
221 void SimulateMaliciousFrameDetachOnUIThread(int render_process_id,
222 int frame_routing_id,
223 const base::Closure& done_cb) {
224 RenderFrameHostImpl* rfh =
225 RenderFrameHostImpl::FromID(render_process_id, frame_routing_id);
226 CHECK(rfh);
227
228 // Inject a frame detach message. An attacker-controlled renderer could do
229 // this without also cancelling the pending navigation (as blink would, if you
230 // removed the iframe from the document via js).
231 rfh->OnMessageReceived(FrameHostMsg_Detach(frame_routing_id));
232 done_cb.Run();
233 }
234
235 } // namespace
236
237 IN_PROC_BROWSER_TEST_F(CrossSiteResourceHandlerTest,
238 NoDeliveryToDetachedFrame) {
239 GURL attacker_page = embedded_test_server()->GetURL(
240 "evil.com", "/cross_site_iframe_factory.html?evil(evil)");
241 NavigateToURL(shell(), attacker_page);
242
243 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
244 ->GetFrameTree()
245 ->root();
246
247 RenderFrameHost* child_frame = root->child_at(0)->current_frame_host();
248
249 // Attacker initiates a navigation to a cross-site document. Under --site-per-
250 // process, these bytes must not be sent to the attacker process.
251 GURL target_resource =
252 embedded_test_server()->GetURL("a.com", "/title1.html");
253
254 // We add a testing hook to simulate the attacker-controlled process sending
255 // FrameHostMsg_Detach before the http response arrives. At the time this test
256 // was written, the resource request had a lifetime separate from the RFH,
257 tracking_delegate().SetTrackedURL(
258 target_resource, base::Bind(&SimulateMaliciousFrameDetachOnUIThread,
259 child_frame->GetProcess()->GetID(),
260 child_frame->GetRoutingID()));
261 ExecuteScript(
262 shell()->web_contents()->GetMainFrame(),
263 base::StringPrintf("document.getElementById('child-0').src='%s'",
264 target_resource.spec().c_str()));
265
266 // Wait for the scenario to play out. If this returns false, it means the
267 // request did not succeed, which is good in this case.
268 EXPECT_FALSE(tracking_delegate().WaitForTrackedURLAndGetCompleted())
269 << "Request should have been cancelled before reaching the renderer.";
270 }
271
272 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/loader/cross_site_resource_handler.cc ('k') | content/content_tests.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698