diff options
Diffstat (limited to 'chromium/components/nacl/browser/nacl_browser.cc')
-rw-r--r-- | chromium/components/nacl/browser/nacl_browser.cc | 567 |
1 files changed, 0 insertions, 567 deletions
diff --git a/chromium/components/nacl/browser/nacl_browser.cc b/chromium/components/nacl/browser/nacl_browser.cc deleted file mode 100644 index b1f1236b7a2..00000000000 --- a/chromium/components/nacl/browser/nacl_browser.cc +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright 2013 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 "components/nacl/browser/nacl_browser.h" - -#include "base/command_line.h" -#include "base/file_util.h" -#include "base/message_loop/message_loop.h" -#include "base/metrics/histogram.h" -#include "base/path_service.h" -#include "base/pickle.h" -#include "base/rand_util.h" -#include "base/time/time.h" -#include "base/win/windows_version.h" -#include "build/build_config.h" -#include "content/public/browser/browser_thread.h" -#include "url/gurl.h" - -namespace { - -// An arbitrary delay to coalesce multiple writes to the cache. -const int kValidationCacheCoalescingTimeMS = 6000; -const char kValidationCacheSequenceName[] = "NaClValidationCache"; -const base::FilePath::CharType kValidationCacheFileName[] = - FILE_PATH_LITERAL("nacl_validation_cache.bin"); - -const bool kValidationCacheEnabledByDefault = true; - -enum ValidationCacheStatus { - CACHE_MISS = 0, - CACHE_HIT, - CACHE_MAX -}; - -// Keep the cache bounded to an arbitrary size. If it's too small, useful -// entries could be evicted when multiple .nexes are loaded at once. On the -// other hand, entries are not always claimed (and hence removed), so the size -// of the cache will likely saturate at its maximum size. -// Entries may not be claimed for two main reasons. 1) the NaCl process could -// be killed while it is loading. 2) the trusted NaCl plugin opens files using -// the code path but doesn't resolve them. -// TODO(ncbray) don't cache files that the plugin will not resolve. -const int kFilePathCacheSize = 100; - -const base::FilePath::StringType NaClIrtName() { - base::FilePath::StringType irt_name(FILE_PATH_LITERAL("nacl_irt_")); - -#if defined(ARCH_CPU_X86_FAMILY) -#if defined(ARCH_CPU_X86_64) - bool is64 = true; -#elif defined(OS_WIN) - bool is64 = (base::win::OSInfo::GetInstance()->wow64_status() == - base::win::OSInfo::WOW64_ENABLED); -#else - bool is64 = false; -#endif - if (is64) - irt_name.append(FILE_PATH_LITERAL("x86_64")); - else - irt_name.append(FILE_PATH_LITERAL("x86_32")); - -#elif defined(ARCH_CPU_ARMEL) - // TODO(mcgrathr): Eventually we'll need to distinguish arm32 vs thumb2. - // That may need to be based on the actual nexe rather than a static - // choice, which would require substantial refactoring. - irt_name.append(FILE_PATH_LITERAL("arm")); -#elif defined(ARCH_CPU_MIPSEL) - irt_name.append(FILE_PATH_LITERAL("mips32")); -#else -#error Add support for your architecture to NaCl IRT file selection -#endif - irt_name.append(FILE_PATH_LITERAL(".nexe")); - return irt_name; -} - -bool CheckEnvVar(const char* name, bool default_value) { - bool result = default_value; - const char* var = getenv(name); - if (var && strlen(var) > 0) { - result = var[0] != '0'; - } - return result; -} - -void ReadCache(const base::FilePath& filename, std::string* data) { - if (!base::ReadFileToString(filename, data)) { - // Zero-size data used as an in-band error code. - data->clear(); - } -} - -void WriteCache(const base::FilePath& filename, const Pickle* pickle) { - file_util::WriteFile(filename, static_cast<const char*>(pickle->data()), - pickle->size()); -} - -void RemoveCache(const base::FilePath& filename, - const base::Closure& callback) { - base::DeleteFile(filename, false); - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - callback); -} - -void LogCacheQuery(ValidationCacheStatus status) { - UMA_HISTOGRAM_ENUMERATION("NaCl.ValidationCache.Query", status, CACHE_MAX); -} - -void LogCacheSet(ValidationCacheStatus status) { - // Bucket zero is reserved for future use. - UMA_HISTOGRAM_ENUMERATION("NaCl.ValidationCache.Set", status, CACHE_MAX); -} - -// Crash throttling parameters. -const size_t kMaxCrashesPerInterval = 3; -const int64 kCrashesIntervalInSeconds = 120; - -} // namespace - -namespace nacl { - -base::PlatformFile OpenNaClExecutableImpl(const base::FilePath& file_path) { - // Get a file descriptor. On Windows, we need 'GENERIC_EXECUTE' in order to - // memory map the executable. - // IMPORTANT: This file descriptor must not have write access - that could - // allow a NaCl inner sandbox escape. - base::PlatformFile file; - base::PlatformFileError error_code; - file = base::CreatePlatformFile( - file_path, - (base::PLATFORM_FILE_OPEN | - base::PLATFORM_FILE_READ | - base::PLATFORM_FILE_EXECUTE), // Windows only flag. - NULL, - &error_code); - if (error_code != base::PLATFORM_FILE_OK) - return base::kInvalidPlatformFileValue; - - // Check that the file does not reference a directory. Returning a descriptor - // to an extension directory could allow an outer sandbox escape. openat(...) - // could be used to traverse into the file system. - base::PlatformFileInfo file_info; - if (!base::GetPlatformFileInfo(file, &file_info) || file_info.is_directory) { - base::ClosePlatformFile(file); - return base::kInvalidPlatformFileValue; - } - return file; -} - -NaClBrowser::NaClBrowser() - : weak_factory_(this), - irt_platform_file_(base::kInvalidPlatformFileValue), - irt_filepath_(), - irt_state_(NaClResourceUninitialized), - validation_cache_file_path_(), - validation_cache_is_enabled_( - CheckEnvVar("NACL_VALIDATION_CACHE", - kValidationCacheEnabledByDefault)), - validation_cache_is_modified_(false), - validation_cache_state_(NaClResourceUninitialized), - path_cache_(kFilePathCacheSize), - ok_(true) { -} - -void NaClBrowser::SetDelegate(NaClBrowserDelegate* delegate) { - NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); - nacl_browser->browser_delegate_.reset(delegate); -} - -NaClBrowserDelegate* NaClBrowser::GetDelegate() { - // The delegate is not owned by the IO thread. This accessor method can be - // called from other threads. - DCHECK(GetInstance()->browser_delegate_.get() != NULL); - return GetInstance()->browser_delegate_.get(); -} - -void NaClBrowser::EarlyStartup() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - InitIrtFilePath(); - InitValidationCacheFilePath(); -} - -NaClBrowser::~NaClBrowser() { - if (irt_platform_file_ != base::kInvalidPlatformFileValue) - base::ClosePlatformFile(irt_platform_file_); -} - -void NaClBrowser::InitIrtFilePath() { - // Allow the IRT library to be overridden via an environment - // variable. This allows the NaCl/Chromium integration bot to - // specify a newly-built IRT rather than using a prebuilt one - // downloaded via Chromium's DEPS file. We use the same environment - // variable that the standalone NaCl PPAPI plugin accepts. - const char* irt_path_var = getenv("NACL_IRT_LIBRARY"); - if (irt_path_var != NULL) { - base::FilePath::StringType path_string( - irt_path_var, const_cast<const char*>(strchr(irt_path_var, '\0'))); - irt_filepath_ = base::FilePath(path_string); - } else { - base::FilePath plugin_dir; - if (!browser_delegate_->GetPluginDirectory(&plugin_dir)) { - DLOG(ERROR) << "Failed to locate the plugins directory, NaCl disabled."; - MarkAsFailed(); - return; - } - irt_filepath_ = plugin_dir.Append(NaClIrtName()); - } -} - -#if defined(OS_WIN) -bool NaClBrowser::GetNaCl64ExePath(base::FilePath* exe_path) { - base::FilePath module_path; - if (!PathService::Get(base::FILE_MODULE, &module_path)) { - LOG(ERROR) << "NaCl process launch failed: could not resolve module"; - return false; - } - *exe_path = module_path.DirName().Append(L"nacl64"); - return true; -} -#endif - -NaClBrowser* NaClBrowser::GetInstance() { - return Singleton<NaClBrowser>::get(); -} - -bool NaClBrowser::IsReady() const { - return (IsOk() && - irt_state_ == NaClResourceReady && - validation_cache_state_ == NaClResourceReady); -} - -bool NaClBrowser::IsOk() const { - return ok_; -} - -base::PlatformFile NaClBrowser::IrtFile() const { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - CHECK_EQ(irt_state_, NaClResourceReady); - CHECK_NE(irt_platform_file_, base::kInvalidPlatformFileValue); - return irt_platform_file_; -} - -void NaClBrowser::EnsureAllResourcesAvailable() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - EnsureIrtAvailable(); - EnsureValidationCacheAvailable(); -} - -// Load the IRT async. -void NaClBrowser::EnsureIrtAvailable() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - if (IsOk() && irt_state_ == NaClResourceUninitialized) { - irt_state_ = NaClResourceRequested; - // TODO(ncbray) use blocking pool. - if (!base::FileUtilProxy::CreateOrOpen( - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::FILE) - .get(), - irt_filepath_, - base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, - base::Bind(&NaClBrowser::OnIrtOpened, - weak_factory_.GetWeakPtr()))) { - LOG(ERROR) << "Internal error, NaCl disabled."; - MarkAsFailed(); - } - } -} - -void NaClBrowser::OnIrtOpened(base::PlatformFileError error_code, - base::PassPlatformFile file, - bool created) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - DCHECK_EQ(irt_state_, NaClResourceRequested); - DCHECK(!created); - if (error_code == base::PLATFORM_FILE_OK) { - irt_platform_file_ = file.ReleaseValue(); - } else { - LOG(ERROR) << "Failed to open NaCl IRT file \"" - << irt_filepath_.LossyDisplayName() - << "\": " << error_code; - MarkAsFailed(); - } - irt_state_ = NaClResourceReady; - CheckWaiting(); -} - -void NaClBrowser::FireGdbDebugStubPortOpened(int port) { - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind(debug_stub_port_listener_, port)); -} - -bool NaClBrowser::HasGdbDebugStubPortListener() { - return !debug_stub_port_listener_.is_null(); -} - -void NaClBrowser::SetGdbDebugStubPortListener( - base::Callback<void(int)> listener) { - debug_stub_port_listener_ = listener; -} - -void NaClBrowser::ClearGdbDebugStubPortListener() { - debug_stub_port_listener_.Reset(); -} - -void NaClBrowser::InitValidationCacheFilePath() { - // Determine where the validation cache resides in the file system. It - // exists in Chrome's cache directory and is not tied to any specific - // profile. - // Start by finding the user data directory. - base::FilePath user_data_dir; - if (!browser_delegate_->GetUserDirectory(&user_data_dir)) { - RunWithoutValidationCache(); - return; - } - // The cache directory may or may not be the user data directory. - base::FilePath cache_file_path; - browser_delegate_->GetCacheDirectory(&cache_file_path); - // Append the base file name to the cache directory. - - validation_cache_file_path_ = - cache_file_path.Append(kValidationCacheFileName); -} - -void NaClBrowser::EnsureValidationCacheAvailable() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - if (IsOk() && validation_cache_state_ == NaClResourceUninitialized) { - if (ValidationCacheIsEnabled()) { - validation_cache_state_ = NaClResourceRequested; - - // Structure for carrying data between the callbacks. - std::string* data = new std::string(); - // We can get away not giving this a sequence ID because this is the first - // task and further file access will not occur until after we get a - // response. - if (!content::BrowserThread::PostBlockingPoolTaskAndReply( - FROM_HERE, - base::Bind(ReadCache, validation_cache_file_path_, data), - base::Bind(&NaClBrowser::OnValidationCacheLoaded, - weak_factory_.GetWeakPtr(), - base::Owned(data)))) { - RunWithoutValidationCache(); - } - } else { - RunWithoutValidationCache(); - } - } -} - -void NaClBrowser::OnValidationCacheLoaded(const std::string *data) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - // Did the cache get cleared before the load completed? If so, ignore the - // incoming data. - if (validation_cache_state_ == NaClResourceReady) - return; - - if (data->size() == 0) { - // No file found. - validation_cache_.Reset(); - } else { - Pickle pickle(data->data(), data->size()); - validation_cache_.Deserialize(&pickle); - } - validation_cache_state_ = NaClResourceReady; - CheckWaiting(); -} - -void NaClBrowser::RunWithoutValidationCache() { - // Be paranoid. - validation_cache_.Reset(); - validation_cache_is_enabled_ = false; - validation_cache_state_ = NaClResourceReady; - CheckWaiting(); -} - -void NaClBrowser::CheckWaiting() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - if (!IsOk() || IsReady()) { - // Queue the waiting tasks into the message loop. This helps avoid - // re-entrancy problems that could occur if the closure was invoked - // directly. For example, this could result in use-after-free of the - // process host. - for (std::vector<base::Closure>::iterator iter = waiting_.begin(); - iter != waiting_.end(); ++iter) { - base::MessageLoop::current()->PostTask(FROM_HERE, *iter); - } - waiting_.clear(); - } -} - -void NaClBrowser::MarkAsFailed() { - ok_ = false; - CheckWaiting(); -} - -void NaClBrowser::WaitForResources(const base::Closure& reply) { - waiting_.push_back(reply); - EnsureAllResourcesAvailable(); - CheckWaiting(); -} - -const base::FilePath& NaClBrowser::GetIrtFilePath() { - return irt_filepath_; -} - -void NaClBrowser::PutFilePath(const base::FilePath& path, uint64* file_token_lo, - uint64* file_token_hi) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - while (true) { - uint64 file_token[2] = {base::RandUint64(), base::RandUint64()}; - // A zero file_token indicates there is no file_token, if we get zero, ask - // for another number. - if (file_token[0] != 0 || file_token[1] != 0) { - // If the file_token is in use, ask for another number. - std::string key(reinterpret_cast<char*>(file_token), sizeof(file_token)); - PathCacheType::iterator iter = path_cache_.Peek(key); - if (iter == path_cache_.end()) { - path_cache_.Put(key, path); - *file_token_lo = file_token[0]; - *file_token_hi = file_token[1]; - break; - } - } - } -} - -bool NaClBrowser::GetFilePath(uint64 file_token_lo, uint64 file_token_hi, - base::FilePath* path) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - uint64 file_token[2] = {file_token_lo, file_token_hi}; - std::string key(reinterpret_cast<char*>(file_token), sizeof(file_token)); - PathCacheType::iterator iter = path_cache_.Peek(key); - if (iter == path_cache_.end()) { - *path = base::FilePath(FILE_PATH_LITERAL("")); - return false; - } - *path = iter->second; - path_cache_.Erase(iter); - return true; -} - - -bool NaClBrowser::QueryKnownToValidate(const std::string& signature, - bool off_the_record) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - if (off_the_record) { - // If we're off the record, don't reorder the main cache. - return validation_cache_.QueryKnownToValidate(signature, false) || - off_the_record_validation_cache_.QueryKnownToValidate(signature, true); - } else { - bool result = validation_cache_.QueryKnownToValidate(signature, true); - LogCacheQuery(result ? CACHE_HIT : CACHE_MISS); - // Queries can modify the MRU order of the cache. - MarkValidationCacheAsModified(); - return result; - } -} - -void NaClBrowser::SetKnownToValidate(const std::string& signature, - bool off_the_record) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - if (off_the_record) { - off_the_record_validation_cache_.SetKnownToValidate(signature); - } else { - validation_cache_.SetKnownToValidate(signature); - // The number of sets should be equal to the number of cache misses, minus - // validation failures and successful validations where stubout occurs. - LogCacheSet(CACHE_HIT); - MarkValidationCacheAsModified(); - } -} - -void NaClBrowser::ClearValidationCache(const base::Closure& callback) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - // Note: this method may be called before EnsureValidationCacheAvailable has - // been invoked. In other words, this method may be called before any NaCl - // processes have been created. This method must succeed and invoke the - // callback in such a case. If it does not invoke the callback, Chrome's UI - // will hang in that case. - validation_cache_.Reset(); - off_the_record_validation_cache_.Reset(); - - if (validation_cache_file_path_.empty()) { - // Can't figure out what file to remove, but don't drop the callback. - content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, - callback); - } else { - // Delegate the removal of the cache from the filesystem to another thread - // to avoid blocking the IO thread. - // This task is dispatched immediately, not delayed and coalesced, because - // the user interface for cache clearing is likely waiting for the callback. - // In addition, we need to make sure the cache is actually cleared before - // invoking the callback to meet the implicit guarantees of the UI. - content::BrowserThread::PostBlockingPoolSequencedTask( - kValidationCacheSequenceName, - FROM_HERE, - base::Bind(RemoveCache, validation_cache_file_path_, callback)); - } - - // Make sure any delayed tasks to persist the cache to the filesystem are - // squelched. - validation_cache_is_modified_ = false; - - // If the cache is cleared before it is loaded from the filesystem, act as if - // we just loaded an empty cache. - if (validation_cache_state_ != NaClResourceReady) { - validation_cache_state_ = NaClResourceReady; - CheckWaiting(); - } -} - -void NaClBrowser::MarkValidationCacheAsModified() { - if (!validation_cache_is_modified_) { - // Wait before persisting to disk. This can coalesce multiple cache - // modifications info a single disk write. - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&NaClBrowser::PersistValidationCache, - weak_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kValidationCacheCoalescingTimeMS)); - validation_cache_is_modified_ = true; - } -} - -void NaClBrowser::PersistValidationCache() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - // validation_cache_is_modified_ may be false if the cache was cleared while - // this delayed task was pending. - // validation_cache_file_path_ may be empty if something went wrong during - // initialization. - if (validation_cache_is_modified_ && !validation_cache_file_path_.empty()) { - Pickle* pickle = new Pickle(); - validation_cache_.Serialize(pickle); - - // Pass the serialized data to another thread to write to disk. File IO is - // not allowed on the IO thread (which is the thread this method runs on) - // because it can degrade the responsiveness of the browser. - // The task is sequenced so that multiple writes happen in order. - content::BrowserThread::PostBlockingPoolSequencedTask( - kValidationCacheSequenceName, - FROM_HERE, - base::Bind(WriteCache, validation_cache_file_path_, - base::Owned(pickle))); - } - validation_cache_is_modified_ = false; -} - -void NaClBrowser::OnProcessCrashed() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - if (crash_times_.size() == kMaxCrashesPerInterval) { - crash_times_.pop_front(); - } - base::Time time = base::Time::Now(); - crash_times_.push_back(time); -} - -bool NaClBrowser::IsThrottled() { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - if (crash_times_.size() != kMaxCrashesPerInterval) { - return false; - } - base::TimeDelta delta = base::Time::Now() - crash_times_.front(); - return delta.InSeconds() <= kCrashesIntervalInSeconds; -} - -} // namespace nacl |