Commit cc67fc68 authored by Lowell Manners's avatar Lowell Manners Committed by Commit Bot

[bfcache] Don't immediately flush pages with related SiteInstances.

Rather than synchronously deleting all pages in the BackForwardCache
when there is a conflicting BrowsingInstance, call
EvictFramesInBrowsingInstance which asynchronously deletes only the
conflicting pages.

Calling evict is safer than immediately deleting frames, which can lead
to UAF bugs.

Change-Id: I4e453c3c1dacc5ed764cc344efbc26f24d7fe115
Bug: 993337
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1844964
Commit-Queue: Lowell Manners <lowell@chromium.org>
Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707310}
parent 3236b343
......@@ -222,6 +222,8 @@ std::string BackForwardCacheImpl::CanStoreDocumentResult::ToString() {
return "No: granted media stream access";
case Reason::kSchedulerTrackedFeatureUsed:
return "No: scheduler tracked feature is used";
case Reason::kConflictingBrowsingInstance:
return "No: conflicting BrowsingInstance";
}
}
......@@ -464,6 +466,17 @@ void BackForwardCacheImpl::Flush() {
entries_.clear();
}
void BackForwardCacheImpl::EvictFramesInRelatedSiteInstances(
SiteInstance* site_instance) {
for (std::unique_ptr<Entry>& entry : entries_) {
if (entry->render_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
site_instance))
entry->render_frame_host->EvictFromBackForwardCacheWithReason(
BackForwardCacheMetrics::NotRestoredReason::
kConflictingBrowsingInstance);
}
}
void BackForwardCacheImpl::PostTaskToDestroyEvictedFrames() {
base::PostTask(FROM_HERE, {BrowserThread::UI},
base::BindOnce(&BackForwardCacheImpl::DestroyEvictedFrames,
......
......@@ -27,6 +27,7 @@ namespace content {
class RenderFrameHostImpl;
class RenderFrameProxyHost;
class RenderViewHostImpl;
class SiteInstance;
// BackForwardCache:
//
......@@ -131,6 +132,10 @@ class CONTENT_EXPORT BackForwardCacheImpl : public BackForwardCache {
// Remove all entries from the BackForwardCache.
void Flush();
// Evict all cached pages in the same BrowsingInstance as
// |site_instance|.
void EvictFramesInRelatedSiteInstances(SiteInstance* site_instance);
// Posts a task to destroy all frames in the BackForwardCache that have been
// marked as evicted.
void PostTaskToDestroyEvictedFrames();
......
......@@ -58,7 +58,8 @@ class BackForwardCacheMetrics
kDialog = 17,
kGrantedMediaStreamAccess = 18,
kSchedulerTrackedFeatureUsed = 19,
kMaxValue = kSchedulerTrackedFeatureUsed,
kConflictingBrowsingInstance = 20,
kMaxValue = kConflictingBrowsingInstance,
};
// Please keep in sync with BackForwardCacheHistoryNavigationOutcome in
......
......@@ -2558,18 +2558,30 @@ void NavigationControllerImpl::NavigateToExistingPendingEntry(
return;
}
// By design, a page in the BackForwardCache is alone in its BrowsingInstance.
// History navigation might try to reuse a specific SiteInstance, already used
// by a page in the cache. This must not happen. It would fail creating the
// RenderFrame, because only one main document can live there. For this
// reason, the BackForwardCache is flushed.
// TODO(arthursonzogni): Flushing the entire cache is a bit overkill, this can
// be refined to only delete the page (if any) using the same
// BrowsingInstance.
// History navigation might try to reuse a specific BrowsingInstance, already
// used by a page in the cache. To avoid having two different main frames that
// live in the same BrowsingInstance, evict the all pages with this
// BrowsingInstance from the cache.
//
// For example, take the following scenario:
//
// A1 = Some page on a.com
// A2 = Some other page on a.com
// B3 = An uncacheable page on b.com
//
// Then the following navigations occur:
// A1->A2->B3->A1
// On the navigation from B3 to A1, A2 will remain in the cache (B3 doesn't
// take its place) and A1 will be created in the same BrowsingInstance (and
// SiteInstance), as A2.
//
// If we didn't do anything, both A1 and A2 would remain alive in the same
// BrowsingInstance/SiteInstance, which is unsupported by
// RenderFrameHostManager::CommitPending(). To avoid this conundrum, we evict
// A2 from the cache.
if (pending_entry_->site_instance()) {
SiteInstance* current = root->current_frame_host()->GetSiteInstance();
if (!current->IsRelatedSiteInstance(pending_entry_->site_instance()))
back_forward_cache_.Flush();
back_forward_cache_.EvictFramesInRelatedSiteInstances(
pending_entry_->site_instance());
}
// If we were navigating to a slow-to-commit page, and the user performs
......
......@@ -4766,6 +4766,7 @@ Unknown properties are collapsed to zero. -->
<int value="17" label="Dialog"/>
<int value="18" label="Granted media stream access"/>
<int value="19" label="Scheduler tracked feature used"/>
<int value="20" label="Conflicting BrowsingInstance"/>
</enum>
<enum name="BackForwardNavigationType">
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