Fix possible map::end() dereference in AppCacheUpdateJob triggered by a compromised renderer.

BUG=551044

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

Cr-Commit-Position: refs/heads/master@{#358815}
diff --git a/content/browser/appcache/appcache_backend_impl.cc b/content/browser/appcache/appcache_backend_impl.cc
index e055f1a..01eed000 100644
--- a/content/browser/appcache/appcache_backend_impl.cc
+++ b/content/browser/appcache/appcache_backend_impl.cc
@@ -68,32 +68,29 @@
     const int64 cache_document_was_loaded_from,
     const GURL& manifest_url) {
   AppCacheHost* host = GetHost(host_id);
-  if (!host || host->was_select_cache_called())
+  if (!host)
     return false;
 
-  host->SelectCache(document_url, cache_document_was_loaded_from,
+  return host->SelectCache(document_url, cache_document_was_loaded_from,
                     manifest_url);
-  return true;
 }
 
 bool AppCacheBackendImpl::SelectCacheForWorker(
     int host_id, int parent_process_id, int parent_host_id) {
   AppCacheHost* host = GetHost(host_id);
-  if (!host || host->was_select_cache_called())
+  if (!host)
     return false;
 
-  host->SelectCacheForWorker(parent_process_id, parent_host_id);
-  return true;
+  return host->SelectCacheForWorker(parent_process_id, parent_host_id);
 }
 
 bool AppCacheBackendImpl::SelectCacheForSharedWorker(
     int host_id, int64 appcache_id) {
   AppCacheHost* host = GetHost(host_id);
-  if (!host || host->was_select_cache_called())
+  if (!host)
     return false;
 
-  host->SelectCacheForSharedWorker(appcache_id);
-  return true;
+  return host->SelectCacheForSharedWorker(appcache_id);
 }
 
 bool AppCacheBackendImpl::MarkAsForeignEntry(
@@ -104,8 +101,7 @@
   if (!host)
     return false;
 
-  host->MarkAsForeignEntry(document_url, cache_document_was_loaded_from);
-  return true;
+  return host->MarkAsForeignEntry(document_url, cache_document_was_loaded_from);
 }
 
 bool AppCacheBackendImpl::GetStatusWithCallback(
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
index dbdaf308..9ec45f61 100644
--- a/content/browser/appcache/appcache_host.cc
+++ b/content/browser/appcache/appcache_host.cc
@@ -80,18 +80,21 @@
   observers_.RemoveObserver(observer);
 }
 
-void AppCacheHost::SelectCache(const GURL& document_url,
+bool AppCacheHost::SelectCache(const GURL& document_url,
                                const int64 cache_document_was_loaded_from,
                                const GURL& manifest_url) {
+  if (was_select_cache_called_)
+    return false;
+
   DCHECK(pending_start_update_callback_.is_null() &&
          pending_swap_cache_callback_.is_null() &&
          pending_get_status_callback_.is_null() &&
-         !is_selection_pending() && !was_select_cache_called_);
+         !is_selection_pending());
 
   was_select_cache_called_ = true;
   if (!is_cache_selection_enabled_) {
     FinishCacheSelection(NULL, NULL);
-    return;
+    return true;
   }
 
   origin_in_use_ = document_url.GetOrigin();
@@ -111,7 +114,7 @@
 
   if (cache_document_was_loaded_from != kAppCacheNoCacheId) {
     LoadSelectedCache(cache_document_was_loaded_from);
-    return;
+    return true;
   }
 
   if (!manifest_url.is_empty() &&
@@ -132,7 +135,7 @@
               0,
               false /*is_cross_origin*/));
       frontend_->OnContentBlocked(host_id_, manifest_url);
-      return;
+      return true;
     }
 
     // Note: The client detects if the document was not loaded using HTTP GET
@@ -141,49 +144,62 @@
     set_preferred_manifest_url(manifest_url);
     new_master_entry_url_ = document_url;
     LoadOrCreateGroup(manifest_url);
-    return;
+    return true;
   }
 
   // TODO(michaeln): If there was a manifest URL, the user agent may report
   // to the user that it was ignored, to aid in application development.
   FinishCacheSelection(NULL, NULL);
+  return true;
 }
 
-void AppCacheHost::SelectCacheForWorker(int parent_process_id,
+bool AppCacheHost::SelectCacheForWorker(int parent_process_id,
                                         int parent_host_id) {
+  if (was_select_cache_called_)
+    return false;
+
   DCHECK(pending_start_update_callback_.is_null() &&
          pending_swap_cache_callback_.is_null() &&
          pending_get_status_callback_.is_null() &&
-         !is_selection_pending() && !was_select_cache_called_);
+         !is_selection_pending());
 
   was_select_cache_called_ = true;
   parent_process_id_ = parent_process_id;
   parent_host_id_ = parent_host_id;
   FinishCacheSelection(NULL, NULL);
+  return true;
 }
 
-void AppCacheHost::SelectCacheForSharedWorker(int64 appcache_id) {
+bool AppCacheHost::SelectCacheForSharedWorker(int64 appcache_id) {
+  if (was_select_cache_called_)
+    return false;
+
   DCHECK(pending_start_update_callback_.is_null() &&
          pending_swap_cache_callback_.is_null() &&
          pending_get_status_callback_.is_null() &&
-         !is_selection_pending() && !was_select_cache_called_);
+         !is_selection_pending());
 
   was_select_cache_called_ = true;
   if (appcache_id != kAppCacheNoCacheId) {
     LoadSelectedCache(appcache_id);
-    return;
+    return true;
   }
   FinishCacheSelection(NULL, NULL);
+  return true;
 }
 
 // TODO(michaeln): change method name to MarkEntryAsForeign for consistency
