#ifndef V8_SANDBOX_EXTERNAL_POINTER_TABLE_INL_H_
#define V8_SANDBOX_EXTERNAL_POINTER_TABLE_INL_H_
#include "src/sandbox/external-pointer-table.h"
#include "src/sandbox/compactible-external-entity-table-inl.h"
#include "src/sandbox/external-pointer.h"
#ifdef V8_COMPRESS_POINTERS
namespace v8 {
namespace internal {
void ExternalPointerTableEntry::MakeExternalPointerEntry(Address value,
ExternalPointerTag tag,
bool mark_as_alive) {
DCHECK_EQ(0, value & kExternalPointerTagAndMarkbitMask);
DCHECK_NE(tag, kExternalPointerFreeEntryTag);
DCHECK_NE(tag, kExternalPointerEvacuationEntryTag);
Payload new_payload(value, tag);
if (V8_UNLIKELY(mark_as_alive)) {
new_payload.SetMarkBit();
}
payload_.store(new_payload, std::memory_order_relaxed);
MaybeUpdateRawPointerForLSan(value);
}
Address ExternalPointerTableEntry::GetExternalPointer(
ExternalPointerTagRange tag_range) const {
auto payload = payload_.load(std::memory_order_relaxed);
DCHECK(payload.ContainsPointer());
return payload.Untag(tag_range);
}
void ExternalPointerTableEntry::SetExternalPointer(Address value,
ExternalPointerTag tag) {
DCHECK_EQ(0, value & kExternalPointerTagAndMarkbitMask);
DCHECK(payload_.load(std::memory_order_relaxed).ContainsPointer());
Payload new_payload(value, tag);
new_payload.SetMarkBit();
payload_.store(new_payload, std::memory_order_relaxed);
MaybeUpdateRawPointerForLSan(value);
}
bool ExternalPointerTableEntry::HasExternalPointer(
ExternalPointerTagRange tag_range) const {
auto payload = payload_.load(std::memory_order_relaxed);
if (!payload.ContainsPointer()) return false;
return payload.IsTaggedWithTagIn(tag_range);
}
Address ExternalPointerTableEntry::ExchangeExternalPointer(
Address value, ExternalPointerTag tag) {
DCHECK_EQ(0, value & kExternalPointerTagAndMarkbitMask);
Payload new_payload(value, tag);
new_payload.SetMarkBit();
Payload old_payload =
payload_.exchange(new_payload, std::memory_order_relaxed);
DCHECK(old_payload.ContainsPointer());
MaybeUpdateRawPointerForLSan(value);
return old_payload.Untag(tag);
}
ExternalPointerTag ExternalPointerTableEntry::GetExternalPointerTag() const {
auto payload = payload_.load(std::memory_order_relaxed);
DCHECK(payload.ContainsPointer());
return payload.ExtractTag();
}
Address ExternalPointerTableEntry::ExtractManagedResourceOrNull() const {
auto payload = payload_.load(std::memory_order_relaxed);
ExternalPointerTag tag = payload.ExtractTag();
if (IsManagedExternalPointerType(tag)) {
return payload.Untag(tag);
}
return kNullAddress;
}
void ExternalPointerTableEntry::MakeZappedEntry() {
Payload new_payload(kNullAddress, kExternalPointerZappedEntryTag);
payload_.store(new_payload, std::memory_order_relaxed);
}
void ExternalPointerTableEntry::MakeFreelistEntry(uint32_t next_entry_index) {
static_assert(kMaxExternalPointers <= std::numeric_limits<uint32_t>::max());
Payload new_payload(next_entry_index, kExternalPointerFreeEntryTag);
payload_.store(new_payload, std::memory_order_relaxed);
}
uint32_t ExternalPointerTableEntry::GetNextFreelistEntryIndex() const {
auto payload = payload_.load(std::memory_order_relaxed);
return payload.ExtractFreelistLink();
}
void ExternalPointerTableEntry::Mark() {
auto old_payload = payload_.load(std::memory_order_relaxed);
DCHECK(old_payload.ContainsPointer());
auto new_payload = old_payload;
new_payload.SetMarkBit();
bool success = payload_.compare_exchange_strong(old_payload, new_payload,
std::memory_order_relaxed);
DCHECK(success || old_payload.HasMarkBitSet());
USE(success);
}
void ExternalPointerTableEntry::MakeEvacuationEntry(Address handle_location) {
Payload new_payload(handle_location, kExternalPointerEvacuationEntryTag);
payload_.store(new_payload, std::memory_order_relaxed);
}
bool ExternalPointerTableEntry::HasEvacuationEntry() const {
auto payload = payload_.load(std::memory_order_relaxed);
return payload.ContainsEvacuationEntry();
}
void ExternalPointerTableEntry::Evacuate(ExternalPointerTableEntry& dest,
EvacuateMarkMode mode) {
auto payload = payload_.load(std::memory_order_relaxed);
DCHECK(payload.ContainsPointer());
switch (mode) {
case EvacuateMarkMode::kTransferMark:
break;
case EvacuateMarkMode::kLeaveUnmarked:
DCHECK(!payload.HasMarkBitSet());
break;
case EvacuateMarkMode::kClearMark:
DCHECK(payload.HasMarkBitSet());
payload.ClearMarkBit();
break;
}
dest.payload_.store(payload, std::memory_order_relaxed);
#if defined(LEAK_SANITIZER)
dest.raw_pointer_for_lsan_ = raw_pointer_for_lsan_;
#endif
MakeZappedEntry();
}
void ExternalPointerTableEntry::CopyFrom(const ExternalPointerTableEntry& src) {
auto payload = src.payload_.load(std::memory_order_relaxed);
DCHECK(payload.ContainsPointer());
payload_.store(payload, std::memory_order_relaxed);
#if defined(LEAK_SANITIZER)
raw_pointer_for_lsan_ = src.raw_pointer_for_lsan_;
#endif
}
Address ExternalPointerTable::Get(ExternalPointerHandle handle,
ExternalPointerTagRange tag_range) const {
uint32_t index = HandleToIndex(handle);
DCHECK(index == 0 || at(index).HasExternalPointer(tag_range));
return at(index).GetExternalPointer(tag_range);
}
void ExternalPointerTable::Set(ExternalPointerHandle handle, Address value,
ExternalPointerTag tag) {
DCHECK_NE(kNullExternalPointerHandle, handle);
uint32_t index = HandleToIndex(handle);
FreeManagedResourceIfPresent(index);
TakeOwnershipOfManagedResourceIfNecessary(value, handle, tag);
at(index).SetExternalPointer(value, tag);
}
Address ExternalPointerTable::Exchange(ExternalPointerHandle handle,
Address value, ExternalPointerTag tag) {
DCHECK_NE(kNullExternalPointerHandle, handle);
DCHECK(!IsManagedExternalPointerType(tag));
uint32_t index = HandleToIndex(handle);
return at(index).ExchangeExternalPointer(value, tag);
}
ExternalPointerTag ExternalPointerTable::GetTag(
ExternalPointerHandle handle) const {
uint32_t index = HandleToIndex(handle);
return at(index).GetExternalPointerTag();
}
void ExternalPointerTable::Zap(ExternalPointerHandle handle) {
if (handle == kNullExternalPointerHandle) return;
uint32_t index = HandleToIndex(handle);
at(index).MakeZappedEntry();
}
ExternalPointerHandle ExternalPointerTable::AllocateAndInitializeEntry(
Space* space, Address initial_value, ExternalPointerTag tag) {
DCHECK(space->BelongsTo(this));
uint32_t index = AllocateEntry(space);
at(index).MakeExternalPointerEntry(initial_value, tag,
space->allocate_black());
ExternalPointerHandle handle = IndexToHandle(index);
TakeOwnershipOfManagedResourceIfNecessary(initial_value, handle, tag);
return handle;
}
ExternalPointerHandle ExternalPointerTable::DuplicateEntry(
Space* space, ExternalPointerHandle handle) {
DCHECK_NE(handle, kNullExternalPointerHandle);
uint32_t old_index = HandleToIndex(handle);
uint32_t new_index = AllocateEntry(space);
if (new_index == 0) return kNullExternalPointerHandle;
CHECK(!IsManagedExternalPointerType(at(old_index).GetExternalPointerTag()));
at(new_index).CopyFrom(at(old_index));
return IndexToHandle(new_index);
}
void ExternalPointerTable::Mark(Space* space, ExternalPointerHandle handle,
Address handle_location) {
DCHECK(space->BelongsTo(this));
#ifdef DEBUG
ExternalPointerHandle current_handle = base::AsAtomic32::Acquire_Load(
reinterpret_cast<ExternalPointerHandle*>(handle_location));
DCHECK(handle == kNullExternalPointerHandle || handle == current_handle);
#endif
if (handle == kNullExternalPointerHandle) return;
uint32_t index = HandleToIndex(handle);
DCHECK(space->Contains(index));
MaybeCreateEvacuationEntry(space, index, handle_location);
at(index).Mark();
}
void ExternalPointerTable::Evacuate(Space* from_space, Space* to_space,
ExternalPointerHandle handle,
Address handle_location,
EvacuateMarkMode mode) {
DCHECK(from_space->BelongsTo(this));
DCHECK(to_space->BelongsTo(this));
CHECK(IsValidHandle(handle));
auto handle_ptr = reinterpret_cast<ExternalPointerHandle*>(handle_location);
#ifdef DEBUG
DCHECK_EQ(handle, *handle_ptr);
#endif
if (handle == kNullExternalPointerHandle) return;
uint32_t from_index = HandleToIndex(handle);
DCHECK(from_space->Contains(from_index));
uint32_t to_index = AllocateEntry(to_space);
at(from_index).Evacuate(at(to_index), mode);
ExternalPointerHandle new_handle = IndexToHandle(to_index);
if (Address addr = at(to_index).ExtractManagedResourceOrNull()) {
ManagedResource* resource = reinterpret_cast<ManagedResource*>(addr);
DCHECK_EQ(resource->ept_entry_, handle);
resource->ept_entry_ = new_handle;
}
base::AsAtomic32::Relaxed_Store(handle_ptr, new_handle);
}
bool ExternalPointerTable::IsValidHandle(ExternalPointerHandle handle) {
uint32_t index = handle >> kExternalPointerIndexShift;
return handle == index << kExternalPointerIndexShift;
}
uint32_t ExternalPointerTable::HandleToIndex(ExternalPointerHandle handle) {
DCHECK(IsValidHandle(handle));
uint32_t index = handle >> kExternalPointerIndexShift;
#if defined(LEAK_SANITIZER)
index /= 2;
#endif
DCHECK_LE(index, kMaxExternalPointers);
return index;
}
ExternalPointerHandle ExternalPointerTable::IndexToHandle(uint32_t index) {
DCHECK_LE(index, kMaxExternalPointers);
ExternalPointerHandle handle = index << kExternalPointerIndexShift;
#if defined(LEAK_SANITIZER)
handle *= 2;
#endif
DCHECK_NE(handle, kNullExternalPointerHandle);
return handle;
}
bool ExternalPointerTable::Contains(Space* space,
ExternalPointerHandle handle) const {
DCHECK(space->BelongsTo(this));
return space->Contains(HandleToIndex(handle));
}
void ExternalPointerTable::Space::NotifyExternalPointerFieldInvalidated(
Address field_address, ExternalPointerTagRange tag_range) {
DCHECK(!IsManagedExternalPointerType(tag_range));
#ifdef DEBUG
ExternalPointerHandle handle = base::AsAtomic32::Acquire_Load(
reinterpret_cast<ExternalPointerHandle*>(field_address));
DCHECK(Contains(HandleToIndex(handle)));
#endif
AddInvalidatedField(field_address);
}
void ExternalPointerTable::ManagedResource::ZapExternalPointerTableEntry() {
if (owning_table_) {
owning_table_->Zap(ept_entry_);
}
ept_entry_ = kNullExternalPointerHandle;
}
void ExternalPointerTable::TakeOwnershipOfManagedResourceIfNecessary(
Address value, ExternalPointerHandle handle, ExternalPointerTag tag) {
if (IsManagedExternalPointerType(tag) && value != kNullAddress) {
ManagedResource* resource = reinterpret_cast<ManagedResource*>(value);
DCHECK_EQ(resource->ept_entry_, kNullExternalPointerHandle);
resource->owning_table_ = this;
resource->ept_entry_ = handle;
}
}
void ExternalPointerTable::FreeManagedResourceIfPresent(uint32_t entry_index) {
if (Address addr = at(entry_index).ExtractManagedResourceOrNull()) {
ManagedResource* resource = reinterpret_cast<ManagedResource*>(addr);
DCHECK_IMPLIES(!v8_flags.stress_snapshot,
resource->ept_entry_ == IndexToHandle(entry_index));
resource->ept_entry_ = kNullExternalPointerHandle;
}
}
}
}
#endif
#endif