Commit 2679a36e authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[sandbox] Make external ptr table usable under pointer compression

This CL moves the external pointer table out of V8_ENABLE_SANDBOX and
into V8_COMPRESS_POINTERS. The external pointer table is also useful
even when not sandboxing external pointers to ease alignment
requirements under pointer compression.

It is onerous for the allocator to support non-tagged-size alignment.
Under pointer compression, tagged is 4 bytes while system pointers are
8 bytes. Because external pointer table indices are 4-bytes, fields that
require natural alignment (e.g. the state field in JSAtomicsMutex) when
the system pointer size is 8-bytes can use an indirection via the
pointer table to ease the alignment restriction back to 4-bytes under
pointer compression.

Bug: v8:10391
Change-Id: Iac1200e40c987128cd6a227cd279ba4dac0e5c56
Cq-Include-Trybots: luci.v8.try:v8_linux64_pointer_compression_rel_ng
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3783076Reviewed-by: 's avatarSamuel Groß <saelo@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81977}
parent 55a497d9
......@@ -228,6 +228,10 @@ static_assert(kSandboxMinimumReservationSize > kPtrComprCageReservationSize,
"The minimum reservation size for a sandbox must be larger than "
"the pointer compression cage contained within it.");
#endif // V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
// The size of the virtual memory reservation for an external pointer table.
// This determines the maximum number of entries in a table. Using a maximum
// size allows omitting bounds checks on table accesses if the indices are
......@@ -236,19 +240,23 @@ static_assert(kSandboxMinimumReservationSize > kPtrComprCageReservationSize,
static const size_t kExternalPointerTableReservationSize = 128 * MB;
// The maximum number of entries in an external pointer table.
static const size_t kMaxSandboxedExternalPointers =
static const size_t kMaxExternalPointers =
kExternalPointerTableReservationSize / kApiSystemPointerSize;
// The external pointer table indices stored in HeapObjects as external
// pointers are shifted to the left by this amount to guarantee that they are
// smaller than the maximum table size.
static const uint32_t kExternalPointerIndexShift = 8;
static_assert((1 << (32 - kExternalPointerIndexShift)) ==
kMaxSandboxedExternalPointers,
static_assert((1 << (32 - kExternalPointerIndexShift)) == kMaxExternalPointers,
"kExternalPointerTableReservationSize and "
"kExternalPointerIndexShift don't match");
#endif // V8_ENABLE_SANDBOX
#else // !V8_COMPRESS_POINTERS
// Needed for the V8.SandboxedExternalPointersCount histogram.
static const size_t kMaxExternalPointers = 0;
#endif // V8_COMPRESS_POINTERS
// A ExternalPointerHandle represents a (opaque) reference to an external
// pointer that can be stored inside the sandbox. A ExternalPointerHandle has
......@@ -361,11 +369,11 @@ constexpr uint64_t kAllExternalPointerTypeTags[] = {
// be accessed from multiple threads at the same time. The objects referenced
// in this way must therefore always be thread-safe.
#define SHARED_EXTERNAL_POINTER_TAGS(V) \
V(kFirstSharedTag, unsandboxed, TAG(0)) \
V(kWaiterQueueNodeTag, unsandboxed, TAG(0)) \
V(kFirstSharedTag, sandboxed, TAG(0)) \
V(kWaiterQueueNodeTag, sandboxed, TAG(0)) \
V(kExternalStringResourceTag, unsandboxed, TAG(1)) \
V(kExternalStringResourceDataTag, unsandboxed, TAG(2)) \
V(kLastSharedTag, unsandboxed, TAG(2))
V(kLastSharedTag, sandboxed, TAG(2))
// External pointers using these tags are kept in a per-Isolate external
// pointer table and can only be accessed when this Isolate is active.
......@@ -392,11 +400,16 @@ constexpr uint64_t kAllExternalPointerTypeTags[] = {
// rollout of external pointer sandboxing. If V8_SANDBOXED_EXTERNAL_POINTERS is
// defined, all external pointers are sandboxed. If the sandbox is off, no
// external pointers are sandboxed.
//
// Sandboxed external pointer tags are available when compressing pointers even
// when the sandbox is off. Some tags (e.g. kWaiterQueueNodeTag) are used
// manually with the external pointer table even when the sandbox is off to ease
// alignment requirements.
#define sandboxed(X) (X << kExternalPointerTagShift) | kExternalPointerMarkBit
#define unsandboxed(X) kUnsandboxedExternalPointerTag
#if defined(V8_SANDBOXED_EXTERNAL_POINTERS)
#define EXTERNAL_POINTER_TAG_ENUM(Name, State, Bits) Name = sandboxed(Bits),
#elif defined(V8_ENABLE_SANDBOX)
#elif defined(V8_COMPRESS_POINTERS)
#define EXTERNAL_POINTER_TAG_ENUM(Name, State, Bits) Name = State(Bits),
#else
#define EXTERNAL_POINTER_TAG_ENUM(Name, State, Bits) Name = unsandboxed(Bits),
......@@ -540,7 +553,7 @@ class Internals {
kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize;
static const int kIsolateLongTaskStatsCounterOffset =
kIsolateFastApiCallTargetOffset + kApiSystemPointerSize;
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
static const int kIsolateExternalPointerTableOffset =
kIsolateLongTaskStatsCounterOffset + kApiSizetSize;
static const int kIsolateSharedExternalPointerTableAddressOffset =
......
......@@ -39,7 +39,7 @@ class Isolate;
V(kFastCCallCallerPCOffset, kSystemPointerSize, fast_c_call_caller_pc) \
V(kFastApiCallTargetOffset, kSystemPointerSize, fast_api_call_target) \
V(kLongTaskStatsCounterOffset, kSizetSize, long_task_stats_counter) \
ISOLATE_DATA_FIELDS_SANDBOX(V) \
ISOLATE_DATA_FIELDS_POINTER_COMPRESSION(V) \
/* Full tables (arbitrary size, potentially slower access). */ \
V(kRootsTableOffset, RootsTable::kEntriesCount* kSystemPointerSize, \
roots_table) \
......@@ -55,15 +55,15 @@ class Isolate;
V(kOldAllocationInfo, LinearAllocationArea::kSize, old_allocation_info) \
V(kStackIsIterableOffset, kUInt8Size, stack_is_iterable)
#ifdef V8_ENABLE_SANDBOX
#define ISOLATE_DATA_FIELDS_SANDBOX(V) \
#ifdef V8_COMPRESS_POINTERS
#define ISOLATE_DATA_FIELDS_POINTER_COMPRESSION(V) \
V(kExternalPointerTableOffset, ExternalPointerTable::kSize, \
external_pointer_table) \
V(kSharedExternalPointerTableOffset, kSystemPointerSize, \
shared_external_pointer_table)
#else
#define ISOLATE_DATA_FIELDS_SANDBOX(V)
#endif // V8_ENABLE_SANDBOX
#define ISOLATE_DATA_FIELDS_POINTER_COMPRESSION(V)
#endif // V8_COMPRESS_POINTERS
// This class contains a collection of data accessible from both C++ runtime
// and compiled code (including builtins, interpreter bytecode handlers and
......@@ -196,7 +196,7 @@ class IsolateData final {
size_t long_task_stats_counter_ = 0;
// Table containing pointers to external objects.
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
ExternalPointerTable external_pointer_table_;
ExternalPointerTable* shared_external_pointer_table_;
#endif
......@@ -254,7 +254,7 @@ void IsolateData::AssertPredictableLayout() {
static_assert(sizeof(IsolateData) == IsolateData::kSize);
}
#undef ISOLATE_DATA_FIELDS_SANDBOX
#undef ISOLATE_DATA_FIELDS_POINTER_COMPRESSION
#undef ISOLATE_DATA_FIELDS
} // namespace internal
......
......@@ -3386,7 +3386,7 @@ void Isolate::CheckIsolateLayout() {
Internals::kIsolateLongTaskStatsCounterOffset);
CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, isolate_data_.stack_guard_)),
Internals::kIsolateStackGuardOffset);
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
CHECK_EQ(static_cast<int>(
OFFSET_OF(Isolate, isolate_data_.external_pointer_table_)),
Internals::kIsolateExternalPointerTableOffset);
......@@ -3573,14 +3573,14 @@ void Isolate::Deinit() {
ClearSerializerData();
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
external_pointer_table().TearDown();
if (owns_shareable_data()) {
shared_external_pointer_table().TearDown();
delete isolate_data_.shared_external_pointer_table_;
isolate_data_.shared_external_pointer_table_ = nullptr;
}
#endif // V8_ENABLE_SANDBOX
#endif // V8_COMPRESS_POINTERS
{
base::MutexGuard lock_guard(&thread_data_table_mutex_);
......@@ -4134,7 +4134,7 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
isolate_data_.external_reference_table()->Init(this);
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
external_pointer_table().Init(this);
if (owns_shareable_data()) {
isolate_data_.shared_external_pointer_table_ = new ExternalPointerTable();
......@@ -4144,7 +4144,7 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
isolate_data_.shared_external_pointer_table_ =
shared_isolate()->isolate_data_.shared_external_pointer_table_;
}
#endif // V8_ENABLE_SANDBOX
#endif // V8_COMPRESS_POINTERS
#if V8_ENABLE_WEBASSEMBLY
wasm::GetWasmEngine()->AddIsolate(this);
......@@ -5780,7 +5780,7 @@ void Isolate::DetachFromSharedIsolate() {
#endif // DEBUG
}
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
ExternalPointerHandle
Isolate::InsertWaiterQueueNodeIntoSharedExternalPointerTable(Address node) {
DCHECK_NE(kNullAddress, node);
......@@ -5795,7 +5795,7 @@ Isolate::InsertWaiterQueueNodeIntoSharedExternalPointerTable(Address node) {
shared_external_pointer_table().Set(handle, node, kWaiterQueueNodeTag);
return handle;
}
#endif // V8_ENABLE_SANDBOX
#endif // V8_COMPRESS_POINTERS
namespace {
class DefaultWasmAsyncResolvePromiseTask : public v8::Task {
......
......@@ -1950,7 +1950,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
LocalHeap* main_thread_local_heap();
LocalHeap* CurrentLocalHeap();
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
ExternalPointerTable& external_pointer_table() {
return isolate_data_.external_pointer_table_;
}
......@@ -1982,7 +1982,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
ExternalPointerHandle InsertWaiterQueueNodeIntoSharedExternalPointerTable(
Address node);
#endif
#endif // V8_COMPRESS_POINTERS
struct PromiseHookFields {
using HasContextPromiseHook = base::BitField<bool, 0, 1>;
......@@ -2451,7 +2451,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
// isolates or when no shared isolate is used.
Isolate* shared_isolate_ = nullptr;
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
// The external pointer handle to the Isolate's main thread's WaiterQueueNode.
// It is used to wait for JS-exposed mutex or condition variable.
Maybe<ExternalPointerHandle> waiter_queue_node_external_pointer_ =
......
......@@ -105,17 +105,10 @@ namespace internal {
V8.WasmStreamingUntilCompilationFinishedMilliSeconds, 0, 10000, 50) \
HR(wasm_compilation_until_streaming_finished, \
V8.WasmCompilationUntilStreamFinishedMilliSeconds, 0, 10000, 50) \
SANDBOXED_HISTOGRAM_LIST(HR)
#ifdef V8_ENABLE_SANDBOX
#define SANDBOXED_HISTOGRAM_LIST(HR) \
/* Number of in-use external pointers in the external pointer table */ \
/* Counted after sweeping the table at the end of mark-compact GC */ \
HR(sandboxed_external_pointers_count, V8.SandboxedExternalPointersCount, 0, \
kMaxSandboxedExternalPointers, 101)
#else
#define SANDBOXED_HISTOGRAM_LIST(HR)
#endif // V8_ENABLE_SANDBOX
HR(external_pointers_count, V8.SandboxedExternalPointersCount, 0, \
kMaxExternalPointers, 101)
#define NESTED_TIMED_HISTOGRAM_LIST(HT) \
/* Timer histograms, not thread safe: HT(name, caption, max, unit) */ \
......
......@@ -10,7 +10,7 @@
#include "src/sandbox/external-pointer.h"
#include "src/utils/allocation.h"
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
namespace v8 {
namespace internal {
......@@ -186,6 +186,6 @@ void ExternalPointerTable::Mark(ExternalPointerHandle handle) {
} // namespace internal
} // namespace v8
#endif // V8_ENABLE_SANDBOX
#endif // V8_COMPRESS_POINTERS
#endif // V8_SANDBOX_EXTERNAL_POINTER_TABLE_INL_H_
......@@ -10,7 +10,7 @@
#include "src/logging/counters.h"
#include "src/sandbox/external-pointer-table-inl.h"
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
namespace v8 {
namespace internal {
......@@ -51,8 +51,7 @@ uint32_t ExternalPointerTable::Sweep(Isolate* isolate) {
base::Release_Store(&freelist_head_, current_freelist_head);
uint32_t num_active_entries = capacity_ - freelist_size;
isolate->counters()->sandboxed_external_pointers_count()->AddSample(
num_active_entries);
isolate->counters()->external_pointers_count()->AddSample(num_active_entries);
return num_active_entries;
}
......@@ -65,7 +64,7 @@ uint32_t ExternalPointerTable::Grow() {
// Grow the table by one block.
uint32_t old_capacity = capacity_;
uint32_t new_capacity = old_capacity + kEntriesPerBlock;
CHECK_LE(new_capacity, kMaxSandboxedExternalPointers);
CHECK_LE(new_capacity, kMaxExternalPointers);
// Failure likely means OOM. TODO(saelo) handle this.
VirtualAddressSpace* root_space = GetPlatformVirtualAddressSpace();
......@@ -93,4 +92,4 @@ uint32_t ExternalPointerTable::Grow() {
} // namespace internal
} // namespace v8
#endif // V8_ENABLE_SANDBOX
#endif // V8_COMPRESS_POINTERS
......@@ -11,7 +11,7 @@
#include "src/base/platform/mutex.h"
#include "src/common/globals.h"
#ifdef V8_ENABLE_SANDBOX
#ifdef V8_COMPRESS_POINTERS
namespace v8 {
namespace internal {
......@@ -19,7 +19,10 @@ namespace internal {
class Isolate;
/**
* A table storing pointers to objects outside the sandbox.
* A table storing pointers to objects outside the V8 heap.
*
* When V8_ENABLE_SANDBOX, its primary use is for pointing to objects outside
* the sandbox, as described below.
*
* An external pointer table provides the basic mechanisms to ensure
* memory-safe access to objects located outside the sandbox, but referenced
......@@ -57,6 +60,9 @@ class Isolate;
* to store the index of the next free entry. When the freelist is empty and a
* new entry is allocated, the table grows in place and the freelist is
* re-populated from the newly added entries.
*
* When V8_COMPRESS_POINTERS, external pointer tables are also used to ease
* alignment requirements in heap object fields via indirection.
*/
class V8_EXPORT_PRIVATE ExternalPointerTable {
public:
......@@ -120,7 +126,12 @@ class V8_EXPORT_PRIVATE ExternalPointerTable {
// An external pointer table grows in blocks of this size. This is also the
// initial size of the table.
static const size_t kBlockSize = 64 * KB;
//
// The external pointer table is used both for sandboxing external pointers
// and for easing alignment requirements when compressing pointers. When just
// doing the latter, expect less usage.
static const size_t kBlockSize =
SandboxedExternalPointersAreEnabled() ? 64 * KB : 16 * KB;
static const size_t kEntriesPerBlock = kBlockSize / kSystemPointerSize;
// When the table is swept, it first sets the freelist head to this special
......@@ -234,6 +245,6 @@ class V8_EXPORT_PRIVATE ExternalPointerTable {
} // namespace internal
} // namespace v8
#endif // V8_ENABLE_SANDBOX
#endif // V8_COMPRESS_POINTERS
#endif // V8_SANDBOX_EXTERNAL_POINTER_TABLE_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment