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