Add support for Flash Player Component updates on Linux

This change adds support for Flash Player Component updates on Linux.
It enables the component updater on Linux, and creates a hints file that
the zygote will use to locate and preload the latest component updated flash.

This should not affect component update OS X and Windows, however, because
it forks the update path on Linux from those platforms, there is a risk of a bug
that could affect component update on OS X and Windows.

BUG=460595

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

Cr-Commit-Position: refs/heads/master@{#343435}
diff --git a/chrome/browser/component_updater/pepper_flash_component_installer.cc b/chrome/browser/component_updater/pepper_flash_component_installer.cc
index 2ebbb31..dffeaed 100644
--- a/chrome/browser/component_updater/pepper_flash_component_installer.cc
+++ b/chrome/browser/component_updater/pepper_flash_component_installer.cc
@@ -15,6 +15,7 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/path_service.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -25,6 +26,7 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/component_flash_hint_file_linux.h"
 #include "chrome/common/pepper_flash.h"
 #include "chrome/common/ppapi_utils.h"
 #include "components/component_updater/component_updater_service.h"
@@ -43,7 +45,7 @@
 
 namespace {
 
-#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX)
+#if defined(GOOGLE_CHROME_BUILD)
 // CRX hash. The extension id is: mimojjlkmoijpicakmndhoigimigcmbb.
 const uint8_t kSha2Hash[] = {0xc8, 0xce, 0x99, 0xba, 0xce, 0x89, 0xf8, 0x20,
                              0xac, 0xd3, 0x7e, 0x86, 0x8c, 0x86, 0x2c, 0x11,
@@ -53,7 +55,7 @@
 // If we don't have a Pepper Flash component, this is the version we claim.
 const char kNullVersion[] = "0.0.0.0";
 
-#endif  // defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX)
+#endif  // defined(GOOGLE_CHROME_BUILD)
 
 // The base directory on Windows looks like:
 // <profile>\AppData\Local\Google\Chrome\User Data\PepperFlash\.
@@ -63,7 +65,7 @@
   return result;
 }
 
-#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX)
+#if defined(GOOGLE_CHROME_BUILD)
 // Pepper Flash plugins have the version encoded in the path itself
 // so we need to enumerate the directories to find the full path.
 // On success, |latest_dir| returns something like:
@@ -99,8 +101,9 @@
   }
   return found;
 }
-#endif  // defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX)
+#endif  // defined(GOOGLE_CHROME_BUILD)
 
+#if !defined(OS_LINUX) || defined(GOOGLE_CHROME_BUILD)
 bool MakePepperFlashPluginInfo(const base::FilePath& flash_path,
                                const Version& flash_version,
                                bool out_of_process,
@@ -177,6 +180,7 @@
       plugin_info.ToWebPluginInfo(), true);
   PluginService::GetInstance()->RefreshPlugins();
 }
+#endif  // !defined(OS_LINUX) || defined(GOOGLE_CHROME_BUILD)
 
 }  // namespace
 
@@ -219,24 +223,42 @@
     return false;
   if (current_version_.CompareTo(version) > 0)
     return false;
-  if (!base::PathExists(unpack_path.Append(chrome::kPepperFlashPluginFilename)))
+  const base::FilePath unpacked_plugin =
+      unpack_path.Append(chrome::kPepperFlashPluginFilename);
+  if (!base::PathExists(unpacked_plugin))
     return false;
   // Passed the basic tests. Time to install it.
   base::FilePath path =
       GetPepperFlashBaseDirectory().AppendASCII(version.GetString());
   if (base::PathExists(path))
     return false;
+  current_version_ = version;
+
   if (!base::Move(unpack_path, path))
     return false;
+#if defined(OS_LINUX)
+  const base::FilePath flash_path =
+      path.Append(chrome::kPepperFlashPluginFilename);
+  // Populate the component updated flash hint file so that the zygote can
+  // locate and preload the latest version of flash.
+  if (!chrome::component_flash_hint_file::RecordFlashUpdate(
+          flash_path, flash_path, version.GetString())) {
+    if (!base::DeleteFile(path, true))
+      LOG(ERROR) << "Hint file creation failed, but unable to delete "
+                    "installed flash plugin.";
+    return false;
+  }
+#else
   // Installation is done. Now tell the rest of chrome. Both the path service
