summaryrefslogtreecommitdiff
path: root/deps/v8/src/sandbox/sandbox.h
blob: 20f2343db50e08dfacc099e94c7f839f524d591f (plain)
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
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_SANDBOX_SANDBOX_H_
#define V8_SANDBOX_SANDBOX_H_

#include "include/v8-internal.h"
#include "include/v8-platform.h"
#include "include/v8config.h"
#include "src/common/globals.h"
#include "testing/gtest/include/gtest/gtest_prod.h"  // nogncheck

namespace v8 {

namespace internal {

#ifdef V8_SANDBOX_IS_AVAILABLE

/**
 * The V8 Sandbox.
 *
 * When enabled, V8 reserves a large region of virtual address space - the
 * sandbox - and places most of its objects inside of it. It is then assumed
 * that an attacker can, by exploiting a vulnerability in V8, corrupt memory
 * inside the sandbox arbitrarily and from different threads. The sandbox
 * attempts to stop an attacker from corrupting other memory in the process.
 *
 * The sandbox relies on a number of different mechanisms to achieve its goal.
 * For example, objects inside the sandbox can reference each other through
 * offsets from the start of the sandbox ("sandboxed pointers") instead of raw
 * pointers, and external objects can be referenced through indices into a
 * per-Isolate table of external pointers ("sandboxed external pointers").
 *
 * The pointer compression region, which contains most V8 objects, and inside
 * of which compressed (32-bit) pointers are used, is located at the start of
 * the sandbox. The remainder of the sandbox is mostly used for memory
 * buffers, in particular ArrayBuffer backing stores and WASM memory cages.
 *
 * As the embedder is responsible for providing ArrayBuffer allocators, V8
 * exposes the virtual address space backing the sandbox to the embedder.
 */
class V8_EXPORT_PRIVATE Sandbox {
 public:
  // +-  ~~~  -+----------------------------------------  ~~~  -+-  ~~~  -+
  // |  32 GB  |                 (Ideally) 1 TB                 |  32 GB  |
  // |         |                                                |         |
  // | Guard   |      4 GB      :  ArrayBuffer backing stores,  | Guard   |
  // | Region  |    V8 Heap     :  WASM memory buffers, and     | Region  |
  // | (front) |     Region     :  any other sandboxed objects. | (back)  |
  // +-  ~~~  -+----------------+-----------------------  ~~~  -+-  ~~~  -+
  //           ^                                                ^
  //           base                                             base + size

  Sandbox() = default;

  Sandbox(const Sandbox&) = delete;
  Sandbox& operator=(Sandbox&) = delete;

  bool Initialize(v8::VirtualAddressSpace* vas);
  void Disable() {
    CHECK(!initialized_);
    disabled_ = true;
  }

  void TearDown();

  bool is_initialized() const { return initialized_; }
  bool is_disabled() const { return disabled_; }
  bool is_enabled() const { return !disabled_; }
  bool is_partially_reserved() const { return is_partially_reserved_; }

  Address base() const { return base_; }
  Address end() const { return end_; }
  size_t size() const { return size_; }

  Address base_address() const { return reinterpret_cast<Address>(&base_); }
  Address end_address() const { return reinterpret_cast<Address>(&end_); }
  Address size_address() const { return reinterpret_cast<Address>(&size_); }

  v8::PageAllocator* page_allocator() const {
    return sandbox_page_allocator_.get();
  }

  v8::VirtualAddressSpace* address_space() const {
    return address_space_.get();
  }

  bool Contains(Address addr) const {
    return addr >= base_ && addr < base_ + size_;
  }

  bool Contains(void* ptr) const {
    return Contains(reinterpret_cast<Address>(ptr));
  }

#ifdef V8_SANDBOXED_POINTERS
  class SandboxedPointerConstants final {
   public:
    Address empty_backing_store_buffer() const {
      return empty_backing_store_buffer_;
    }
    Address empty_backing_store_buffer_address() const {
      return reinterpret_cast<Address>(&empty_backing_store_buffer_);
    }
    void set_empty_backing_store_buffer(Address value) {
      empty_backing_store_buffer_ = value;
    }

    void Reset() { empty_backing_store_buffer_ = 0; }

   private:
    Address empty_backing_store_buffer_ = 0;
  };
  const SandboxedPointerConstants& constants() const { return constants_; }
#endif

 private:
  // The SequentialUnmapperTest calls the private Initialize method to create a
  // sandbox without guard regions, which would consume too much memory.
  friend class SequentialUnmapperTest;

  // These tests call the private Initialize methods below.
  FRIEND_TEST(SandboxTest, InitializationWithSize);
  FRIEND_TEST(SandboxTest, PartiallyReservedSandboxInitialization);
  FRIEND_TEST(SandboxTest, PartiallyReservedSandboxPageAllocation);

  // We allow tests to disable the guard regions around the sandbox. This is
  // useful for example for tests like the SequentialUnmapperTest which track
  // page allocations and so would incur a large overhead from the guard
  // regions. The provided virtual address space must be able to allocate
  // subspaces. The size must be a multiple of the allocation granularity of the
  // virtual memory space.
  bool Initialize(v8::VirtualAddressSpace* vas, size_t size,
                  bool use_guard_regions);

  // Used when reserving virtual memory is too expensive. A partially reserved
  // sandbox does not reserve all of its virtual memory and so doesn't have the
  // desired security properties as unrelated mappings could end up inside of
  // it and be corrupted. The size and size_to_reserve parameters must be
  // multiples of the allocation granularity of the virtual address space.
  bool InitializeAsPartiallyReservedSandbox(v8::VirtualAddressSpace* vas,
                                            size_t size,
                                            size_t size_to_reserve);

  // Initialize the constant objects for this sandbox. Called by the Initialize
  // methods above.
  void InitializeConstants();

  Address base_ = kNullAddress;
  Address end_ = kNullAddress;
  size_t size_ = 0;

  // Base and size of the virtual memory reservation backing this sandbox.
  // These can be different from the sandbox base and size due to guard regions
  // or when a fake sandbox is used.
  Address reservation_base_ = kNullAddress;
  size_t reservation_size_ = 0;

  bool initialized_ = false;
  bool disabled_ = false;
  bool is_partially_reserved_ = false;

  // The virtual address subspace backing the sandbox.
  std::unique_ptr<v8::VirtualAddressSpace> address_space_;

  // The page allocator instance for this sandbox.
  std::unique_ptr<v8::PageAllocator> sandbox_page_allocator_;

#ifdef V8_SANDBOXED_POINTERS
  // Constant objects inside this sandbox.
  SandboxedPointerConstants constants_;
#endif
};

#endif  // V8_SANDBOX_IS_AVAILABLE

#ifdef V8_SANDBOX
// This function is only available when the sandbox is actually used.
V8_EXPORT_PRIVATE Sandbox* GetProcessWideSandbox();
#endif

V8_INLINE void* EmptyBackingStoreBuffer() {
#ifdef V8_SANDBOXED_POINTERS
  return reinterpret_cast<void*>(
      GetProcessWideSandbox()->constants().empty_backing_store_buffer());
#else
  return nullptr;
#endif
}

}  // namespace internal
}  // namespace v8

#endif  // V8_SANDBOX_SANDBOX_H_