blob: 59d38aa64ab25bd5151a3b72bbb9830555c1434c [file] [edit]
// Copyright 2023 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_TRUSTED_POINTER_TABLE_H_
#define V8_SANDBOX_TRUSTED_POINTER_TABLE_H_
#include "include/v8config.h"
#include "src/base/atomicops.h"
#include "src/base/memory.h"
#include "src/base/platform/mutex.h"
#include "src/common/globals.h"
#include "src/sandbox/external-entity-table.h"
#include "src/sandbox/indirect-pointer-tag.h"
#ifdef V8_ENABLE_SANDBOX
namespace v8 {
namespace internal {
class Isolate;
class Counters;
/**
* The entries of a TrustedPointerTable.
*
* Each entry contains an (absolute) pointer to a TrustedObject.
*/
struct TrustedPointerTableEntry {
// Make this entry a "regular" entry, containing an absolute pointer to a
// TrustedObject.
inline void MakeTrustedPointerEntry(Address value, bool mark_as_alive);
// Make this entry a freelist entry, containing the index of the next entry
// on the freelist.
inline void MakeFreelistEntry(uint32_t next_entry_index);
// Retrieve the pointer stored in this entry.
// This entry must not be a freelist entry.
inline Address GetContent() const;
// Store the given pointer in this entry.
// This entry must not be a freelist entry.
inline void SetContent(Address value);
// Returns true if this entry is a freelist entry.
inline bool IsFreelistEntry() const;
// Get the index of the next entry on the freelist. This method may be
// called even when the entry is not a freelist entry. However, the result
// is only valid if this is a freelist entry. This behaviour is required
// for efficient entry allocation, see TryAllocateEntryFromFreelist.
inline uint32_t GetNextFreelistEntryIndex() const;
// Mark this entry as alive during garbage collection.
inline void Mark();
// Unmark this entry during sweeping.
inline void Unmark();
// Test whether this entry is currently marked as alive.
inline bool IsMarked() const;
private:
friend class TrustedPointerTable;
// Freelist entries contain the index of the next free entry in their lower 32
// bits and this tag in the upper 32 bits.
static constexpr Address kFreeEntryTag = 0xffffffffULL << 32;
// The marking bit is stored in the content_ field, see below.
static constexpr Address kMarkingBit = 1;
// The pointer to the TrustedObject stored in this entry. Also contains the
// marking bit: since this is a tagged pointer to a V8 HeapObject, we know
// that it will be 4-byte aligned and that the LSB should always be set. We
// therefore use the LSB as marking bit. In this way:
// - When loading the pointer, we only need to perform an unconditional OR 1
// to get the correctly tagged pointer
// - When storing the pointer we don't need to do anything since the tagged
// pointer will automatically be marked
static_assert(kMarkingBit == kHeapObjectTag);
std::atomic<Address> content_;
};
static_assert(sizeof(TrustedPointerTableEntry) ==
kTrustedPointerTableEntrySize);
/**
* A table containing (full) pointers to TrustedObjects.
*
* When the sandbox is enabled, a trusted pointer table (TPT) is used to safely
* reference trusted heap objects located in one of the trusted spaces outside
* of the sandbox. The TPT guarantees that every access to an object via a
* trusted pointer (an index into the table) either results in an invalid
* pointer or a valid pointer to a valid (live) object of the expected type.
*
* The TPT is very similar to the external pointer table (EPT), but is used to
* reference V8 HeapObjects (located inside a V8 heap) rather than C++ objects
* (typically located on one of the system heaps). As such, the garbage
* collector needs to be aware of the table indirection.
*/
class V8_EXPORT_PRIVATE TrustedPointerTable
: public ExternalEntityTable<TrustedPointerTableEntry,
kTrustedPointerTableReservationSize> {
public:
// Size of a TrustedPointerTable, for layout computation in IsolateData.
static int constexpr kSize = 2 * kSystemPointerSize;
static_assert(kMaxTrustedPointers == kMaxCapacity);
TrustedPointerTable() = default;
TrustedPointerTable(const TrustedPointerTable&) = delete;
TrustedPointerTable& operator=(const TrustedPointerTable&) = delete;
// The Spaces used by a TrustedPointerTable.
using Space = ExternalEntityTable<
TrustedPointerTableEntry,
kTrustedPointerTableReservationSize>::SpaceWithBlackAllocationSupport;
// Retrieves the content of the entry referenced by the given handle.
//
// This method is atomic and can be called from background threads.
inline Address Get(TrustedPointerHandle handle) const;
// Sets the content of the entry referenced by the given handle.
//
// This method is atomic and can be called from background threads.
inline void Set(TrustedPointerHandle handle, Address pointer,
IndirectPointerTag tag);
// Allocates a new entry in the table and initialize it.
//
// This method is atomic and can be called from background threads.
inline TrustedPointerHandle AllocateAndInitializeEntry(
Space* space, Address pointer, IndirectPointerTag tag);
// Marks the specified entry as alive.
//
// This method is atomic and can be called from background threads.
inline void Mark(Space* space, TrustedPointerHandle handle);
// Frees all unmarked entries in the given space.
//
// This method must only be called while mutator threads are stopped as it is
// not safe to allocate table entries while a space is being swept.
//
// Returns the number of live entries after sweeping.
uint32_t Sweep(Space* space, Counters* counters);
// Iterate over all active entries in the given space.
//
// The callback function will be invoked once for every entry that is
// currently in use, i.e. has been allocated and not yet freed, and will
// receive the handle and content of that entry.
template <typename Callback>
void IterateActiveEntriesIn(Space* space, Callback callback);
// The base address of this table, for use in JIT compilers.
Address base_address() const { return base(); }
private:
inline uint32_t HandleToIndex(TrustedPointerHandle handle) const;
inline TrustedPointerHandle IndexToHandle(uint32_t index) const;
// Ensure that the value is valid before storing it into this table.
inline void Validate(Address pointer, IndirectPointerTag tag);
};
static_assert(sizeof(TrustedPointerTable) == TrustedPointerTable::kSize);
} // namespace internal
} // namespace v8
#endif // V8_ENABLE_SANDBOX
#endif // V8_SANDBOX_TRUSTED_POINTER_TABLE_H_