Commit 453a193d authored by Dmitry Gozman's avatar Dmitry Gozman Committed by Commit Bot

AppCache: fix a race when registering new backend on navigation

We currently have a race between:
- AppCacheBackendImpl::RegisterHost call coming over it's own mojo pipe and
  looking for precreated AppCacheHost;
- Destroying NavigationRequest, which destroys NavigationHandleImpl, which
  destroys AppCacheHavigationHandle which posts a task to destroy precreated
  AppCacheHost on IO thread.

To combat this, we take AppCacheHavigationHandle from committed NavigationRequest
before destroying it and keep it around in RenderFrameHostImpl. This measure
keeps in a common case when we commit navigation and reach for app cache host.

However, we still cannot assert that we always have a precreated host because
browser can decided to destroy the frame immediately (together with
RenderFrameHostImpl and AppCacheHavigationHandle), while renderer asks for the
AppCacheHost. The best thing we can do in this case is stall AppCacheHost
registration until renderer's frame is inevitably destroyed by the browser's
request.

This is a speculative fix for crashes in issue 941903. Some existing tests, e.g.
http/tests/misc/iframe-reparenting-id-collision.html, easily expose the race,
but since they are not using AppCache, we don't hit the crash point from the bug.

Bug: 941903
Change-Id: I3925d0b9ab9655235b524da561d0e8ba8345fb4b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1529381Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarMarijn Kruisselbrink <mek@chromium.org>
Commit-Queue: Dmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#642307}
parent 0a06353e
...@@ -51,6 +51,15 @@ void AppCacheBackendImpl::RegisterHost( ...@@ -51,6 +51,15 @@ void AppCacheBackendImpl::RegisterHost(
// here on. // here on.
host->set_frontend(std::move(frontend), render_frame_id); host->set_frontend(std::move(frontend), render_frame_id);
} else { } else {
if (id < 0) {
// Negative ids correspond to precreated hosts. We should be able to
// retrieve one, but currently we have a race between this IPC and
// browser removing the corresponding frame and precreated AppCacheHost.
// Instead of crashing the renderer or returning wrong host, we do not
// bind any host and let renderer do nothing until it is destroyed by
// the request from browser.
return;
}
host = std::make_unique<AppCacheHost>(id, process_id(), render_frame_id, host = std::make_unique<AppCacheHost>(id, process_id(), render_frame_id,
std::move(frontend), service_); std::move(frontend), service_);
} }
......
...@@ -28,27 +28,26 @@ class ChromeAppCacheService; ...@@ -28,27 +28,26 @@ class ChromeAppCacheService;
// 2) When the navigation request is sent to the IO thread, we include a // 2) When the navigation request is sent to the IO thread, we include a
// pointer to the AppCacheNavigationHandleCore. // pointer to the AppCacheNavigationHandleCore.
// //
// 3. The AppCacheHost instance is created and its ownership is passed to the // 3) The AppCacheHost instance is created and its ownership is passed to the
// AppCacheNavigationHandleCore instance. Now the app cache host id is // AppCacheNavigationHandleCore instance.
// updated.
// //
// 4) The AppCacheNavigationHandleCore instance informs the // 4) When the navigation is ready to commit, the NavigationRequest will
// AppCacheNavigationHandle instance on the UI thread that the app cache
// host id was updated.
//
// 5) When the navigation is ready to commit, the NavigationRequest will
// update the CommitNavigationParams based on the id from the // update the CommitNavigationParams based on the id from the
// AppCacheNavigationHandle. // AppCacheNavigationHandle.
// //
// 6) The commit leads to AppCache registrations happening from the renderer. // 5) The commit leads to AppCache registrations happening from the renderer.
// This is via the IPC message AppCacheHostMsg_RegisterHost. The // This is via the AppCacheBackend.RegisterHost mojo call. The
// AppCacheDispatcherHost class which handles these IPCs will be informed // AppCacheBackendImpl class which handles these calls will be informed
// about these hosts when the navigation commits. It will ignore the // about these hosts when the navigation commits. It will ignore the
// host registrations as they have already been registered. The // host registrations as they have already been registered. The
// ownership of the AppCacheHost is passed from the // ownership of the AppCacheHost is passed from the
// AppCacheNavigationHandle core to the AppCacheBackend. // AppCacheNavigationHandle core to the AppCacheBackendImpl.
//
// 7) When the navigation finishes, the AppCacheNavigationHandle is // 6) Meanwhile, RenderFrameHostImpl takes ownership of
// AppCacheNavigationHandle once navigation commits, so that precreated
// AppCacheHost is not destroyed before IPC above reaches AppCacheBackend.
//
// 7) When the next navigation commits, previous AppCacheNavigationHandle is
// destroyed. The destructor of the AppCacheNavigationHandle posts a // destroyed. The destructor of the AppCacheNavigationHandle posts a
// task to destroy the AppCacheNavigationHandleCore on the IO thread. // task to destroy the AppCacheNavigationHandleCore on the IO thread.
......
...@@ -538,6 +538,11 @@ void NavigationHandleImpl::InitAppCacheHandle( ...@@ -538,6 +538,11 @@ void NavigationHandleImpl::InitAppCacheHandle(
appcache_service, ChildProcessHost::kInvalidUniqueID)); appcache_service, ChildProcessHost::kInvalidUniqueID));
} }
std::unique_ptr<AppCacheNavigationHandle>
NavigationHandleImpl::TakeAppCacheHandle() {
return std::move(appcache_handle_);
}
void NavigationHandleImpl::WillStartRequest( void NavigationHandleImpl::WillStartRequest(
ThrottleChecksFinishedCallback callback) { ThrottleChecksFinishedCallback callback) {
TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this, TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this,
......
...@@ -214,6 +214,8 @@ class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle, ...@@ -214,6 +214,8 @@ class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle,
return appcache_handle_.get(); return appcache_handle_.get();
} }
std::unique_ptr<AppCacheNavigationHandle> TakeAppCacheHandle();
typedef base::OnceCallback<void(NavigationThrottle::ThrottleCheckResult)> typedef base::OnceCallback<void(NavigationThrottle::ThrottleCheckResult)>
ThrottleChecksFinishedCallback; ThrottleChecksFinishedCallback;
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "cc/base/switches.h" #include "cc/base/switches.h"
#include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_manager.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/appcache/appcache_navigation_handle.h"
#include "content/browser/background_fetch/background_fetch_service_impl.h" #include "content/browser/background_fetch/background_fetch_service_impl.h"
#include "content/browser/bluetooth/web_bluetooth_service_impl.h" #include "content/browser/bluetooth/web_bluetooth_service_impl.h"
#include "content/browser/browser_main_loop.h" #include "content/browser/browser_main_loop.h"
...@@ -6281,6 +6282,8 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal( ...@@ -6281,6 +6282,8 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal(
committed_request->GetMimeType() == "message/rfc822"); committed_request->GetMimeType() == "message/rfc822");
accessibility_reset_count_ = 0; accessibility_reset_count_ = 0;
appcache_handle_ =
committed_request->navigation_handle()->TakeAppCacheHandle();
frame_tree_node()->navigator()->DidNavigate(this, *validated_params, frame_tree_node()->navigator()->DidNavigate(this, *validated_params,
std::move(committed_request), std::move(committed_request),
is_same_document_navigation); is_same_document_navigation);
......
...@@ -129,6 +129,7 @@ struct ResourceResponse; ...@@ -129,6 +129,7 @@ struct ResourceResponse;
} // namespace network } // namespace network
namespace content { namespace content {
class AppCacheNavigationHandle;
class AuthenticatorImpl; class AuthenticatorImpl;
class FrameTree; class FrameTree;
class FrameTreeNode; class FrameTreeNode;
...@@ -1743,6 +1744,11 @@ class CONTENT_EXPORT RenderFrameHostImpl ...@@ -1743,6 +1744,11 @@ class CONTENT_EXPORT RenderFrameHostImpl
// NavigationRequest is for a cross-document navigation. // NavigationRequest is for a cross-document navigation.
std::unique_ptr<NavigationRequest> navigation_request_; std::unique_ptr<NavigationRequest> navigation_request_;
// Holds AppCacheNavigationHandle after navigation request has been committed,
// which keeps corresponding AppCacheHost alive while renderer asks for it.
// See AppCacheNavigationHandle comment for more details.
std::unique_ptr<AppCacheNavigationHandle> appcache_handle_;
// Holds the cross-document NavigationRequests that are waiting to commit, // Holds the cross-document NavigationRequests that are waiting to commit,
// indexed by IDs. These are navigations that have passed ReadyToCommit stage // indexed by IDs. These are navigations that have passed ReadyToCommit stage
// and are waiting for the renderer to send back a matching // and are waiting for the renderer to send back a matching
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment