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