• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/nacl/browser/nacl_browser.h"
6 
7 #include <stddef.h>
8 #include <utility>
9 
10 #include "base/command_line.h"
11 #include "base/files/file_proxy.h"
12 #include "base/files/file_util.h"
13 #include "base/lazy_instance.h"
14 #include "base/location.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/path_service.h"
17 #include "base/pickle.h"
18 #include "base/rand_util.h"
19 #include "base/task/single_thread_task_runner.h"
20 #include "base/time/time.h"
21 #include "build/build_config.h"
22 #include "content/public/browser/browser_task_traits.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "url/gurl.h"
25 
26 #if BUILDFLAG(IS_WIN)
27 #include "base/win/windows_version.h"
28 #endif
29 
30 namespace {
31 
32 // Tasks posted in this file are on the critical path of displaying the official
33 // virtual keyboard on Chrome OS. https://crbug.com/976542
34 constexpr base::TaskPriority kUserBlocking = base::TaskPriority::USER_BLOCKING;
35 
36 // An arbitrary delay to coalesce multiple writes to the cache.
37 const int kValidationCacheCoalescingTimeMS = 6000;
38 const base::FilePath::CharType kValidationCacheFileName[] =
39     FILE_PATH_LITERAL("nacl_validation_cache.bin");
40 
41 const bool kValidationCacheEnabledByDefault = true;
42 
NaClIrtName()43 const base::FilePath::StringType NaClIrtName() {
44   base::FilePath::StringType irt_name(FILE_PATH_LITERAL("nacl_irt_"));
45 
46 #if defined(ARCH_CPU_X86_FAMILY)
47 #if defined(ARCH_CPU_X86_64)
48   bool is64 = true;
49 #elif BUILDFLAG(IS_WIN)
50   bool is64 = base::win::OSInfo::GetInstance()->IsWowX86OnAMD64();
51 #else
52   bool is64 = false;
53 #endif
54   if (is64)
55     irt_name.append(FILE_PATH_LITERAL("x86_64"));
56   else
57     irt_name.append(FILE_PATH_LITERAL("x86_32"));
58 
59 #elif defined(ARCH_CPU_ARM_FAMILY)
60   irt_name.append(FILE_PATH_LITERAL("arm"));
61 #elif defined(ARCH_CPU_MIPSEL)
62   irt_name.append(FILE_PATH_LITERAL("mips32"));
63 #else
64 #error Add support for your architecture to NaCl IRT file selection
65 #endif
66   irt_name.append(FILE_PATH_LITERAL(".nexe"));
67   return irt_name;
68 }
69 
70 #if !BUILDFLAG(IS_ANDROID)
CheckEnvVar(const char * name,bool default_value)71 bool CheckEnvVar(const char* name, bool default_value) {
72   bool result = default_value;
73   const char* var = getenv(name);
74   if (var && strlen(var) > 0) {
75     result = var[0] != '0';
76   }
77   return result;
78 }
79 #endif
80 
ReadCache(const base::FilePath & filename,std::string * data)81 void ReadCache(const base::FilePath& filename, std::string* data) {
82   if (!base::ReadFileToString(filename, data)) {
83     // Zero-size data used as an in-band error code.
84     data->clear();
85   }
86 }
87 
WriteCache(const base::FilePath & filename,const base::Pickle * pickle)88 void WriteCache(const base::FilePath& filename, const base::Pickle* pickle) {
89   base::WriteFile(filename,
90                   base::make_span(static_cast<const uint8_t*>(pickle->data()),
91                                   pickle->size()));
92 }
93 
RemoveCache(const base::FilePath & filename,base::OnceClosure callback)94 void RemoveCache(const base::FilePath& filename, base::OnceClosure callback) {
95   base::DeleteFile(filename);
96   content::GetIOThreadTaskRunner({})->PostTask(FROM_HERE, std::move(callback));
97 }
98 
LogCacheQuery(nacl::NaClBrowser::ValidationCacheStatus status)99 void LogCacheQuery(nacl::NaClBrowser::ValidationCacheStatus status) {
100   UMA_HISTOGRAM_ENUMERATION("NaCl.ValidationCache.Query", status,
101                             nacl::NaClBrowser::CACHE_MAX);
102 }
103 
LogCacheSet(nacl::NaClBrowser::ValidationCacheStatus status)104 void LogCacheSet(nacl::NaClBrowser::ValidationCacheStatus status) {
105   // Bucket zero is reserved for future use.
106   UMA_HISTOGRAM_ENUMERATION("NaCl.ValidationCache.Set", status,
107                             nacl::NaClBrowser::CACHE_MAX);
108 }
109 
110 // Crash throttling parameters.
111 const size_t kMaxCrashesPerInterval = 3;
112 const int64_t kCrashesIntervalInSeconds = 120;
113 
114 // Holds the NaClBrowserDelegate, which is leaked on shutdown.
115 NaClBrowserDelegate* g_browser_delegate = nullptr;
116 
117 }  // namespace
118 
119 namespace nacl {
120 
OpenNaClReadExecImpl(const base::FilePath & file_path,bool is_executable)121 base::File OpenNaClReadExecImpl(const base::FilePath& file_path,
122                                 bool is_executable) {
123   // Get a file descriptor. On Windows, we need 'GENERIC_EXECUTE' in order to
124   // memory map the executable.
125   // IMPORTANT: This file descriptor must not have write access - that could
126   // allow a NaCl inner sandbox escape.
127   uint32_t flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
128   if (is_executable)
129     flags |= base::File::FLAG_WIN_EXECUTE;  // Windows only flag.
130   base::File file(file_path, flags);
131   if (!file.IsValid())
132     return file;
133 
134   // Check that the file does not reference a directory. Returning a descriptor
135   // to an extension directory could allow an outer sandbox escape. openat(...)
136   // could be used to traverse into the file system.
137   base::File::Info file_info;
138   if (!file.GetInfo(&file_info) || file_info.is_directory)
139     return base::File();
140 
141   return file;
142 }
143 
NaClBrowser()144 NaClBrowser::NaClBrowser() {
145 #if !BUILDFLAG(IS_ANDROID)
146   validation_cache_is_enabled_ =
147       CheckEnvVar("NACL_VALIDATION_CACHE", kValidationCacheEnabledByDefault);
148 #endif
149       DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
150 }
151 
SetDelegate(std::unique_ptr<NaClBrowserDelegate> delegate)152 void NaClBrowser::SetDelegate(std::unique_ptr<NaClBrowserDelegate> delegate) {
153   // In the browser SetDelegate is called after threads are initialized.
154   // In tests it is called before initializing BrowserThreads.
155   if (content::BrowserThread::IsThreadInitialized(content::BrowserThread::UI)) {
156     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
157   }
158   DCHECK(delegate);
159   DCHECK(!g_browser_delegate);
160   g_browser_delegate = delegate.release();
161 }
162 
GetDelegate()163 NaClBrowserDelegate* NaClBrowser::GetDelegate() {
164   // NaClBrowser calls this on the IO thread, not the UI thread.
165   DCHECK(g_browser_delegate);
166   return g_browser_delegate;
167 }
168 
ClearAndDeleteDelegateForTest()169 void NaClBrowser::ClearAndDeleteDelegateForTest() {
170   DCHECK(
171       !content::BrowserThread::IsThreadInitialized(content::BrowserThread::UI));
172   DCHECK(g_browser_delegate);
173   delete g_browser_delegate;
174   g_browser_delegate = nullptr;
175 }
176 
EarlyStartup()177 void NaClBrowser::EarlyStartup() {
178   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
179   InitIrtFilePath();
180   InitValidationCacheFilePath();
181 }
182 
~NaClBrowser()183 NaClBrowser::~NaClBrowser() {
184   NOTREACHED();
185 }
186 
InitIrtFilePath()187 void NaClBrowser::InitIrtFilePath() {
188   // Allow the IRT library to be overridden via an environment
189   // variable.  This allows the NaCl/Chromium integration bot to
190   // specify a newly-built IRT rather than using a prebuilt one
191   // downloaded via Chromium's DEPS file.  We use the same environment
192   // variable that the standalone NaCl PPAPI plugin accepts.
193   const char* irt_path_var = getenv("NACL_IRT_LIBRARY");
194   if (irt_path_var != NULL) {
195     base::FilePath::StringType path_string(
196         irt_path_var, const_cast<const char*>(strchr(irt_path_var, '\0')));
197     irt_filepath_ = base::FilePath(path_string);
198   } else {
199     base::FilePath plugin_dir;
200     if (!GetDelegate()->GetPluginDirectory(&plugin_dir)) {
201       DLOG(ERROR) << "Failed to locate the plugins directory, NaCl disabled.";
202       MarkAsFailed();
203       return;
204     }
205     irt_filepath_ = plugin_dir.Append(NaClIrtName());
206   }
207 }
208 
209 #if BUILDFLAG(IS_WIN)
GetNaCl64ExePath(base::FilePath * exe_path)210 bool NaClBrowser::GetNaCl64ExePath(base::FilePath* exe_path) {
211   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
212   base::FilePath module_path;
213   if (!base::PathService::Get(base::FILE_MODULE, &module_path)) {
214     LOG(ERROR) << "NaCl process launch failed: could not resolve module";
215     return false;
216   }
217   *exe_path = module_path.DirName().Append(L"nacl64");
218   return true;
219 }
220 #endif
221 
222 // static
GetInstanceInternal()223 NaClBrowser* NaClBrowser::GetInstanceInternal() {
224   static NaClBrowser* g_instance = nullptr;
225   if (!g_instance)
226     g_instance = new NaClBrowser();
227   return g_instance;
228 }
229 
GetInstance()230 NaClBrowser* NaClBrowser::GetInstance() {
231   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
232   return GetInstanceInternal();
233 }
234 
IsReady() const235 bool NaClBrowser::IsReady() const {
236   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
237   return (IsOk() &&
238           irt_state_ == NaClResourceReady &&
239           validation_cache_state_ == NaClResourceReady);
240 }
241 
IsOk() const242 bool NaClBrowser::IsOk() const {
243   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
244   return !has_failed_;
245 }
246 
IrtFile() const247 const base::File& NaClBrowser::IrtFile() const {
248   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
249   CHECK_EQ(irt_state_, NaClResourceReady);
250   CHECK(irt_file_.IsValid());
251   return irt_file_;
252 }
253 
EnsureAllResourcesAvailable()254 void NaClBrowser::EnsureAllResourcesAvailable() {
255   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
256   EnsureIrtAvailable();
257   EnsureValidationCacheAvailable();
258 }
259 
260 // Load the IRT async.
EnsureIrtAvailable()261 void NaClBrowser::EnsureIrtAvailable() {
262   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
263   if (IsOk() && irt_state_ == NaClResourceUninitialized) {
264     irt_state_ = NaClResourceRequested;
265     auto task_runner = base::ThreadPool::CreateTaskRunner(
266         {base::MayBlock(), kUserBlocking,
267          base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
268     std::unique_ptr<base::FileProxy> file_proxy(
269         new base::FileProxy(task_runner.get()));
270     base::FileProxy* proxy = file_proxy.get();
271     if (!proxy->CreateOrOpen(
272             irt_filepath_, base::File::FLAG_OPEN | base::File::FLAG_READ,
273             base::BindOnce(&NaClBrowser::OnIrtOpened, base::Unretained(this),
274                            std::move(file_proxy)))) {
275       LOG(ERROR) << "Internal error, NaCl disabled.";
276       MarkAsFailed();
277     }
278   }
279 }
280 
OnIrtOpened(std::unique_ptr<base::FileProxy> file_proxy,base::File::Error error_code)281 void NaClBrowser::OnIrtOpened(std::unique_ptr<base::FileProxy> file_proxy,
282                               base::File::Error error_code) {
283   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
284   DCHECK_EQ(irt_state_, NaClResourceRequested);
285   if (file_proxy->IsValid()) {
286     irt_file_ = file_proxy->TakeFile();
287   } else {
288     LOG(ERROR) << "Failed to open NaCl IRT file \""
289                << irt_filepath_.LossyDisplayName()
290                << "\": " << error_code;
291     MarkAsFailed();
292   }
293   irt_state_ = NaClResourceReady;
294   CheckWaiting();
295 }
296 
SetProcessGdbDebugStubPort(int process_id,int port)297 void NaClBrowser::SetProcessGdbDebugStubPort(int process_id, int port) {
298   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
299   gdb_debug_stub_port_map_[process_id] = port;
300   if (port != kGdbDebugStubPortUnknown &&
301       !debug_stub_port_listener_.is_null()) {
302     content::GetIOThreadTaskRunner({})->PostTask(
303         FROM_HERE, base::BindOnce(debug_stub_port_listener_, port));
304   }
305 }
306 
307 // static
SetGdbDebugStubPortListenerForTest(base::RepeatingCallback<void (int)> listener)308 void NaClBrowser::SetGdbDebugStubPortListenerForTest(
309     base::RepeatingCallback<void(int)> listener) {
310   GetInstanceInternal()->debug_stub_port_listener_ = listener;
311 }
312 
313 // static
ClearGdbDebugStubPortListenerForTest()314 void NaClBrowser::ClearGdbDebugStubPortListenerForTest() {
315   GetInstanceInternal()->debug_stub_port_listener_.Reset();
316 }
317 
GetProcessGdbDebugStubPort(int process_id)318 int NaClBrowser::GetProcessGdbDebugStubPort(int process_id) {
319   // Called from TaskManager TaskGroup impl, on CrBrowserMain.
320   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
321   auto i = gdb_debug_stub_port_map_.find(process_id);
322   if (i != gdb_debug_stub_port_map_.end()) {
323     return i->second;
324   }
325   return kGdbDebugStubPortUnused;
326 }
327 
InitValidationCacheFilePath()328 void NaClBrowser::InitValidationCacheFilePath() {
329   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
330   // Determine where the validation cache resides in the file system.  It
331   // exists in Chrome's cache directory and is not tied to any specific
332   // profile.
333   // Start by finding the user data directory.
334   base::FilePath user_data_dir;
335   if (!GetDelegate()->GetUserDirectory(&user_data_dir)) {
336     RunWithoutValidationCache();
337     return;
338   }
339   // The cache directory may or may not be the user data directory.
340   base::FilePath cache_file_path;
341   GetDelegate()->GetCacheDirectory(&cache_file_path);
342   // Append the base file name to the cache directory.
343 
344   validation_cache_file_path_ =
345       cache_file_path.Append(kValidationCacheFileName);
346 }
347 
EnsureValidationCacheAvailable()348 void NaClBrowser::EnsureValidationCacheAvailable() {
349   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
350   if (IsOk() && validation_cache_state_ == NaClResourceUninitialized) {
351     if (ValidationCacheIsEnabled()) {
352       validation_cache_state_ = NaClResourceRequested;
353 
354       // Structure for carrying data between the callbacks.
355       std::string* data = new std::string();
356       // We can get away not giving this a sequence ID because this is the first
357       // task and further file access will not occur until after we get a
358       // response.
359       base::ThreadPool::PostTaskAndReply(
360           FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
361           base::BindOnce(ReadCache, validation_cache_file_path_, data),
362           base::BindOnce(&NaClBrowser::OnValidationCacheLoaded,
363                          base::Unretained(this), base::Owned(data)));
364     } else {
365       RunWithoutValidationCache();
366     }
367   }
368 }
369 
OnValidationCacheLoaded(const std::string * data)370 void NaClBrowser::OnValidationCacheLoaded(const std::string *data) {
371   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
372   // Did the cache get cleared before the load completed?  If so, ignore the
373   // incoming data.
374   if (validation_cache_state_ == NaClResourceReady)
375     return;
376 
377   if (data->size() == 0) {
378     // No file found.
379     validation_cache_.Reset();
380   } else {
381     base::Pickle pickle(data->data(), data->size());
382     validation_cache_.Deserialize(&pickle);
383   }
384   validation_cache_state_ = NaClResourceReady;
385   CheckWaiting();
386 }
387 
RunWithoutValidationCache()388 void NaClBrowser::RunWithoutValidationCache() {
389   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
390   // Be paranoid.
391   validation_cache_.Reset();
392   validation_cache_is_enabled_ = false;
393   validation_cache_state_ = NaClResourceReady;
394   CheckWaiting();
395 }
396 
CheckWaiting()397 void NaClBrowser::CheckWaiting() {
398   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
399   if (!IsOk() || IsReady()) {
400     // Queue the waiting tasks into the message loop.  This helps avoid
401     // re-entrancy problems that could occur if the closure was invoked
402     // directly.  For example, this could result in use-after-free of the
403     // process host.
404     for (auto iter = waiting_.begin(); iter != waiting_.end(); ++iter) {
405       base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
406           FROM_HERE, std::move(*iter));
407     }
408     waiting_.clear();
409   }
410 }
411 
MarkAsFailed()412 void NaClBrowser::MarkAsFailed() {
413   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
414   has_failed_ = true;
415   CheckWaiting();
416 }
417 
WaitForResources(base::OnceClosure reply)418 void NaClBrowser::WaitForResources(base::OnceClosure reply) {
419   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
420   waiting_.push_back(std::move(reply));
421   EnsureAllResourcesAvailable();
422   CheckWaiting();
423 }
424 
GetIrtFilePath()425 const base::FilePath& NaClBrowser::GetIrtFilePath() {
426   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
427   return irt_filepath_;
428 }
429 
PutFilePath(const base::FilePath & path,uint64_t * file_token_lo,uint64_t * file_token_hi)430 void NaClBrowser::PutFilePath(const base::FilePath& path,
431                               uint64_t* file_token_lo,
432                               uint64_t* file_token_hi) {
433   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
434   while (true) {
435     uint64_t file_token[2] = {base::RandUint64(), base::RandUint64()};
436     // A zero file_token indicates there is no file_token, if we get zero, ask
437     // for another number.
438     if (file_token[0] != 0 || file_token[1] != 0) {
439       // If the file_token is in use, ask for another number.
440       std::string key(reinterpret_cast<char*>(file_token), sizeof(file_token));
441       auto iter = path_cache_.Peek(key);
442       if (iter == path_cache_.end()) {
443         path_cache_.Put(key, path);
444         *file_token_lo = file_token[0];
445         *file_token_hi = file_token[1];
446         break;
447       }
448     }
449   }
450 }
451 
GetFilePath(uint64_t file_token_lo,uint64_t file_token_hi,base::FilePath * path)452 bool NaClBrowser::GetFilePath(uint64_t file_token_lo,
453                               uint64_t file_token_hi,
454                               base::FilePath* path) {
455   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
456   uint64_t file_token[2] = {file_token_lo, file_token_hi};
457   std::string key(reinterpret_cast<char*>(file_token), sizeof(file_token));
458   auto iter = path_cache_.Peek(key);
459   if (iter == path_cache_.end()) {
460     *path = base::FilePath(FILE_PATH_LITERAL(""));
461     return false;
462   }
463   *path = iter->second;
464   path_cache_.Erase(iter);
465   return true;
466 }
467 
468 
QueryKnownToValidate(const std::string & signature,bool off_the_record)469 bool NaClBrowser::QueryKnownToValidate(const std::string& signature,
470                                        bool off_the_record) {
471   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
472   if (off_the_record) {
473     // If we're off the record, don't reorder the main cache.
474     return validation_cache_.QueryKnownToValidate(signature, false) ||
475         off_the_record_validation_cache_.QueryKnownToValidate(signature, true);
476   } else {
477     bool result = validation_cache_.QueryKnownToValidate(signature, true);
478     LogCacheQuery(result ? CACHE_HIT : CACHE_MISS);
479     // Queries can modify the MRU order of the cache.
480     MarkValidationCacheAsModified();
481     return result;
482   }
483 }
484 
SetKnownToValidate(const std::string & signature,bool off_the_record)485 void NaClBrowser::SetKnownToValidate(const std::string& signature,
486                                      bool off_the_record) {
487   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
488   if (off_the_record) {
489     off_the_record_validation_cache_.SetKnownToValidate(signature);
490   } else {
491     validation_cache_.SetKnownToValidate(signature);
492     // The number of sets should be equal to the number of cache misses, minus
493     // validation failures and successful validations where stubout occurs.
494     LogCacheSet(CACHE_HIT);
495     MarkValidationCacheAsModified();
496   }
497 }
498 
ClearValidationCache(base::OnceClosure callback)499 void NaClBrowser::ClearValidationCache(base::OnceClosure callback) {
500   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
501   // Note: this method may be called before EnsureValidationCacheAvailable has
502   // been invoked.  In other words, this method may be called before any NaCl
503   // processes have been created.  This method must succeed and invoke the
504   // callback in such a case.  If it does not invoke the callback, Chrome's UI
505   // will hang in that case.
506   validation_cache_.Reset();
507   off_the_record_validation_cache_.Reset();
508 
509   if (validation_cache_file_path_.empty()) {
510     // Can't figure out what file to remove, but don't drop the callback.
511     content::GetIOThreadTaskRunner({})->PostTask(FROM_HERE,
512                                                  std::move(callback));
513   } else {
514     // Delegate the removal of the cache from the filesystem to another thread
515     // to avoid blocking the IO thread.
516     // This task is dispatched immediately, not delayed and coalesced, because
517     // the user interface for cache clearing is likely waiting for the callback.
518     // In addition, we need to make sure the cache is actually cleared before
519     // invoking the callback to meet the implicit guarantees of the UI.
520     file_task_runner_->PostTask(
521         FROM_HERE, base::BindOnce(RemoveCache, validation_cache_file_path_,
522                                   std::move(callback)));
523   }
524 
525   // Make sure any delayed tasks to persist the cache to the filesystem are
526   // squelched.
527   validation_cache_is_modified_ = false;
528 
529   // If the cache is cleared before it is loaded from the filesystem, act as if
530   // we just loaded an empty cache.
531   if (validation_cache_state_ != NaClResourceReady) {
532     validation_cache_state_ = NaClResourceReady;
533     CheckWaiting();
534   }
535 }
536 
MarkValidationCacheAsModified()537 void NaClBrowser::MarkValidationCacheAsModified() {
538   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
539   if (!validation_cache_is_modified_) {
540     // Wait before persisting to disk.  This can coalesce multiple cache
541     // modifications info a single disk write.
542     base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
543         FROM_HERE,
544         base::BindOnce(&NaClBrowser::PersistValidationCache,
545                        base::Unretained(this)),
546         base::Milliseconds(kValidationCacheCoalescingTimeMS));
547     validation_cache_is_modified_ = true;
548   }
549 }
550 
PersistValidationCache()551 void NaClBrowser::PersistValidationCache() {
552   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
553   // validation_cache_is_modified_ may be false if the cache was cleared while
554   // this delayed task was pending.
555   // validation_cache_file_path_ may be empty if something went wrong during
556   // initialization.
557   if (validation_cache_is_modified_ && !validation_cache_file_path_.empty()) {
558     base::Pickle* pickle = new base::Pickle();
559     validation_cache_.Serialize(pickle);
560 
561     // Pass the serialized data to another thread to write to disk.  File IO is
562     // not allowed on the IO thread (which is the thread this method runs on)
563     // because it can degrade the responsiveness of the browser.
564     // The task is sequenced so that multiple writes happen in order.
565     file_task_runner_->PostTask(
566         FROM_HERE, base::BindOnce(WriteCache, validation_cache_file_path_,
567                                   base::Owned(pickle)));
568   }
569   validation_cache_is_modified_ = false;
570 }
571 
OnProcessEnd(int process_id)572 void NaClBrowser::OnProcessEnd(int process_id) {
573   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
574   gdb_debug_stub_port_map_.erase(process_id);
575 }
576 
OnProcessCrashed()577 void NaClBrowser::OnProcessCrashed() {
578   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
579   if (crash_times_.size() == kMaxCrashesPerInterval) {
580     crash_times_.pop_front();
581   }
582   base::Time time = base::Time::Now();
583   crash_times_.push_back(time);
584 }
585 
IsThrottled()586 bool NaClBrowser::IsThrottled() {
587   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
588   if (crash_times_.size() != kMaxCrashesPerInterval) {
589     return false;
590   }
591   base::TimeDelta delta = base::Time::Now() - crash_times_.front();
592   return delta.InSeconds() <= kCrashesIntervalInSeconds;
593 }
594 
595 }  // namespace nacl
596