• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 the V8 project authors. All rights reserved.
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 V8_SANDBOX_SANDBOX_H_
6 #define V8_SANDBOX_SANDBOX_H_
7 
8 #include "include/v8-internal.h"
9 #include "include/v8-platform.h"
10 #include "include/v8config.h"
11 #include "src/common/globals.h"
12 #include "testing/gtest/include/gtest/gtest_prod.h"  // nogncheck
13 
14 namespace v8 {
15 
16 namespace internal {
17 
18 #ifdef V8_SANDBOX_IS_AVAILABLE
19 
20 /**
21  * The V8 Sandbox.
22  *
23  * When enabled, V8 reserves a large region of virtual address space - the
24  * sandbox - and places most of its objects inside of it. It is then assumed
25  * that an attacker can, by exploiting a vulnerability in V8, corrupt memory
26  * inside the sandbox arbitrarily and from different threads. The sandbox
27  * attempts to stop an attacker from corrupting other memory in the process.
28  *
29  * The sandbox relies on a number of different mechanisms to achieve its goal.
30  * For example, objects inside the sandbox can reference each other through
31  * offsets from the start of the sandbox ("sandboxed pointers") instead of raw
32  * pointers, and external objects can be referenced through indices into a
33  * per-Isolate table of external pointers ("sandboxed external pointers").
34  *
35  * The pointer compression region, which contains most V8 objects, and inside
36  * of which compressed (32-bit) pointers are used, is located at the start of
37  * the sandbox. The remainder of the sandbox is mostly used for memory
38  * buffers, in particular ArrayBuffer backing stores and WASM memory cages.
39  *
40  * As the embedder is responsible for providing ArrayBuffer allocators, V8
41  * exposes the virtual address space backing the sandbox to the embedder.
42  */
43 class V8_EXPORT_PRIVATE Sandbox {
44  public:
45   // +-  ~~~  -+----------------------------------------  ~~~  -+-  ~~~  -+
46   // |  32 GB  |                 (Ideally) 1 TB                 |  32 GB  |
47   // |         |                                                |         |
48   // | Guard   |      4 GB      :  ArrayBuffer backing stores,  | Guard   |
49   // | Region  |    V8 Heap     :  WASM memory buffers, and     | Region  |
50   // | (front) |     Region     :  any other sandboxed objects. | (back)  |
51   // +-  ~~~  -+----------------+-----------------------  ~~~  -+-  ~~~  -+
52   //           ^                                                ^
53   //           base                                             base + size
54 
55   Sandbox() = default;
56 
57   Sandbox(const Sandbox&) = delete;
58   Sandbox& operator=(Sandbox&) = delete;
59 
60   bool Initialize(v8::VirtualAddressSpace* vas);
Disable()61   void Disable() {
62     CHECK(!initialized_);
63     disabled_ = true;
64   }
65 
66   void TearDown();
67 
is_initialized()68   bool is_initialized() const { return initialized_; }
is_disabled()69   bool is_disabled() const { return disabled_; }
is_enabled()70   bool is_enabled() const { return !disabled_; }
is_partially_reserved()71   bool is_partially_reserved() const { return is_partially_reserved_; }
72 
base()73   Address base() const { return base_; }
end()74   Address end() const { return end_; }
size()75   size_t size() const { return size_; }
76 
base_address()77   Address base_address() const { return reinterpret_cast<Address>(&base_); }
end_address()78   Address end_address() const { return reinterpret_cast<Address>(&end_); }
size_address()79   Address size_address() const { return reinterpret_cast<Address>(&size_); }
80 
page_allocator()81   v8::PageAllocator* page_allocator() const {
82     return sandbox_page_allocator_.get();
83   }
84 
address_space()85   v8::VirtualAddressSpace* address_space() const {
86     return address_space_.get();
87   }
88 
Contains(Address addr)89   bool Contains(Address addr) const {
90     return addr >= base_ && addr < base_ + size_;
91   }
92 
Contains(void * ptr)93   bool Contains(void* ptr) const {
94     return Contains(reinterpret_cast<Address>(ptr));
95   }
96 
97 #ifdef V8_SANDBOXED_POINTERS
98   class SandboxedPointerConstants final {
99    public:
empty_backing_store_buffer()100     Address empty_backing_store_buffer() const {
101       return empty_backing_store_buffer_;
102     }
empty_backing_store_buffer_address()103     Address empty_backing_store_buffer_address() const {
104       return reinterpret_cast<Address>(&empty_backing_store_buffer_);
105     }
set_empty_backing_store_buffer(Address value)106     void set_empty_backing_store_buffer(Address value) {
107       empty_backing_store_buffer_ = value;
108     }
109 
Reset()110     void Reset() { empty_backing_store_buffer_ = 0; }
111 
112    private:
113     Address empty_backing_store_buffer_ = 0;
114   };
constants()115   const SandboxedPointerConstants& constants() const { return constants_; }
116 #endif
117 
118  private:
119   // The SequentialUnmapperTest calls the private Initialize method to create a
120   // sandbox without guard regions, which would consume too much memory.
121   friend class SequentialUnmapperTest;
122 
123   // These tests call the private Initialize methods below.
124   FRIEND_TEST(SandboxTest, InitializationWithSize);
125   FRIEND_TEST(SandboxTest, PartiallyReservedSandboxInitialization);
126   FRIEND_TEST(SandboxTest, PartiallyReservedSandboxPageAllocation);
127 
128   // We allow tests to disable the guard regions around the sandbox. This is
129   // useful for example for tests like the SequentialUnmapperTest which track
130   // page allocations and so would incur a large overhead from the guard
131   // regions. The provided virtual address space must be able to allocate
132   // subspaces. The size must be a multiple of the allocation granularity of the
133   // virtual memory space.
134   bool Initialize(v8::VirtualAddressSpace* vas, size_t size,
135                   bool use_guard_regions);
136 
137   // Used when reserving virtual memory is too expensive. A partially reserved
138   // sandbox does not reserve all of its virtual memory and so doesn't have the
139   // desired security properties as unrelated mappings could end up inside of
140   // it and be corrupted. The size and size_to_reserve parameters must be
141   // multiples of the allocation granularity of the virtual address space.
142   bool InitializeAsPartiallyReservedSandbox(v8::VirtualAddressSpace* vas,
143                                             size_t size,
144                                             size_t size_to_reserve);
145 
146   // Initialize the constant objects for this sandbox. Called by the Initialize
147   // methods above.
148   void InitializeConstants();
149 
150   Address base_ = kNullAddress;
151   Address end_ = kNullAddress;
152   size_t size_ = 0;
153 
154   // Base and size of the virtual memory reservation backing this sandbox.
155   // These can be different from the sandbox base and size due to guard regions
156   // or when a fake sandbox is used.
157   Address reservation_base_ = kNullAddress;
158   size_t reservation_size_ = 0;
159 
160   bool initialized_ = false;
161   bool disabled_ = false;
162   bool is_partially_reserved_ = false;
163 
164   // The virtual address subspace backing the sandbox.
165   std::unique_ptr<v8::VirtualAddressSpace> address_space_;
166 
167   // The page allocator instance for this sandbox.
168   std::unique_ptr<v8::PageAllocator> sandbox_page_allocator_;
169 
170 #ifdef V8_SANDBOXED_POINTERS
171   // Constant objects inside this sandbox.
172   SandboxedPointerConstants constants_;
173 #endif
174 };
175 
176 #endif  // V8_SANDBOX_IS_AVAILABLE
177 
178 #ifdef V8_SANDBOX
179 // This function is only available when the sandbox is actually used.
180 V8_EXPORT_PRIVATE Sandbox* GetProcessWideSandbox();
181 #endif
182 
EmptyBackingStoreBuffer()183 V8_INLINE void* EmptyBackingStoreBuffer() {
184 #ifdef V8_SANDBOXED_POINTERS
185   return reinterpret_cast<void*>(
186       GetProcessWideSandbox()->constants().empty_backing_store_buffer());
187 #else
188   return nullptr;
189 #endif
190 }
191 
192 }  // namespace internal
193 }  // namespace v8
194 
195 #endif  // V8_SANDBOX_SANDBOX_H_
196