11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#ifndef V8_SANDBOX_EXTERNAL_POINTER_TABLE_H_ 61cb0ef41Sopenharmony_ci#define V8_SANDBOX_EXTERNAL_POINTER_TABLE_H_ 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ci#include "include/v8config.h" 91cb0ef41Sopenharmony_ci#include "src/base/atomicops.h" 101cb0ef41Sopenharmony_ci#include "src/base/memory.h" 111cb0ef41Sopenharmony_ci#include "src/base/platform/mutex.h" 121cb0ef41Sopenharmony_ci#include "src/common/globals.h" 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_ci#ifdef V8_SANDBOX_IS_AVAILABLE 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_cinamespace v8 { 171cb0ef41Sopenharmony_cinamespace internal { 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_ciclass Isolate; 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci/** 221cb0ef41Sopenharmony_ci * A table storing pointers to objects outside the sandbox. 231cb0ef41Sopenharmony_ci * 241cb0ef41Sopenharmony_ci * An external pointer table provides the basic mechanisms to ensure 251cb0ef41Sopenharmony_ci * memory-safe access to objects located outside the sandbox, but referenced 261cb0ef41Sopenharmony_ci * from within it. When an external pointer table is used, objects located 271cb0ef41Sopenharmony_ci * inside the sandbox reference outside objects through indices into the table. 281cb0ef41Sopenharmony_ci * 291cb0ef41Sopenharmony_ci * Type safety can be ensured by using type-specific tags for the external 301cb0ef41Sopenharmony_ci * pointers. These tags will be ORed into the unused top bits of the pointer 311cb0ef41Sopenharmony_ci * when storing them and will be ANDed away when loading the pointer later 321cb0ef41Sopenharmony_ci * again. If a pointer of the wrong type is accessed, some of the top bits will 331cb0ef41Sopenharmony_ci * remain in place, rendering the pointer inaccessible. 341cb0ef41Sopenharmony_ci * 351cb0ef41Sopenharmony_ci * Temporal memory safety is achieved through garbage collection of the table, 361cb0ef41Sopenharmony_ci * which ensures that every entry is either an invalid pointer or a valid 371cb0ef41Sopenharmony_ci * pointer pointing to a live object. 381cb0ef41Sopenharmony_ci * 391cb0ef41Sopenharmony_ci * Spatial memory safety can, if necessary, be ensured by storing the size of a 401cb0ef41Sopenharmony_ci * referenced object together with the object itself outside the sandbox, and 411cb0ef41Sopenharmony_ci * referencing both through a single entry in the table. 421cb0ef41Sopenharmony_ci * 431cb0ef41Sopenharmony_ci * The garbage collection algorithm for the table works as follows: 441cb0ef41Sopenharmony_ci * - The top bit of every entry is reserved for the marking bit. 451cb0ef41Sopenharmony_ci * - Every store to an entry automatically sets the marking bit when ORing 461cb0ef41Sopenharmony_ci * with the tag. This avoids the need for write barriers. 471cb0ef41Sopenharmony_ci * - Every load of an entry automatically removes the marking bit when ANDing 481cb0ef41Sopenharmony_ci * with the inverted tag. 491cb0ef41Sopenharmony_ci * - When the GC marking visitor finds a live object with an external pointer, 501cb0ef41Sopenharmony_ci * it marks the corresponding entry as alive through Mark(), which sets the 511cb0ef41Sopenharmony_ci * marking bit using an atomic CAS operation. 521cb0ef41Sopenharmony_ci * - When marking is finished, Sweep() iterates of the table once while the 531cb0ef41Sopenharmony_ci * mutator is stopped and builds a freelist from all dead entries while also 541cb0ef41Sopenharmony_ci * removing the marking bit from any live entry. 551cb0ef41Sopenharmony_ci * 561cb0ef41Sopenharmony_ci * The freelist is a singly-linked list, using the lower 32 bits of each entry 571cb0ef41Sopenharmony_ci * to store the index of the next free entry. When the freelist is empty and a 581cb0ef41Sopenharmony_ci * new entry is allocated, the table grows in place and the freelist is 591cb0ef41Sopenharmony_ci * re-populated from the newly added entries. 601cb0ef41Sopenharmony_ci */ 611cb0ef41Sopenharmony_ciclass V8_EXPORT_PRIVATE ExternalPointerTable { 621cb0ef41Sopenharmony_ci public: 631cb0ef41Sopenharmony_ci // Size of an ExternalPointerTable, for layout computation in IsolateData. 641cb0ef41Sopenharmony_ci // Asserted to be equal to the actual size in external-pointer-table.cc. 651cb0ef41Sopenharmony_ci static int constexpr kSize = 3 * kSystemPointerSize; 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_ci ExternalPointerTable() = default; 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci // Initializes this external pointer table by reserving the backing memory 701cb0ef41Sopenharmony_ci // and initializing the freelist. 711cb0ef41Sopenharmony_ci inline void Init(Isolate* isolate); 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci // Resets this external pointer table and deletes all associated memory. 741cb0ef41Sopenharmony_ci inline void TearDown(); 751cb0ef41Sopenharmony_ci 761cb0ef41Sopenharmony_ci // Retrieves the entry at the given index. 771cb0ef41Sopenharmony_ci // 781cb0ef41Sopenharmony_ci // This method is atomic and can be called from background threads. 791cb0ef41Sopenharmony_ci inline Address Get(uint32_t index, ExternalPointerTag tag) const; 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci // Sets the entry at the given index to the given value. 821cb0ef41Sopenharmony_ci // 831cb0ef41Sopenharmony_ci // This method is atomic and can be called from background threads. 841cb0ef41Sopenharmony_ci inline void Set(uint32_t index, Address value, ExternalPointerTag tag); 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci // Allocates a new entry in the external pointer table. The caller must 871cb0ef41Sopenharmony_ci // initialize the entry afterwards through set(). In particular, the caller is 881cb0ef41Sopenharmony_ci // responsible for setting the mark bit of the new entry. 891cb0ef41Sopenharmony_ci // TODO(saelo) this can fail, in which case we should probably do GC + retry. 901cb0ef41Sopenharmony_ci // 911cb0ef41Sopenharmony_ci // This method is atomic and can be called from background threads. 921cb0ef41Sopenharmony_ci inline uint32_t Allocate(); 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci // Runtime function called from CSA. Internally just calls Allocate(). 951cb0ef41Sopenharmony_ci static uint32_t AllocateEntry(ExternalPointerTable* table); 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci // Marks the specified entry as alive. 981cb0ef41Sopenharmony_ci // 991cb0ef41Sopenharmony_ci // This method is atomic and can be called from background threads. 1001cb0ef41Sopenharmony_ci inline void Mark(uint32_t index); 1011cb0ef41Sopenharmony_ci 1021cb0ef41Sopenharmony_ci // Frees unmarked entries. 1031cb0ef41Sopenharmony_ci // 1041cb0ef41Sopenharmony_ci // This method must be called on the mutator thread or while that thread is 1051cb0ef41Sopenharmony_ci // stopped. 1061cb0ef41Sopenharmony_ci // 1071cb0ef41Sopenharmony_ci // Returns the number of live entries after sweeping. 1081cb0ef41Sopenharmony_ci uint32_t Sweep(Isolate* isolate); 1091cb0ef41Sopenharmony_ci 1101cb0ef41Sopenharmony_ci private: 1111cb0ef41Sopenharmony_ci // Required for Isolate::CheckIsolateLayout(). 1121cb0ef41Sopenharmony_ci friend class Isolate; 1131cb0ef41Sopenharmony_ci 1141cb0ef41Sopenharmony_ci // An external pointer table grows in blocks of this size. This is also the 1151cb0ef41Sopenharmony_ci // initial size of the table. 1161cb0ef41Sopenharmony_ci static const size_t kBlockSize = 64 * KB; 1171cb0ef41Sopenharmony_ci static const size_t kEntriesPerBlock = kBlockSize / kSystemPointerSize; 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci static const Address kExternalPointerMarkBit = 1ULL << 63; 1201cb0ef41Sopenharmony_ci 1211cb0ef41Sopenharmony_ci // Returns true if this external pointer table has been initialized. 1221cb0ef41Sopenharmony_ci bool is_initialized() { return buffer_ != kNullAddress; } 1231cb0ef41Sopenharmony_ci 1241cb0ef41Sopenharmony_ci // Extends the table and adds newly created entries to the freelist. Returns 1251cb0ef41Sopenharmony_ci // the new freelist head. When calling this method, mutex_ must be locked. 1261cb0ef41Sopenharmony_ci // 1271cb0ef41Sopenharmony_ci // TODO(saelo) this can fail, deal with that appropriately. 1281cb0ef41Sopenharmony_ci uint32_t Grow(); 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci // Computes the address of the specified entry. 1311cb0ef41Sopenharmony_ci inline Address entry_address(uint32_t index) const { 1321cb0ef41Sopenharmony_ci return buffer_ + index * sizeof(Address); 1331cb0ef41Sopenharmony_ci } 1341cb0ef41Sopenharmony_ci 1351cb0ef41Sopenharmony_ci // Loads the value at the given index. This method is non-atomic, only use it 1361cb0ef41Sopenharmony_ci // when no other threads can currently access the table. 1371cb0ef41Sopenharmony_ci inline Address load(uint32_t index) const { 1381cb0ef41Sopenharmony_ci return base::Memory<Address>(entry_address(index)); 1391cb0ef41Sopenharmony_ci } 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ci // Stores the provided value at the given index. This method is non-atomic, 1421cb0ef41Sopenharmony_ci // only use it when no other threads can currently access the table. 1431cb0ef41Sopenharmony_ci inline void store(uint32_t index, Address value) { 1441cb0ef41Sopenharmony_ci base::Memory<Address>(entry_address(index)) = value; 1451cb0ef41Sopenharmony_ci } 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci // Atomically loads the value at the given index. 1481cb0ef41Sopenharmony_ci inline Address load_atomic(uint32_t index) const { 1491cb0ef41Sopenharmony_ci auto addr = reinterpret_cast<base::Atomic64*>(entry_address(index)); 1501cb0ef41Sopenharmony_ci return base::Relaxed_Load(addr); 1511cb0ef41Sopenharmony_ci } 1521cb0ef41Sopenharmony_ci 1531cb0ef41Sopenharmony_ci // Atomically stores the provided value at the given index. 1541cb0ef41Sopenharmony_ci inline void store_atomic(uint32_t index, Address value) { 1551cb0ef41Sopenharmony_ci auto addr = reinterpret_cast<base::Atomic64*>(entry_address(index)); 1561cb0ef41Sopenharmony_ci base::Relaxed_Store(addr, value); 1571cb0ef41Sopenharmony_ci } 1581cb0ef41Sopenharmony_ci 1591cb0ef41Sopenharmony_ci static bool is_marked(Address entry) { 1601cb0ef41Sopenharmony_ci return (entry & kExternalPointerMarkBit) == kExternalPointerMarkBit; 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_ci static Address set_mark_bit(Address entry) { 1641cb0ef41Sopenharmony_ci return entry | kExternalPointerMarkBit; 1651cb0ef41Sopenharmony_ci } 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ci static Address clear_mark_bit(Address entry) { 1681cb0ef41Sopenharmony_ci return entry & ~kExternalPointerMarkBit; 1691cb0ef41Sopenharmony_ci } 1701cb0ef41Sopenharmony_ci 1711cb0ef41Sopenharmony_ci static bool is_free(Address entry) { 1721cb0ef41Sopenharmony_ci return (entry & kExternalPointerFreeEntryTag) == 1731cb0ef41Sopenharmony_ci kExternalPointerFreeEntryTag; 1741cb0ef41Sopenharmony_ci } 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci static Address make_freelist_entry(uint32_t current_freelist_head) { 1771cb0ef41Sopenharmony_ci // The next freelist entry is stored in the lower 32 bits of the entry. 1781cb0ef41Sopenharmony_ci Address entry = current_freelist_head; 1791cb0ef41Sopenharmony_ci return entry | kExternalPointerFreeEntryTag; 1801cb0ef41Sopenharmony_ci } 1811cb0ef41Sopenharmony_ci 1821cb0ef41Sopenharmony_ci // The buffer backing this table. This is const after initialization. Should 1831cb0ef41Sopenharmony_ci // only be accessed using the load_x() and store_x() methods, which take care 1841cb0ef41Sopenharmony_ci // of atomicicy if necessary. 1851cb0ef41Sopenharmony_ci Address buffer_ = kNullAddress; 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ci // The current capacity of this table, which is the number of usable entries. 1881cb0ef41Sopenharmony_ci uint32_t capacity_ = 0; 1891cb0ef41Sopenharmony_ci 1901cb0ef41Sopenharmony_ci // The index of the first entry on the freelist or zero if the list is empty. 1911cb0ef41Sopenharmony_ci uint32_t freelist_head_ = 0; 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_ci // Lock protecting the slow path for entry allocation, in particular Grow(). 1941cb0ef41Sopenharmony_ci // As the size of this structure must be predictable (it's part of 1951cb0ef41Sopenharmony_ci // IsolateData), it cannot directly contain a Mutex and so instead contains a 1961cb0ef41Sopenharmony_ci // pointer to one. 1971cb0ef41Sopenharmony_ci base::Mutex* mutex_ = nullptr; 1981cb0ef41Sopenharmony_ci}; 1991cb0ef41Sopenharmony_ci 2001cb0ef41Sopenharmony_ci} // namespace internal 2011cb0ef41Sopenharmony_ci} // namespace v8 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ci#endif // V8_SANDBOX_IS_AVAILABLE 2041cb0ef41Sopenharmony_ci 2051cb0ef41Sopenharmony_ci#endif // V8_SANDBOX_EXTERNAL_POINTER_TABLE_H_ 206