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