-  // and to the plugin service.
-  current_version_ = version;
+  // and to the plugin service. On Linux, a restart is required to use the new
+  // Flash version, so we do not do this.
   PathService::Override(chrome::DIR_PEPPER_FLASH_PLUGIN, path);
   path = path.Append(chrome::kPepperFlashPluginFilename);
   BrowserThread::PostTask(
       BrowserThread::UI,
       FROM_HERE,
       base::Bind(&RegisterPepperFlashWithChrome, path, version));
+#endif  // !defined(OS_LINUX)
   return true;
 }
 
@@ -254,7 +276,7 @@
 
 namespace {
 
-#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX)
+#if defined(GOOGLE_CHROME_BUILD)
 void FinishPepperFlashUpdateRegistration(ComponentUpdateService* cus,
                                          const Version& version) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -311,12 +333,12 @@
     base::DeleteFile(*iter, true);
   }
 }
-#endif  // defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX)
+#endif  // defined(GOOGLE_CHROME_BUILD)
 
 }  // namespace
 
 void RegisterPepperFlashComponent(ComponentUpdateService* cus) {
-#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_LINUX)
+#if defined(GOOGLE_CHROME_BUILD)
   // Component updated flash supersedes bundled flash therefore if that one
   // is disabled then this one should never install.
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
@@ -325,7 +347,7 @@
   BrowserThread::PostTask(BrowserThread::FILE,
                           FROM_HERE,
                           base::Bind(&StartPepperFlashUpdateRegistration, cus));
-#endif
+#endif  // defined(GOOGLE_CHROME_BUILD)
 }
 
 }  // namespace component_updater
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index ed1a287..603c9c26 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -43,6 +43,8 @@
       'common/common_param_traits.cc',
       'common/common_param_traits.h',
       'common/common_param_traits_macros.h',
+      'common/component_flash_hint_file_linux.h',
+      'common/component_flash_hint_file_linux.cc',
       'common/content_restriction.h',
       'common/content_settings_pattern_serializer.cc',
       'common/content_settings_pattern_serializer.h',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index c288dee..66d0ed8 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -517,6 +517,7 @@
       'common/chrome_content_client_unittest.cc',
       'common/chrome_paths_unittest.cc',
       'common/cloud_print/cloud_print_helpers_unittest.cc',
+      'common/component_flash_hint_file_linux_unittest.cc',
       'common/crash_keys_unittest.cc',
       'common/ini_parser_unittest.cc',
       'common/instant_types_unittest.cc',
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index 5fc72b7..f3b14b3 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -4,10 +4,15 @@
 
 #include "chrome/common/chrome_content_client.h"
 
+#if defined(OS_LINUX)
+#include <fcntl.h>
+#endif  // defined(OS_LINUX)
+
 #include "base/command_line.h"
 #include "base/debug/crash_logging.h"
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
+#include "base/memory/scoped_vector.h"
 #include "base/path_service.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
@@ -20,6 +25,7 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/component_flash_hint_file_linux.h"
 #include "chrome/common/crash_keys.h"
 #include "chrome/common/pepper_flash.h"
 #include "chrome/common/secure_origin_whitelist.h"
@@ -233,6 +239,50 @@
       CreatePepperFlashInfo(base::FilePath(flash_path), flash_version));
 }
 
+#if defined(OS_LINUX)
+bool IsUserDataDirAvailable() {
+  base::FilePath user_data_dir;
+  if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
+    return false;
+  return base::PathExists(user_data_dir);
+}
+
+// This method is used on Linux only because of architectural differences in how
+// it loads the component updated flash plugin, and not because the other
+// platforms do not support component updated flash. On other platforms, the
+// component updater sends an IPC message to all threads, at undefined points in
+// time, with the URL of the component updated flash. Because the linux zygote
+// thread has no access to the file system after it warms up, it must preload
+// the component updated flash.
+bool GetComponentUpdatedPepperFlash(content::PepperPluginInfo* plugin) {
+#if defined(FLAPPER_AVAILABLE)
+  if (chrome::component_flash_hint_file::DoesHintFileExist()) {
+    base::FilePath flash_path;
+    std::string version;
+    if (chrome::component_flash_hint_file::VerifyAndReturnFlashLocation(
+            &flash_path, &version)) {
+      // Test if the file can be mapped as executable. If the user's home
+      // directory is mounted noexec, the component flash plugin will not load.
+      // By testing for this, Chrome can fallback to the bundled flash plugin.
+      if (!chrome::component_flash_hint_file::TestExecutableMapping(
+              flash_path)) {
+        LOG(WARNING) << "The component updated flash plugin could not be "
+                        "mapped as executable. Attempting to fallback to the "
+                        "bundled or system plugin.";
+        return false;
+      }
+      *plugin = CreatePepperFlashInfo(flash_path, version);
+      return true;
+    } else {
+      LOG(ERROR)
+          << "Failed to locate and load the component updated flash plugin.";
+    }
+  }
+#endif  // defined(FLAPPER_AVAILABLE)
+  return false;
+}
+#endif  // defined(OS_LINUX)
+
 bool GetBundledPepperFlash(content::PepperPluginInfo* plugin) {
 #if defined(FLAPPER_AVAILABLE)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
@@ -397,18 +447,65 @@
 #endif
 }
 
