This source file includes following definitions.
- NaClIrtName
- CheckEnvVar
- ReadCache
- WriteCache
- RemoveCache
- LogCacheQuery
- LogCacheSet
- OpenNaClExecutableImpl
- ok_
- SetDelegate
- GetDelegate
- EarlyStartup
- InitIrtFilePath
- GetNaCl64ExePath
- GetInstance
- IsReady
- IsOk
- IrtFile
- EnsureAllResourcesAvailable
- EnsureIrtAvailable
- OnIrtOpened
- FireGdbDebugStubPortOpened
- HasGdbDebugStubPortListener
- SetGdbDebugStubPortListener
- ClearGdbDebugStubPortListener
- InitValidationCacheFilePath
- EnsureValidationCacheAvailable
- OnValidationCacheLoaded
- RunWithoutValidationCache
- CheckWaiting
- MarkAsFailed
- WaitForResources
- GetIrtFilePath
- PutFilePath
- GetFilePath
- QueryKnownToValidate
- SetKnownToValidate
- ClearValidationCache
- MarkValidationCacheAsModified
- PersistValidationCache
- OnProcessCrashed
- IsThrottled
#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 {
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
};
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)
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)) {
data->clear();
}
}
void WriteCache(const base::FilePath& filename, const Pickle* pickle) {
base::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) {
UMA_HISTOGRAM_ENUMERATION("NaCl.ValidationCache.Set", status, CACHE_MAX);
}
const size_t kMaxCrashesPerInterval = 3;
const int64 kCrashesIntervalInSeconds = 120;
}
namespace nacl {
base::File OpenNaClExecutableImpl(const base::FilePath& file_path) {
base::File file(file_path,
(base::File::FLAG_OPEN |
base::File::FLAG_READ |
base::File::FLAG_EXECUTE));
if (!file.IsValid())
return file.Pass();
base::File::Info file_info;
if (!file.GetInfo(&file_info) || file_info.is_directory)
return base::File();
return file.Pass();
}
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() {
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() {
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();
}
void NaClBrowser::EnsureIrtAvailable() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
if (IsOk() && irt_state_ == NaClResourceUninitialized) {
irt_state_ = NaClResourceRequested;
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::File::Error 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::File::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() {
base::FilePath user_data_dir;
if (!browser_delegate_->GetUserDirectory(&user_data_dir)) {
RunWithoutValidationCache();
return;
}
base::FilePath cache_file_path;
browser_delegate_->GetCacheDirectory(&cache_file_path);
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;
std::string* data = new std::string();
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));
if (validation_cache_state_ == NaClResourceReady)
return;
if (data->size() == 0) {
validation_cache_.Reset();
} else {
Pickle pickle(data->data(), data->size());
validation_cache_.Deserialize(&pickle);
}
validation_cache_state_ = NaClResourceReady;
CheckWaiting();
}
void NaClBrowser::RunWithoutValidationCache() {
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()) {
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()};
if (file_token[0] != 0 || file_token[1] != 0) {
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) {
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);
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);
LogCacheSet(CACHE_HIT);
MarkValidationCacheAsModified();
}
}
void NaClBrowser::ClearValidationCache(const base::Closure& callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
validation_cache_.Reset();
off_the_record_validation_cache_.Reset();
if (validation_cache_file_path_.empty()) {
content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
callback);
} else {
content::BrowserThread::PostBlockingPoolSequencedTask(
kValidationCacheSequenceName,
FROM_HERE,
base::Bind(RemoveCache, validation_cache_file_path_, callback));
}
validation_cache_is_modified_ = false;
if (validation_cache_state_ != NaClResourceReady) {
validation_cache_state_ = NaClResourceReady;
CheckWaiting();
}
}
void NaClBrowser::MarkValidationCacheAsModified() {
if (!validation_cache_is_modified_) {
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));
if (validation_cache_is_modified_ && !validation_cache_file_path_.empty()) {
Pickle* pickle = new Pickle();
validation_cache_.Serialize(pickle);
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;
}
}