1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
|
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_NACL_BROWSER_NACL_BROWSER_H_
#define COMPONENTS_NACL_BROWSER_NACL_BROWSER_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/containers/circular_deque.h"
#include "base/containers/lru_cache.h"
#include "base/files/file.h"
#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/nacl/browser/nacl_browser_delegate.h"
#include "components/nacl/browser/nacl_validation_cache.h"
namespace base {
class FileProxy;
}
namespace nacl {
static const int kGdbDebugStubPortUnknown = -1;
static const int kGdbDebugStubPortUnused = 0;
// 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.
static const int kFilePathCacheSize = 100;
// Open an immutable executable file that can be mmapped (or a read-only file).
// This function should only be called on a thread that can perform file IO.
base::File OpenNaClReadExecImpl(const base::FilePath& file_path,
bool is_executable);
// Represents shared state for all NaClProcessHost objects in the browser.
class NaClBrowser {
public:
static NaClBrowser* GetInstance();
NaClBrowser(const NaClBrowser&) = delete;
NaClBrowser& operator=(const NaClBrowser&) = delete;
// Will it be possible to launch a NaCl process, eventually?
bool IsOk() const;
// Are we ready to launch a NaCl process now? Implies IsOk().
bool IsReady() const;
// Attempt to asynchronously acquire all resources needed to start a process.
// This method is idempotent - it is safe to call multiple times.
void EnsureAllResourcesAvailable();
// Enqueues reply() in the message loop when all the resources needed to start
// a process have been acquired.
void WaitForResources(base::OnceClosure reply);
// Asynchronously attempt to get the IRT open.
// This is entailed by EnsureInitialized. This method is exposed as part of
// the public interface, however, so the IRT can be explicitly opened as
// early as possible to prevent autoupdate issues.
void EnsureIrtAvailable();
// Path to IRT. Available even before IRT is loaded.
const base::FilePath& GetIrtFilePath();
// IRT file handle, only available when IsReady().
const base::File& IrtFile() const;
// Methods for tracking the GDB debug stub port associated with each NaCl
// process.
void SetProcessGdbDebugStubPort(int process_id, int port);
int GetProcessGdbDebugStubPort(int process_id);
// While a test has a GDB debug port callback set, Chrome will allocate a
// currently-unused TCP port to the debug stub server, instead of a fixed
// one.
static void SetGdbDebugStubPortListenerForTest(
base::RepeatingCallback<void(int)> listener);
static void ClearGdbDebugStubPortListenerForTest();
enum ValidationCacheStatus {
CACHE_MISS = 0,
CACHE_HIT,
CACHE_MAX
};
bool ValidationCacheIsEnabled() const {
return validation_cache_is_enabled_;
}
const std::string& GetValidationCacheKey() const {
return validation_cache_.GetValidationCacheKey();
}
// The instance keeps information about NaCl executable files opened via
// PPAPI. This allows the NaCl process to get trusted information about the
// file directly from the browser process. In theory, a compromised renderer
// could provide a writable file handle or lie about the file's path. If we
// trusted the handle was read only but it was not, an mmapped file could be
// modified after validation, allowing an escape from the NaCl sandbox.
// Similarly, if we trusted the file path corresponded to the file handle but
// it did not, the validation cache could be tricked into bypassing validation
// for bad code.
// Instead of allowing these attacks, the NaCl process only trusts information
// it gets directly from the browser process. Because the information is
// stored in a cache of bounded size, it is not guaranteed the browser process
// will be able to provide the requested information. In these cases, the
// NaCl process must make conservative assumptions about the origin of the
// file.
// In theory, a compromised renderer could guess file tokens in an attempt to
// read files it normally doesn't have access to. This would not compromise
// the NaCl sandbox, however, and only has a 1 in ~2**120 chance of success
// per guess.
// TODO(ncbray): move the cache onto NaClProcessHost so that we don't need to
// rely on tokens being unguessable by another process.
void PutFilePath(const base::FilePath& path,
uint64_t* file_token_lo,
uint64_t* file_token_hi);
bool GetFilePath(uint64_t file_token_lo,
uint64_t file_token_hi,
base::FilePath* path);
bool QueryKnownToValidate(const std::string& signature, bool off_the_record);
void SetKnownToValidate(const std::string& signature, bool off_the_record);
void ClearValidationCache(base::OnceClosure callback);
void EarlyStartup();
// Set/get the NaClBrowserDelegate. The |delegate| must be set at startup,
// from the Browser's UI thread. It will be leaked at browser teardown.
static void SetDelegate(std::unique_ptr<NaClBrowserDelegate> delegate);
static NaClBrowserDelegate* GetDelegate();
static void ClearAndDeleteDelegate();
// Called whenever a NaCl process exits.
void OnProcessEnd(int process_id);
// Called whenever a NaCl process crashes, before OnProcessEnd().
void OnProcessCrashed();
// If "too many" crashes occur within a given time period, NaCl is throttled
// until the rate again drops below the threshold.
bool IsThrottled();
private:
enum NaClResourceState {
NaClResourceUninitialized,
NaClResourceRequested,
NaClResourceReady
};
static NaClBrowser* GetInstanceInternal();
NaClBrowser();
~NaClBrowser();
void InitIrtFilePath();
void OpenIrtLibraryFile();
void OnIrtOpened(std::unique_ptr<base::FileProxy> file_proxy,
base::File::Error error_code);
void InitValidationCacheFilePath();
void EnsureValidationCacheAvailable();
void OnValidationCacheLoaded(const std::string* data);
void RunWithoutValidationCache();
// Dispatch waiting tasks if we are ready, or if we know we'll never be ready.
void CheckWaiting();
// Indicate that it is impossible to launch a NaCl process.
void MarkAsFailed();
void MarkValidationCacheAsModified();
void PersistValidationCache();
base::File irt_file_;
base::FilePath irt_filepath_;
NaClResourceState irt_state_ = NaClResourceUninitialized;
NaClValidationCache validation_cache_;
NaClValidationCache off_the_record_validation_cache_;
base::FilePath validation_cache_file_path_;
bool validation_cache_is_enabled_ = false;
bool validation_cache_is_modified_ = false;
NaClResourceState validation_cache_state_ = NaClResourceUninitialized;
base::RepeatingCallback<void(int)> debug_stub_port_listener_;
// Map from process id to debug stub port if any.
typedef std::map<int, int> GdbDebugStubPortMap;
GdbDebugStubPortMap gdb_debug_stub_port_map_;
typedef base::HashingLRUCache<std::string, base::FilePath> PathCacheType;
PathCacheType path_cache_{kFilePathCacheSize};
// True if it is no longer possible to launch NaCl processes.
bool has_failed_ = false;
// A list of pending tasks to start NaCl processes.
std::vector<base::OnceClosure> waiting_;
base::circular_deque<base::Time> crash_times_;
scoped_refptr<base::SequencedTaskRunner> file_task_runner_ =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE});
};
} // namespace nacl
#endif // COMPONENTS_NACL_BROWSER_NACL_BROWSER_H_
|