+#if defined(ENABLE_PLUGINS)
+// static
+content::PepperPluginInfo* ChromeContentClient::FindMostRecentPlugin(
+    const std::vector<content::PepperPluginInfo*>& plugins) {
+  content::PepperPluginInfo* result = nullptr;
+  auto it = std::max_element(
+      plugins.begin(), plugins.end(),
+      [](content::PepperPluginInfo* x, content::PepperPluginInfo* y) {
+        Version version_x(x->version);
+        DCHECK(version_x.IsValid());
+        return version_x.IsOlderThan(y->version);
+      });
+  if (it != plugins.end())
+    result = *it;
+  return result;
+}
+#endif  // defined(ENABLE_PLUGINS)
+
 void ChromeContentClient::AddPepperPlugins(
     std::vector<content::PepperPluginInfo>* plugins) {
 #if defined(ENABLE_PLUGINS)
   ComputeBuiltInPlugins(plugins);
   AddPepperFlashFromCommandLine(plugins);
 
-  content::PepperPluginInfo plugin;
-  if (GetBundledPepperFlash(&plugin))
-    plugins->push_back(plugin);
-  if (GetSystemPepperFlash(&plugin))
-    plugins->push_back(plugin);
-#endif
+#if defined(OS_LINUX)
+  // Depending on the sandbox configurtion, the user data directory
+  // is not always available. If it is not available, do not try and load any
+  // flash plugin. The flash player, if any, preloaded before the sandbox
+  // initialization will continue to be used.
+  if (!IsUserDataDirAvailable()) {
+    return;
+  }
+#endif  // defined(OS_LINUX)
+
+  ScopedVector<content::PepperPluginInfo> flash_versions;
+
+#if defined(OS_LINUX)
+  scoped_ptr<content::PepperPluginInfo> component_flash(
+      new content::PepperPluginInfo);
+  if (GetComponentUpdatedPepperFlash(component_flash.get()))
+    flash_versions.push_back(component_flash.release());
+#endif  // defined(OS_LINUX)
+
+  scoped_ptr<content::PepperPluginInfo> bundled_flash(
+      new content::PepperPluginInfo);
+  if (GetBundledPepperFlash(bundled_flash.get()))
+    flash_versions.push_back(bundled_flash.release());
+
+  scoped_ptr<content::PepperPluginInfo> system_flash(
+      new content::PepperPluginInfo);
+  if (GetSystemPepperFlash(system_flash.get()))
+    flash_versions.push_back(system_flash.release());
+
+  // This function will return only the most recent version of the flash plugin.
+  content::PepperPluginInfo* max_flash =
+      FindMostRecentPlugin(flash_versions.get());
+  if (max_flash != nullptr)
+    plugins->push_back(*max_flash);
+#endif  // defined(ENABLE_PLUGINS)
 }
 
 void ChromeContentClient::AddAdditionalSchemes(
diff --git a/chrome/common/chrome_content_client.h b/chrome/common/chrome_content_client.h
index 8b77d22..0b5d516 100644
--- a/chrome/common/chrome_content_client.h
+++ b/chrome/common/chrome_content_client.h
@@ -41,6 +41,13 @@
       content::PepperPluginInfo::GetInterfaceFunc get_interface,
       content::PepperPluginInfo::PPP_InitializeModuleFunc initialize_module,
       content::PepperPluginInfo::PPP_ShutdownModuleFunc shutdown_module);
+
+  // This returns the most recent plugin based on the plugin versions.
+  // It does not make sense to call this on a vector that contains more than one
+  // plugin type. This function may return a nullptr if given an empty vector.
+  // The method is only visible for testing purposes.
+  static content::PepperPluginInfo* FindMostRecentPlugin(
+      const std::vector<content::PepperPluginInfo*>& plugins);
 #endif
 
   void SetActiveURL(const GURL& url) override;