-void AppCacheHost::MarkAsForeignEntry(const GURL& document_url,
+bool AppCacheHost::MarkAsForeignEntry(const GURL& document_url,
                                       int64 cache_document_was_loaded_from) {
+  if (was_select_cache_called_)
+    return false;
+
   // The document url is not the resource url in the fallback case.
   storage()->MarkEntryAsForeign(
       main_resource_was_namespace_entry_ ? namespace_entry_url_ : document_url,
       cache_document_was_loaded_from);
   SelectCache(document_url, kAppCacheNoCacheId, GURL());
+  return true;
 }
 
 void AppCacheHost::GetStatusWithCallback(const GetStatusCallback& callback,
diff --git a/content/browser/appcache/appcache_host.h b/content/browser/appcache/appcache_host.h
index 82029ba..3818f30 100644
--- a/content/browser/appcache/appcache_host.h
+++ b/content/browser/appcache/appcache_host.h
@@ -33,6 +33,7 @@
 FORWARD_DECLARE_TEST(AppCacheHostTest, ForDedicatedWorker);
 FORWARD_DECLARE_TEST(AppCacheHostTest, SelectCacheAllowed);
 FORWARD_DECLARE_TEST(AppCacheHostTest, SelectCacheBlocked);
+FORWARD_DECLARE_TEST(AppCacheHostTest, SelectCacheTwice);
 FORWARD_DECLARE_TEST(AppCacheTest, CleanupUnusedCache);
 class AppCache;
 class AppCacheFrontend;
@@ -76,13 +77,13 @@
   void RemoveObserver(Observer* observer);
 
   // Support for cache selection and scriptable method calls.
-  void SelectCache(const GURL& document_url,
+  bool SelectCache(const GURL& document_url,
                    const int64 cache_document_was_loaded_from,
                    const GURL& manifest_url);
-  void SelectCacheForWorker(int parent_process_id,
+  bool SelectCacheForWorker(int parent_process_id,
                             int parent_host_id);
-  void SelectCacheForSharedWorker(int64 appcache_id);
-  void MarkAsForeignEntry(const GURL& document_url,
+  bool SelectCacheForSharedWorker(int64 appcache_id);
+  bool MarkAsForeignEntry(const GURL& document_url,
                           int64 cache_document_was_loaded_from);
   void GetStatusWithCallback(const GetStatusCallback& callback,
                              void* callback_param);
@@ -163,7 +164,6 @@
   AppCacheStorage* storage() const { return storage_; }
   AppCacheFrontend* frontend() const { return frontend_; }
   AppCache* associated_cache() const { return associated_cache_.get(); }
-  bool was_select_cache_called() const { return was_select_cache_called_; }
 
   void enable_cache_selection(bool enable) {
     is_cache_selection_enabled_ = enable;
@@ -336,6 +336,7 @@
   FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, ForDedicatedWorker);
   FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, SelectCacheAllowed);
   FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, SelectCacheBlocked);
+  FRIEND_TEST_ALL_PREFIXES(content::AppCacheHostTest, SelectCacheTwice);
   FRIEND_TEST_ALL_PREFIXES(content::AppCacheTest, CleanupUnusedCache);
 
   DISALLOW_COPY_AND_ASSIGN(AppCacheHost);
diff --git a/content/browser/appcache/appcache_host_unittest.cc b/content/browser/appcache/appcache_host_unittest.cc
index 817e8c0..255bfa9 100644
--- a/content/browser/appcache/appcache_host_unittest.cc
+++ b/content/browser/appcache/appcache_host_unittest.cc
@@ -530,4 +530,17 @@
   service_.set_quota_manager_proxy(NULL);
 }
 
+TEST_F(AppCacheHostTest, SelectCacheTwice) {
+  AppCacheHost host(1, &mock_frontend_, &service_);
+  const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
+
+  EXPECT_TRUE(host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL()));
+
+  // Select methods should bail if cache has already been selected.
+  EXPECT_FALSE(host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL()));
+  EXPECT_FALSE(host.SelectCacheForWorker(0, 0));
+  EXPECT_FALSE(host.SelectCacheForSharedWorker(kAppCacheNoCacheId));
+  EXPECT_FALSE(host.MarkAsForeignEntry(kDocAndOriginUrl, kAppCacheNoCacheId));
+}
+
 }  // namespace content
diff --git a/content/browser/appcache/appcache_update_job.cc b/content/browser/appcache/appcache_update_job.cc
index 8720fe78..ff06550 100644
--- a/content/browser/appcache/appcache_update_job.cc
+++ b/content/browser/appcache/appcache_update_job.cc
@@ -1105,10 +1105,10 @@
   // The host is about to be deleted; remove from our collection.
   PendingMasters::iterator found =
       pending_master_entries_.find(host->pending_master_entry_url());
-  DCHECK(found != pending_master_entries_.end());
+  CHECK(found != pending_master_entries_.end());
   PendingHosts& hosts = found->second;
   PendingHosts::iterator it = std::find(hosts.begin(), hosts.end(), host);
-  DCHECK(it != hosts.end());
+  CHECK(it != hosts.end());
   hosts.erase(it);
 }