blob: 20f2343db50e08dfacc099e94c7f839f524d591f [file] [log] [blame]
// 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_