diff --git a/chrome/common/chrome_content_client_unittest.cc b/chrome/common/chrome_content_client_unittest.cc
index 42514d7..5fa08bf 100644
--- a/chrome/common/chrome_content_client_unittest.cc
+++ b/chrome/common/chrome_content_client_unittest.cc
@@ -5,6 +5,8 @@
 #include "chrome/common/chrome_content_client.h"
 
 #include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
 #include "base/strings/string_split.h"
 #include "content/public/common/content_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -81,4 +83,42 @@
 #endif
 }
 
+#if defined(ENABLE_PLUGINS)
+TEST(ChromeContentClientTest, FindMostRecent) {
+  ScopedVector<content::PepperPluginInfo> vector1;
+  // Test an empty vector.
+  EXPECT_EQ(ChromeContentClient::FindMostRecentPlugin(vector1.get()), nullptr);
+
+  // Now test the vector with one element.
+  content::PepperPluginInfo* info1 = new content::PepperPluginInfo();
+  info1->version = "1.0.0.0";
+  vector1.push_back(info1);
+  EXPECT_EQ(ChromeContentClient::FindMostRecentPlugin(vector1.get()), info1);
+
+  // Now do the generic test of a complex vector.
+  content::PepperPluginInfo* info2 = new content::PepperPluginInfo();
+  info2->version = "2.0.0.1";
+  content::PepperPluginInfo* info3 = new content::PepperPluginInfo();
+  info3->version = "3.5.6.7";
+  content::PepperPluginInfo* info4 = new content::PepperPluginInfo();
+  info4->version = "4.0.0.153";
+  content::PepperPluginInfo* info5 = new content::PepperPluginInfo();
+  info5->version = "5.0.12.1";
+  content::PepperPluginInfo* info6_12 = new content::PepperPluginInfo();
+  info6_12->version = "6.0.0.12";
+  content::PepperPluginInfo* info6_13 = new content::PepperPluginInfo();
+  info6_13->version = "6.0.0.13";
+
+  ScopedVector<content::PepperPluginInfo> vector2;
+  vector2.push_back(info4);
+  vector2.push_back(info2);
+  vector2.push_back(info6_13);
+  vector2.push_back(info3);
+  vector2.push_back(info5);
+  vector2.push_back(info6_12);
+
+  EXPECT_EQ(ChromeContentClient::FindMostRecentPlugin(vector2.get()), info6_13);
+}
+#endif  // defined(ENABLE_PLUGINS)
+
 }  // namespace chrome_common
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index 74bf041..0da194d 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -60,6 +60,11 @@
 #else
     FILE_PATH_LITERAL("/usr/share/chromium/extensions");
 #endif  // defined(GOOGLE_CHROME_BUILD)
+
+// The path to the hint file that tells the pepper plugin loader
+// where it can find the latest component updated flash.
+const base::FilePath::CharType kComponentUpdatedFlashHint[] =
+    FILE_PATH_LITERAL("latest-component-updated-flash");
 #endif  // defined(OS_LINUX)
 
 static base::LazyInstance<base::FilePath>
@@ -566,6 +571,14 @@
       cur = cur.Append(kOfflinePageMetadataDirname);
       break;
 #endif  // defined(OS_ANDROID)
+#if defined(OS_LINUX)
+    case chrome::FILE_COMPONENT_FLASH_HINT:
+      if (!PathService::Get(chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN,
+                            &cur))
+        return false;
+      cur = cur.Append(kComponentUpdatedFlashHint);
+      break;
+#endif  // defined(OS_LINUX)
 
     default:
       return false;
diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h
index 95ef853..14fcc08 100644
--- a/chrome/common/chrome_paths.h
+++ b/chrome/common/chrome_paths.h
@@ -133,6 +133,10 @@
   DIR_GEN_TEST_DATA,            // Directory where generated test data resides.
   DIR_TEST_DATA,                // Directory where unit test data resides.
   DIR_TEST_TOOLS,               // Directory where unit test tools reside.
+#if defined(OS_LINUX)
+  FILE_COMPONENT_FLASH_HINT,    // A file in a known location that points to
+                                // the component updated flash plugin.
+#endif // defined(OS_LINUX)
 
   PATH_END
 };
