summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkerrnel <kerrnel@chromium.org>2015-08-14 11:15:56 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-14 18:16:23 +0000
commit2bb08b2a5f6bcc382ebaccf1b50bd655592dfe51 (patch)
treed61a74f71fe4083a9b3bedbb74e1d58dee6c8b20
parent8894e7a716085008961141302ecf6de887375ed7 (diff)
downloadchromium_src-2bb08b2a5f6bcc382ebaccf1b50bd655592dfe51.zip
chromium_src-2bb08b2a5f6bcc382ebaccf1b50bd655592dfe51.tar.gz
chromium_src-2bb08b2a5f6bcc382ebaccf1b50bd655592dfe51.tar.bz2
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}
-rw-r--r--chrome/browser/component_updater/pepper_flash_component_installer.cc44
-rw-r--r--chrome/chrome_common.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/common/chrome_content_client.cc109
-rw-r--r--chrome/common/chrome_content_client.h7
-rw-r--r--chrome/common/chrome_content_client_unittest.cc40
-rw-r--r--chrome/common/chrome_paths.cc13
-rw-r--r--chrome/common/chrome_paths.h4
-rw-r--r--chrome/common/component_flash_hint_file_linux.cc225
-rw-r--r--chrome/common/component_flash_hint_file_linux.h52
-rw-r--r--chrome/common/component_flash_hint_file_linux_unittest.cc167
11 files changed, 647 insertions, 17 deletions
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 component_updater {
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 @@ const uint8_t kSha2Hash[] = {0xc8, 0xce, 0x99, 0xba, 0xce, 0x89, 0xf8, 0x20,
// 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 @@ base::FilePath GetPepperFlashBaseDirectory() {
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 @@ bool GetPepperFlashDirectory(base::FilePath* latest_dir,
}
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 @@ void RegisterPepperFlashWithChrome(const base::FilePath& path,
plugin_info.ToWebPluginInfo(), true);
PluginService::GetInstance()->RefreshPlugins();
}
+#endif // !defined(OS_LINUX) || defined(GOOGLE_CHROME_BUILD)
} // namespace
@@ -219,24 +223,42 @@ bool PepperFlashComponentInstaller::Install(
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 @@ bool PepperFlashComponentInstaller::Uninstall() {
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 @@ void StartPepperFlashUpdateRegistration(ComponentUpdateService* cus) {
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 @@ void RegisterPepperFlashComponent(ComponentUpdateService* cus) {
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..603c9c2 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 @@ void AddPepperFlashFromCommandLine(
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 @@ void ChromeContentClient::SetGpuInfo(const gpu::GPUInfo& gpu_info) {
#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 @@ class ChromeContentClient : public content::ContentClient {
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 @@ TEST(ChromeContentClientTest, Basic) {
#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 @@ const base::FilePath::CharType kFilepathSinglePrefExtensions[] =
#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 @@ bool PathProvider(int key, base::FilePath* result) {
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 @@ enum {
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