diff --git a/chrome/common/component_flash_hint_file_linux.cc b/chrome/common/component_flash_hint_file_linux.cc
new file mode 100644
index 0000000..3d6c851
--- /dev/null
+++ b/chrome/common/component_flash_hint_file_linux.cc
@@ -0,0 +1,225 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/component_flash_hint_file_linux.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "base/base64.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/important_file_writer.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/files/scoped_file.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "chrome/common/chrome_paths.h"
+#include "crypto/secure_hash.h"
+#include "crypto/secure_util.h"
+#include "crypto/sha2.h"
+
+namespace chrome {
+
+namespace component_flash_hint_file {
+
+namespace {
+// The current version of the hints file.
+const int kCurrentHintFileVersion = 0x10;
+// The earliest version of the hints file.
+const int kEarliestHintFileVersion = 0x10;
+// The Version field in the JSON encoded file.
+const char kVersionField[] = "Version";
+// The HashAlgorithm field in the JSON encoded file.
+const char kHashAlgoField[] = "HashAlgorithm";
+// The Hash field in the JSON encoded file.
+const char kHashField[] = "Hash";
+// The PluginPath field in the JSON encoded file.
+const char kPluginPath[] = "PluginPath";
+// The PluginVersion field in the JSON encoded file.
+const char kPluginVersion[] = "PluginVersion";
+// For use with the scoped_ptr of an mmap-ed buffer
+struct MmapDeleter {
+  explicit MmapDeleter(size_t map_size) { map_size = map_size_; }
+  inline void operator()(uint8_t* ptr) const {
+    if (ptr != MAP_FAILED)
+      munmap(ptr, map_size_);
+  }
+
+ private:
+  size_t map_size_;
+};
+
+// Hashes the plugin file and returns the result in the out params.
+// |mapped_file| is the file to be hashed.
+// |result| is the buffer, which must be of size crypto::kSHA256Length, which
+// will contain the hash.
+// |len| is the size of the buffer, which must be crypto::kSHA256Length.
+void SHA256Hash(const base::MemoryMappedFile& mapped_file,
+                void* result,
+                size_t len) {
+  CHECK_EQ(crypto::kSHA256Length, len);
+  scoped_ptr<crypto::SecureHash> secure_hash(
+      crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+  secure_hash->Update(mapped_file.data(), mapped_file.length());
+  secure_hash->Finish(result, len);
+}
+
+// This will serialize the file to disk as JSON. The format is:
+// {
+//  "Version": 0x10,
+//  "HashAlgorithm": SecureHash::SHA256,
+//  "Hash": <Base64 Encoded Hash>,
+//  "PluginPath": /path/to/component/updated/flash.so,
+//  "PluginVersion": "1.0.0.1"
+//  }
+bool WriteToDisk(const int version,
+                 const crypto::SecureHash::Algorithm algorithm,
+                 const std::string& hash,
+                 const base::FilePath& plugin_path,
+                 const std::string& flash_version) {
+  base::FilePath hint_file_path;
+  if (!PathService::Get(chrome::FILE_COMPONENT_FLASH_HINT, &hint_file_path))
+    return false;
+
+  std::string encoded_hash;
+  base::Base64Encode(hash, &encoded_hash);
+
+  // Now construct a Value object to convert to JSON.
+  base::DictionaryValue dict;
+  dict.SetInteger(kVersionField, version);
+  dict.SetInteger(kHashAlgoField, crypto::SecureHash::SHA256);
+  dict.SetString(kHashField, encoded_hash);
+  dict.SetString(kPluginPath, plugin_path.value());
+  dict.SetString(kPluginVersion, flash_version);
+  // Do the serialization of the DictionaryValue to JSON.
+  std::string json_string;
+  JSONStringValueSerializer serializer(&json_string);
+  if (!serializer.Serialize(dict))
+    return false;
+
+  return base::ImportantFileWriter::WriteFileAtomically(hint_file_path,
+                                                        json_string);
+}
+
+}  // namespace
+
+bool TestExecutableMapping(const base::FilePath& path) {
+  const base::ScopedFD fd(
+      HANDLE_EINTR(open(path.value().c_str(), O_RDONLY | O_CLOEXEC)));
+  if (!fd.is_valid())
+    return false;
+  const size_t map_size = sizeof(uint8_t);
+  const MmapDeleter deleter(map_size);
+  scoped_ptr<uint8_t, MmapDeleter> buf_ptr(
+      reinterpret_cast<uint8_t*>(mmap(nullptr, map_size, PROT_READ | PROT_EXEC,
+                                      MAP_PRIVATE, fd.get(), 0)),
+      deleter);
+  return buf_ptr.get() != MAP_FAILED;
+}
+
+bool RecordFlashUpdate(const base::FilePath& unpacked_plugin,
+                       const base::FilePath& moved_plugin,
+                       const std::string& version) {
+  base::MemoryMappedFile mapped_file;
+  if (!mapped_file.Initialize(unpacked_plugin))
+    return false;
+
+  std::string hash(crypto::kSHA256Length, 0);
+  SHA256Hash(mapped_file, string_as_array(&hash), hash.size());
+
+  return WriteToDisk(kCurrentHintFileVersion,
+                     crypto::SecureHash::Algorithm::SHA256, hash, moved_plugin,
+                     version);
+}
+
+bool DoesHintFileExist() {
+  base::FilePath hint_file_path;
+  if (!PathService::Get(chrome::FILE_COMPONENT_FLASH_HINT, &hint_file_path))
+    return false;
+  return base::PathExists(hint_file_path);
+}
+
+bool VerifyAndReturnFlashLocation(base::FilePath* path,
+                                  std::string* flash_version) {
+  base::FilePath hint_file_path;
+  if (!PathService::Get(chrome::FILE_COMPONENT_FLASH_HINT, &hint_file_path))
+    return false;
+
+  std::string json_string;
+  if (!base::ReadFileToString(hint_file_path, &json_string))
+    return false;
+
+  int error_code;
+  std::string error_message;
+  JSONStringValueDeserializer deserializer(json_string);
+  const scoped_ptr<base::Value> value(
+      deserializer.Deserialize(&error_code, &error_message));
+
+  if (!value) {
+    LOG(ERROR)
+        << "Could not deserialize the component updated flash hint file. Error "
+        << error_code << ": " << error_message;
+    return false;
+  }
+
+  base::DictionaryValue* dict = nullptr;
+  if (!value->GetAsDictionary(&dict))
+    return false;
+
+  int version;
+  if (!dict->GetInteger(kVersionField, &version))
+    return false;
+  if (version < kEarliestHintFileVersion || version > kCurrentHintFileVersion)
+    return false;
+
+  int hash_algorithm;
+  if (!dict->GetInteger(kHashAlgoField, &hash_algorithm))
+    return false;
+  if (hash_algorithm != crypto::SecureHash::SHA256)
+    return false;
+
+  std::string hash;
+  if (!dict->GetString(kHashField, &hash))
+    return false;
+
+  std::string plugin_path_str;
+  if (!dict->GetString(kPluginPath, &plugin_path_str))
+    return false;
+
+  std::string plugin_version_str;
+  if (!dict->GetString(kPluginVersion, &plugin_version_str))
+    return false;
+
+  std::string decoded_hash;
+  if (!base::Base64Decode(hash, &decoded_hash))
+    return false;
+
+  const base::FilePath plugin_path(plugin_path_str);
+  base::MemoryMappedFile plugin_file;
+  if (!plugin_file.Initialize(plugin_path))
+    return false;
+
+  std::vector<uint8_t> file_hash(crypto::kSHA256Length, 0);
+  SHA256Hash(plugin_file, &file_hash[0], file_hash.size());
+  if (!crypto::SecureMemEqual(&file_hash[0], string_as_array(&decoded_hash),
+                              crypto::kSHA256Length)) {
+    LOG(ERROR)
+        << "The hash recorded in the component flash hint file does not "
+           "match the actual hash of the flash plugin found on disk. The "
+           "component flash plugin will not be loaded.";
+    return false;
+  }
+
+  *path = plugin_path;
+  flash_version->assign(plugin_version_str);
+  return true;
+}
+
+}  // namespace component_flash_hint_file
+
+}  // namespace chrome
diff --git a/chrome/common/component_flash_hint_file_linux.h b/chrome/common/component_flash_hint_file_linux.h
new file mode 100644
index 0000000..abe0701
--- /dev/null
+++ b/chrome/common/component_flash_hint_file_linux.h
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_COMPONENT_FLASH_HINT_FILE_LINUX_H_
+#define CHROME_COMMON_COMPONENT_FLASH_HINT_FILE_LINUX_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_LINUX)
+
+#include <string>
+
+namespace base {
+class FilePath;
+}
+
+namespace chrome {
+
+// The APIs in this namespace wraps the component updated flash hint file, which
+// lives inside the PepperFlash folder of  the user-data-dir, so that the Linux
+// zygote process can preload the right version of flash.
+namespace component_flash_hint_file {
+
+// Records a new flash update into the hint file.
+// |unpacked_plugin| is the current location of the plugin.
+// |moved_plugin| is the location where the plugin will be loaded from.
+bool RecordFlashUpdate(const base::FilePath& unpacked_plugin,
+                       const base::FilePath& moved_plugin,
+                       const std::string& version);
+
+// Reports whether or not a hints file exists.
+bool DoesHintFileExist();
+
+// Return the path of the component updated flash plugin, only if the file has
+// the correct hash sum.
+// |path| will be populated with the path to the flash plugin.
+// |version| will be populated with the version of the flash plugin.
+bool VerifyAndReturnFlashLocation(base::FilePath* path, std::string* version);
+
+// Test if the specified plugin file can be mapped executable.
+// This is useful to test if the flash plugin is in a directory mounted
+// noexec, in which case Chrome will not be able to load and use the plugin.
+// |path| is the path of the flash plugin that will mapped executable.
+bool TestExecutableMapping(const base::FilePath& path);
+
+}  // namespace component_flash_hint_file
+
+}  // namespace chrome
+
+#endif  // defined(OS_LINUX)
+#endif  // CHROME_COMMON_COMPONENT_FLASH_HINT_FILE_LINUX_H_
diff --git a/chrome/common/component_flash_hint_file_linux_unittest.cc b/chrome/common/component_flash_hint_file_linux_unittest.cc
new file mode 100644
index 0000000..52a7a31
--- /dev/null
+++ b/chrome/common/component_flash_hint_file_linux_unittest.cc
@@ -0,0 +1,167 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/component_flash_hint_file_linux.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/process/kill.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/scoped_path_override.h"
+#include "base/test/test_timeouts.h"
+#include "chrome/common/chrome_paths.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace chrome {
+
+class ComponentFlashHintFileTest : public base::MultiProcessTest {};
+
+TEST_F(ComponentFlashHintFileTest, ExistsTest) {
+  const base::ScopedPathOverride path_override(chrome::DIR_USER_DATA);
+  EXPECT_FALSE(component_flash_hint_file::DoesHintFileExist());
+}
+
+TEST_F(ComponentFlashHintFileTest, InstallTest) {
+  const base::ScopedPathOverride path_override(chrome::DIR_USER_DATA);
+  EXPECT_FALSE(component_flash_hint_file::DoesHintFileExist());
+
+  base::FilePath flash_dir;
+  ASSERT_TRUE(PathService::Get(
+      chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN, &flash_dir));
+
+  base::File::Error error;
+  ASSERT_TRUE(base::CreateDirectoryAndGetError(flash_dir, &error));
+
+  // Write out a fixed byte array as the flash file.
+  uint8_t file[] = {0x4c, 0x65, 0x74, 0x20, 0x75, 0x73,
+                    0x20, 0x6e, 0x6f, 0x74, 0x20, 0x67};
+  flash_dir = flash_dir.Append("libflash.so");
+  const std::string flash_version = "1.0.0.1";
+  ASSERT_EQ(static_cast<int>(sizeof(file)),
+            base::WriteFile(flash_dir, reinterpret_cast<const char*>(file),
+                            sizeof(file)));
+  ASSERT_TRUE(component_flash_hint_file::RecordFlashUpdate(flash_dir, flash_dir,
+                                                           flash_version));
+  ASSERT_TRUE(component_flash_hint_file::DoesHintFileExist());
+
+  // Confirm that the flash plugin can be verified and returned.
+  base::FilePath returned_flash_path;
+  std::string version;
+  ASSERT_TRUE(component_flash_hint_file::VerifyAndReturnFlashLocation(
+      &returned_flash_path, &version));
+  ASSERT_EQ(returned_flash_path, flash_dir);
+  ASSERT_EQ(version, flash_version);
+
+  // Now "corrupt" the flash file and make sure the checksum fails and nothing
+  // is returned.
+  file[0] = 0xAA;
+  ASSERT_TRUE(base::WriteFile(flash_dir, reinterpret_cast<const char*>(file),
+                              sizeof(file)) == sizeof(file));
+  base::FilePath empty_path;
+  std::string empty_version;
+  ASSERT_FALSE(component_flash_hint_file::VerifyAndReturnFlashLocation(
+      &empty_path, &empty_version));
+  ASSERT_NE(empty_path, flash_dir);
+  ASSERT_FALSE(empty_version == flash_version);
+}
+
+TEST_F(ComponentFlashHintFileTest, CorruptionTest) {
+  const base::ScopedPathOverride path_override(chrome::DIR_USER_DATA);
+  EXPECT_FALSE(component_flash_hint_file::DoesHintFileExist());
+
+  base::FilePath flash_dir;
+  ASSERT_TRUE(PathService::Get(
+      chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN, &flash_dir));
+
+  base::File::Error error;
+  ASSERT_TRUE(base::CreateDirectoryAndGetError(flash_dir, &error));
+  flash_dir = flash_dir.Append("libflash.so");
+
+  const uint8_t file[] = {0x56, 0x61, 0x20, 0x67, 0x75, 0x76,
+                          0x66, 0x20, 0x62, 0x61, 0x72, 0x20};
+  ASSERT_TRUE(base::WriteFile(flash_dir, reinterpret_cast<const char*>(file),
+                              sizeof(file)) == sizeof(file));
+  const std::string flash_version = "1.0.0.1";
+  ASSERT_TRUE(component_flash_hint_file::RecordFlashUpdate(flash_dir, flash_dir,
+                                                           flash_version));
+  ASSERT_TRUE(component_flash_hint_file::DoesHintFileExist());
+
+  // Now write out a new flash version that will not be moved into place.
+  const uint8_t updated_file[] = {0x43, 0x72, 0x62, 0x63, 0x79, 0x72,
+                                  0x20, 0x66, 0x7a, 0x76, 0x79, 0x76};
+  base::FilePath flash_dir_update;
+  ASSERT_TRUE(PathService::Get(
+      chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN, &flash_dir_update));
+  flash_dir_update = flash_dir_update.Append("other_flash.so");
+  ASSERT_TRUE(base::WriteFile(flash_dir_update,
+                              reinterpret_cast<const char*>(updated_file),
+                              sizeof(updated_file)) == sizeof(updated_file));
+  ASSERT_TRUE(component_flash_hint_file::RecordFlashUpdate(
+      flash_dir_update, flash_dir, flash_version));
+  // |flash_dir_update| needs to be moved to |flash_dir|, but this test
+  // deliberately skips that step, so VerifyAndReturnFlashLocation should fail.
+  base::FilePath failed_flash_dir;
+  std::string failed_version;
+  ASSERT_FALSE(component_flash_hint_file::VerifyAndReturnFlashLocation(
+      &failed_flash_dir, &failed_version));
+}
+
+TEST_F(ComponentFlashHintFileTest, ExecTest1) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath file_path = temp_dir.path().Append("plugin.so");
+  const uint8_t file[] = {0x55, 0x62, 0x79, 0x71, 0x20,
+                          0x6c, 0x62, 0x68, 0x65, 0x20};
+
+  ASSERT_TRUE(base::WriteFile(file_path, reinterpret_cast<const char*>(file),
+                              sizeof(file)) == sizeof(file));
+  ASSERT_TRUE(component_flash_hint_file::TestExecutableMapping(file_path));
+}
+
+MULTIPROCESS_TEST_MAIN(NoExecMountTest) {
+  if (unshare(CLONE_NEWUSER | CLONE_NEWNS) != 0) {
+    LOG(ERROR) << "This kernel does not support unprivileged namespaces. "
+                  "ExecTest2 will succeed without running.";
+    return 0;
+  }
+  // Now mount a NOEXEC fs.
+  const unsigned long tmpfs_flags = MS_NODEV | MS_NOSUID | MS_NOEXEC;
+  base::ScopedTempDir temp_dir;
+  CHECK(temp_dir.CreateUniqueTempDir());
+  CHECK_EQ(0, mount("tmpfs", temp_dir.path().value().c_str(), "tmpfs",
+                    tmpfs_flags, nullptr));
+  const base::FilePath file_path = temp_dir.path().Append("plugin.so");
+  const uint8_t file[] = {0x56, 0x61, 0x20, 0x67, 0x75, 0x72,
+                          0x20, 0x70, 0x76, 0x67, 0x6c, 0x20};
+  bool test_exec = false;
+  bool file_written =
+      base::WriteFile(file_path, reinterpret_cast<const char*>(file),
+                      sizeof(file)) == static_cast<int>(sizeof(file));
+  if (file_written)
+    test_exec = component_flash_hint_file::TestExecutableMapping(file_path);
+
+  if (umount(temp_dir.path().value().c_str()) != 0)
+    LOG(ERROR) << "Could not unmount directory " << temp_dir.path().value();
+
+  CHECK(file_written);
+  CHECK(!test_exec);
+  return 0;
+}
+
+TEST_F(ComponentFlashHintFileTest, ExecTest2) {
+  base::Process process = SpawnChild("NoExecMountTest");
+  ASSERT_TRUE(process.IsValid());
+  int exit_code = 42;
+  ASSERT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
+                                             &exit_code));
+  EXPECT_EQ(0, exit_code);
+}
+
+}  // namespace chrome