[sandbox][x64] Access external pointer through a table

This change moves external pointers into a separate table and turns
external pointers in heap objects into indices into that table.

This CL implements one of two possible ownership models for the table
entries. With this one, every heap object owns its table entries, and
they are allocated when the owning object is allocated. As such, setting
external pointer fields does not require allocation of table entries. On
the other hand, table indices cannot be shared between multiple objects.

This CL does not yet implement freeing of external pointer table
entires. This will later happen by a table garbage collector.

Bug: v8:10391
Change-Id: I4d37785295c25a7d1dcbc9871dd5887b9d788a4f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2235700
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Samuel Groß <saelo@google.com>
Cr-Commit-Position: refs/heads/master@{#70204}
This commit is contained in:
Samuel Groß 2020-09-29 15:40:21 +02:00 committed by Commit Bot
parent 5082a1d924
commit 32e2584405
59 changed files with 884 additions and 220 deletions

View File

@ -2477,6 +2477,8 @@ v8_source_set("v8_base_without_compiler") {
"src/execution/arguments.h",
"src/execution/execution.cc",
"src/execution/execution.h",
"src/execution/external-pointer-table.cc",
"src/execution/external-pointer-table.h",
"src/execution/frame-constants.h",
"src/execution/frames-inl.h",
"src/execution/frames.cc",

View File

@ -140,6 +140,10 @@ V8_INLINE static constexpr internal::Address IntToSmi(int value) {
kSmiTag;
}
// Converts encoded external pointer to address.
V8_EXPORT Address DecodeExternalPointerImpl(const Isolate* isolate,
ExternalPointer_t pointer);
// {obj} must be the raw tagged pointer representation of a HeapObject
// that's guaranteed to never be in ReadOnlySpace.
V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj);
@ -168,6 +172,9 @@ class Internals {
static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize;
static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize;
static const int kEmbedderDataSlotSize = kApiSystemPointerSize;
#ifdef V8_HEAP_SANDBOX
static const int kEmbedderDataSlotRawPayloadOffset = kApiTaggedSize;
#endif
static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize;
static const int kFullStringRepresentationMask = 0x0f;
static const int kStringEncodingMask = 0x8;
@ -187,6 +194,12 @@ class Internals {
static const int kIsolateRootsOffset =
kIsolateStackGuardOffset + 7 * kApiSystemPointerSize;
static const int kExternalPointerTableBufferOffset = 0;
static const int kExternalPointerTableLengthOffset =
kExternalPointerTableBufferOffset + kApiSystemPointerSize;
static const int kExternalPointerTableCapacityOffset =
kExternalPointerTableLengthOffset + kApiInt32Size;
static const int kUndefinedValueRootIndex = 4;
static const int kTheHoleValueRootIndex = 5;
static const int kNullValueRootIndex = 6;
@ -352,15 +365,26 @@ class Internals {
#endif
}
V8_INLINE static Address DecodeExternalPointer(
const Isolate* isolate, ExternalPointer_t encoded_pointer) {
#ifdef V8_HEAP_SANDBOX
return internal::DecodeExternalPointerImpl(isolate, encoded_pointer);
#else
return encoded_pointer;
#endif
}
V8_INLINE static internal::Address ReadExternalPointerField(
internal::Isolate* isolate, internal::Address heap_object_ptr,
int offset) {
internal::Address value = ReadRawField<Address>(heap_object_ptr, offset);
#ifdef V8_HEAP_SANDBOX
internal::ExternalPointer_t encoded_value =
ReadRawField<uint32_t>(heap_object_ptr, offset);
// We currently have to treat zero as nullptr in embedder slots.
if (value) value = DecodeExternalPointer(isolate, value);
return encoded_value ? DecodeExternalPointer(isolate, encoded_value) : 0;
#else
return ReadRawField<Address>(heap_object_ptr, offset);
#endif
return value;
}
#ifdef V8_COMPRESS_POINTERS
@ -368,10 +392,6 @@ class Internals {
static constexpr size_t kPtrComprHeapReservationSize = size_t{1} << 32;
static constexpr size_t kPtrComprIsolateRootAlignment = size_t{1} << 32;
// See v8:10391 for details about V8 heap sandbox.
static constexpr uint32_t kExternalPointerSalt =
0x7fffffff & ~static_cast<uint32_t>(kHeapObjectTagMask);
V8_INLINE static internal::Address GetRootFromOnHeapAddress(
internal::Address addr) {
return addr & -static_cast<intptr_t>(kPtrComprIsolateRootAlignment);
@ -383,14 +403,6 @@ class Internals {
return root + static_cast<internal::Address>(static_cast<uintptr_t>(value));
}
V8_INLINE static Address DecodeExternalPointer(
const Isolate* isolate, ExternalPointer_t encoded_pointer) {
#ifndef V8_HEAP_SANDBOX
return encoded_pointer;
#else
return encoded_pointer ^ kExternalPointerSalt;
#endif
}
#endif // V8_COMPRESS_POINTERS
};

View File

@ -11442,6 +11442,9 @@ void* Object::GetAlignedPointerFromInternalField(int index) {
instance_type == I::kJSApiObjectType ||
instance_type == I::kJSSpecialApiObjectType)) {
int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index);
#ifdef V8_HEAP_SANDBOX
offset += I::kEmbedderDataSlotRawPayloadOffset;
#endif
internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj);
A value = I::ReadExternalPointerField(isolate, obj, offset);
return reinterpret_cast<void*>(value);
@ -12062,6 +12065,9 @@ void* Context::GetAlignedPointerFromEmbedderData(int index) {
I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset);
int value_offset =
I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index);
#ifdef V8_HEAP_SANDBOX
value_offset += I::kEmbedderDataSlotRawPayloadOffset;
#endif
internal::Isolate* isolate = I::GetIsolateForHeapSandbox(ctx);
return reinterpret_cast<void*>(
I::ReadExternalPointerField(isolate, embedder_data, value_offset));

View File

@ -3663,6 +3663,11 @@ MaybeLocal<Uint32> Value::ToUint32(Local<Context> context) const {
RETURN_ESCAPED(result);
}
i::Address i::DecodeExternalPointerImpl(const Isolate* isolate,
i::ExternalPointer_t encoded_pointer) {
return i::DecodeExternalPointer(isolate, encoded_pointer);
}
i::Isolate* i::IsolateFromNeverReadOnlySpaceObject(i::Address obj) {
return i::GetIsolateFromWritableObject(i::HeapObject::cast(i::Object(obj)));
}

View File

@ -647,8 +647,7 @@ void CallOrConstructBuiltinsAssembler::CallFunctionTemplate(
function_template_info, FunctionTemplateInfo::kCallCodeOffset);
TNode<Foreign> foreign = LoadObjectField<Foreign>(
call_handler_info, CallHandlerInfo::kJsCallbackOffset);
TNode<RawPtrT> callback =
DecodeExternalPointer(LoadForeignForeignAddress(foreign));
TNode<RawPtrT> callback = LoadForeignForeignAddressPtr(foreign);
TNode<Object> call_data =
LoadObjectField<Object>(call_handler_info, CallHandlerInfo::kDataOffset);
TailCallStub(CodeFactory::CallApiCallback(isolate()), context, callback, argc,

View File

@ -90,6 +90,7 @@ BUILTIN(DataViewConstructor) {
isolate, result,
JSObject::New(target, new_target, Handle<AllocationSite>::null()));
for (int i = 0; i < ArrayBufferView::kEmbedderFieldCount; ++i) {
// TODO(v8:10391, saelo): Handle external pointers in EmbedderDataSlot
Handle<JSDataView>::cast(result)->SetEmbedderField(i, Smi::zero());
}
@ -101,6 +102,7 @@ BUILTIN(DataViewConstructor) {
// 13. Set O's [[ByteOffset]] internal slot to offset.
Handle<JSDataView>::cast(result)->set_byte_offset(view_byte_offset);
Handle<JSDataView>::cast(result)->AllocateExternalPointerEntries(isolate);
Handle<JSDataView>::cast(result)->set_data_pointer(
isolate,
static_cast<uint8_t*>(array_buffer->backing_store()) + view_byte_offset);

View File

@ -53,8 +53,8 @@ class MicrotaskQueueBuiltinsAssembler : public CodeStubAssembler {
TNode<RawPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueue(
TNode<Context> native_context) {
CSA_ASSERT(this, IsNativeContext(native_context));
return DecodeExternalPointer(LoadObjectField<ExternalPointerT>(
native_context, NativeContext::kMicrotaskQueueOffset));
return LoadExternalPointerFromObject(native_context,
NativeContext::kMicrotaskQueueOffset);
}
TNode<RawPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskRingBuffer(

View File

@ -46,8 +46,7 @@ TNode<RawPtrT> StringBuiltinsAssembler::DirectStringData(
Word32And(string_instance_type,
Int32Constant(kUncachedExternalStringMask)),
Int32Constant(kUncachedExternalStringTag)));
var_data =
DecodeExternalPointer(LoadExternalStringResourceData(CAST(string)));
var_data = LoadExternalStringResourceDataPtr(CAST(string));
Goto(&if_join);
}

View File

@ -25,6 +25,7 @@ void TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields(
TNode<JSTypedArray> holder) {
for (int offset = JSTypedArray::kHeaderSize;
offset < JSTypedArray::kSizeWithEmbedderFields; offset += kTaggedSize) {
// TODO(v8:10391, saelo): Handle external pointers in EmbedderDataSlot
StoreObjectField(holder, offset, SmiConstant(0));
}
}
@ -65,13 +66,13 @@ TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
byte_length);
StoreJSArrayBufferBackingStore(
buffer,
EncodeExternalPointer(ReinterpretCast<RawPtrT>(IntPtrConstant(0))));
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset,
IntPtrConstant(0));
InitializeExternalPointerField(buffer, JSArrayBuffer::kBackingStoreOffset,
PointerConstant(nullptr));
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset,
IntPtrConstant(0));
for (int offset = JSArrayBuffer::kHeaderSize;
offset < JSArrayBuffer::kSizeWithEmbedderFields; offset += kTaggedSize) {
// TODO(v8:10391, saelo): Handle external pointers in EmbedderDataSlot
StoreObjectFieldNoWriteBarrier(buffer, offset, SmiConstant(0));
}
return buffer;
@ -355,6 +356,12 @@ void TypedArrayBuiltinsAssembler::DispatchTypedArrayByElementsKind(
BIND(&next);
}
void TypedArrayBuiltinsAssembler::AllocateJSTypedArrayExternalPointerEntry(
TNode<JSTypedArray> holder) {
InitializeExternalPointerField(
holder, IntPtrConstant(JSTypedArray::kExternalPointerOffset));
}
void TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
TNode<JSTypedArray> holder, TNode<ByteArray> base, TNode<UintPtrT> offset) {
offset = UintPtrAdd(UintPtrConstant(ByteArray::kHeaderSize - kHeapObjectTag),
@ -373,9 +380,8 @@ void TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
offset = Unsigned(IntPtrAdd(offset, isolate_root));
}
StoreObjectField(holder, JSTypedArray::kBasePointerOffset, base);
StoreJSTypedArrayExternalPointer(
holder, EncodeExternalPointer(ReinterpretCast<RawPtrT>(offset)));
StoreJSTypedArrayBasePointer(holder, base);
StoreJSTypedArrayExternalPointerPtr(holder, ReinterpretCast<RawPtrT>(offset));
}
void TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(
@ -384,7 +390,7 @@ void TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(
SmiConstant(0));
base = RawPtrAdd(base, Signed(offset));
StoreJSTypedArrayExternalPointer(holder, EncodeExternalPointer(base));
StoreJSTypedArrayExternalPointerPtr(holder, base);
}
void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric(

View File

@ -77,6 +77,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
void DispatchTypedArrayByElementsKind(
TNode<Word32T> elements_kind, const TypedArraySwitchCase& case_function);
void AllocateJSTypedArrayExternalPointerEntry(TNode<JSTypedArray> holder);
void SetJSTypedArrayOnHeapDataPtr(TNode<JSTypedArray> holder,
TNode<ByteArray> base,
TNode<UintPtrT> offset);

View File

@ -53,6 +53,7 @@ transitioning macro AllocateTypedArray(implicit context: Context)(
typedArray.byte_offset = byteOffset;
typedArray.byte_length = byteLength;
typedArray.length = length;
typed_array::AllocateJSTypedArrayExternalPointerEntry(typedArray);
if constexpr (isOnHeap) {
typed_array::SetJSTypedArrayOnHeapDataPtr(typedArray, elements, byteOffset);
} else {

View File

@ -157,6 +157,10 @@ macro GetTypedArrayAccessor(elementsKind: ElementsKind): TypedArrayAccessor {
unreachable;
}
extern macro
TypedArrayBuiltinsAssembler::AllocateJSTypedArrayExternalPointerEntry(
JSTypedArray): void;
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
JSTypedArray, ByteArray, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(

View File

@ -3371,9 +3371,9 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
MemOperand(function_data,
WasmExportedFunctionData::kSignatureOffset - kHeapObjectTag));
Register signature = foreign_signature;
__ movq(signature,
MemOperand(foreign_signature, wasm::ObjectAccess::ToTagged(
Foreign::kForeignAddressOffset)));
__ LoadExternalPointerField(
signature,
FieldOperand(foreign_signature, Foreign::kForeignAddressOffset));
foreign_signature = no_reg;
Register return_count = r8;
__ movq(return_count,

View File

@ -1370,6 +1370,105 @@ void CodeStubAssembler::BranchIfToBooleanIsTrue(SloppyTNode<Object> value,
}
}
TNode<ExternalPointerT> CodeStubAssembler::ChangeUint32ToExternalPointer(
TNode<Uint32T> value) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
return ReinterpretCast<ExternalPointerT>(ChangeUint32ToWord(value));
}
TNode<Uint32T> CodeStubAssembler::ChangeExternalPointerToUint32(
TNode<ExternalPointerT> value) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
return Unsigned(TruncateWordToInt32(ReinterpretCast<UintPtrT>(value)));
}
void CodeStubAssembler::InitializeExternalPointerField(TNode<HeapObject> object,
TNode<IntPtrT> offset) {
#ifdef V8_HEAP_SANDBOX
TNode<ExternalReference> external_pointer_table_address = ExternalConstant(
ExternalReference::external_pointer_table_address(isolate()));
TNode<Uint32T> table_length = UncheckedCast<Uint32T>(
Load(MachineType::Uint32(), external_pointer_table_address,
UintPtrConstant(Internals::kExternalPointerTableLengthOffset)));
TNode<Uint32T> table_capacity = UncheckedCast<Uint32T>(
Load(MachineType::Uint32(), external_pointer_table_address,
UintPtrConstant(Internals::kExternalPointerTableCapacityOffset)));
Label grow_table(this, Label::kDeferred), finish(this);
TNode<BoolT> compare = Uint32LessThan(table_length, table_capacity);
Branch(compare, &finish, &grow_table);
BIND(&grow_table);
{
TNode<ExternalReference> table_grow_function = ExternalConstant(
ExternalReference::external_pointer_table_grow_table_function());
CallCFunction(
table_grow_function, MachineType::Pointer(),
std::make_pair(MachineType::Pointer(), external_pointer_table_address));
Goto(&finish);
}
BIND(&finish);
TNode<Uint32T> new_table_length = Uint32Add(table_length, Uint32Constant(1));
StoreNoWriteBarrier(
MachineRepresentation::kWord32, external_pointer_table_address,
UintPtrConstant(Internals::kExternalPointerTableLengthOffset),
new_table_length);
TNode<Uint32T> index = table_length;
TNode<ExternalPointerT> encoded = ChangeUint32ToExternalPointer(index);
StoreObjectFieldNoWriteBarrier<ExternalPointerT>(object, offset, encoded);
#endif
}
TNode<RawPtrT> CodeStubAssembler::LoadExternalPointerFromObject(
TNode<HeapObject> object, TNode<IntPtrT> offset) {
#ifdef V8_HEAP_SANDBOX
TNode<ExternalReference> external_pointer_table_address = ExternalConstant(
ExternalReference::external_pointer_table_address(isolate()));
TNode<RawPtrT> table = UncheckedCast<RawPtrT>(
Load(MachineType::Pointer(), external_pointer_table_address,
UintPtrConstant(Internals::kExternalPointerTableBufferOffset)));
TNode<ExternalPointerT> encoded =
LoadObjectField<ExternalPointerT>(object, offset);
TNode<Word32T> index = ChangeExternalPointerToUint32(encoded);
// TODO(v8:10391, saelo): bounds check if table is not caged
TNode<IntPtrT> table_offset = ElementOffsetFromIndex(
ChangeUint32ToWord(index), SYSTEM_POINTER_ELEMENTS, 0);
return UncheckedCast<RawPtrT>(
Load(MachineType::Pointer(), table, table_offset));
#else
return LoadObjectField<RawPtrT>(object, offset);
#endif // V8_HEAP_SANDBOX
}
void CodeStubAssembler::StoreExternalPointerToObject(TNode<HeapObject> object,
TNode<IntPtrT> offset,
TNode<RawPtrT> pointer) {
#ifdef V8_HEAP_SANDBOX
TNode<ExternalReference> external_pointer_table_address = ExternalConstant(
ExternalReference::external_pointer_table_address(isolate()));
TNode<RawPtrT> table = UncheckedCast<RawPtrT>(
Load(MachineType::Pointer(), external_pointer_table_address,
UintPtrConstant(Internals::kExternalPointerTableBufferOffset)));
TNode<ExternalPointerT> encoded =
LoadObjectField<ExternalPointerT>(object, offset);
TNode<Word32T> index = ChangeExternalPointerToUint32(encoded);
// TODO(v8:10391, saelo): bounds check if table is not caged
TNode<IntPtrT> table_offset = ElementOffsetFromIndex(
ChangeUint32ToWord(index), SYSTEM_POINTER_ELEMENTS, 0);
StoreNoWriteBarrier(MachineType::PointerRepresentation(), table, table_offset,
pointer);
#else
StoreObjectFieldNoWriteBarrier<RawPtrT>(object, offset, pointer);
#endif // V8_HEAP_SANDBOX
}
TNode<Object> CodeStubAssembler::LoadFromParentFrame(int offset) {
TNode<RawPtrT> frame_pointer = LoadParentFramePointer();
return LoadFullTagged(frame_pointer, IntPtrConstant(offset));
@ -2004,10 +2103,9 @@ TNode<IntPtrT> CodeStubAssembler::LoadPropertyArrayLength(
TNode<RawPtrT> CodeStubAssembler::LoadJSTypedArrayDataPtr(
TNode<JSTypedArray> typed_array) {
// Data pointer = DecodeExternalPointer(external_pointer) +
// static_cast<Tagged_t>(base_pointer).
// Data pointer = external_pointer + static_cast<Tagged_t>(base_pointer).
TNode<RawPtrT> external_pointer =
DecodeExternalPointer(LoadJSTypedArrayExternalPointer(typed_array));
LoadJSTypedArrayExternalPointerPtr(typed_array);
TNode<IntPtrT> base_pointer;
if (COMPRESS_POINTERS_BOOL) {
@ -6589,8 +6687,7 @@ TNode<RawPtrT> ToDirectStringAssembler::TryToSequential(
if_bailout);
TNode<String> string = var_string_.value();
TNode<RawPtrT> result =
DecodeExternalPointer(LoadExternalStringResourceData(CAST(string)));
TNode<RawPtrT> result = LoadExternalStringResourceDataPtr(CAST(string));
if (ptr_kind == PTR_TO_STRING) {
result = RawPtrSub(result, IntPtrConstant(SeqOneByteString::kHeaderSize -
kHeapObjectTag));
@ -12568,7 +12665,8 @@ void CodeStubAssembler::ThrowIfArrayBufferViewBufferIsDetached(
TNode<RawPtrT> CodeStubAssembler::LoadJSArrayBufferBackingStorePtr(
TNode<JSArrayBuffer> array_buffer) {
return DecodeExternalPointer(LoadJSArrayBufferBackingStore(array_buffer));
return LoadExternalPointerFromObject(array_buffer,
JSArrayBuffer::kBackingStoreOffset);
}
TNode<JSArrayBuffer> CodeStubAssembler::LoadJSArrayBufferViewBuffer(

View File

@ -968,29 +968,78 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Works only with V8_ENABLE_FORCE_SLOW_PATH compile time flag. Nop otherwise.
void GotoIfForceSlowPath(Label* if_true);
// Convert external pointer from on-V8-heap representation to an actual
// external pointer value.
TNode<RawPtrT> DecodeExternalPointer(
TNode<ExternalPointerT> encoded_pointer) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
TNode<RawPtrT> value = ReinterpretCast<RawPtrT>(encoded_pointer);
if (V8_HEAP_SANDBOX_BOOL) {
value = UncheckedCast<RawPtrT>(
WordXor(value, UintPtrConstant(kExternalPointerSalt)));
}
return value;
//
// ExternalPointerT-related functionality.
//
TNode<ExternalPointerT> ChangeUint32ToExternalPointer(TNode<Uint32T> value);
TNode<Uint32T> ChangeExternalPointerToUint32(TNode<ExternalPointerT> value);
// Initialize an external pointer field in an object.
void InitializeExternalPointerField(TNode<HeapObject> object, int offset) {
InitializeExternalPointerField(object, IntPtrConstant(offset));
}
void InitializeExternalPointerField(TNode<HeapObject> object,
TNode<IntPtrT> offset);
// Initialize an external pointer field in an object with given value.
void InitializeExternalPointerField(TNode<HeapObject> object, int offset,
TNode<RawPtrT> pointer) {
InitializeExternalPointerField(object, IntPtrConstant(offset), pointer);
}
// Convert external pointer value to on-V8-heap representation.
// This should eventually become a call to a non-allocating runtime function.
TNode<ExternalPointerT> EncodeExternalPointer(TNode<RawPtrT> pointer) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
TNode<RawPtrT> encoded_pointer = pointer;
if (V8_HEAP_SANDBOX_BOOL) {
encoded_pointer = UncheckedCast<RawPtrT>(
WordXor(encoded_pointer, UintPtrConstant(kExternalPointerSalt)));
}
return ReinterpretCast<ExternalPointerT>(encoded_pointer);
void InitializeExternalPointerField(TNode<HeapObject> object,
TNode<IntPtrT> offset,
TNode<RawPtrT> pointer) {
InitializeExternalPointerField(object, offset);
StoreExternalPointerToObject(object, offset, pointer);
}
// Load an external pointer value from an object.
TNode<RawPtrT> LoadExternalPointerFromObject(TNode<HeapObject> object,
int offset) {
return LoadExternalPointerFromObject(object, IntPtrConstant(offset));
}
TNode<RawPtrT> LoadExternalPointerFromObject(TNode<HeapObject> object,
TNode<IntPtrT> offset);
// Store external object pointer to object.
void StoreExternalPointerToObject(TNode<HeapObject> object, int offset,
TNode<RawPtrT> pointer) {
StoreExternalPointerToObject(object, IntPtrConstant(offset), pointer);
}
void StoreExternalPointerToObject(TNode<HeapObject> object,
TNode<IntPtrT> offset,
TNode<RawPtrT> pointer);
TNode<RawPtrT> LoadForeignForeignAddressPtr(TNode<Foreign> object) {
return LoadExternalPointerFromObject(object,
Foreign::kForeignAddressOffset);
}
TNode<RawPtrT> LoadExternalStringResourcePtr(TNode<ExternalString> object) {
return LoadExternalPointerFromObject(object,
ExternalString::kResourceOffset);
}
TNode<RawPtrT> LoadExternalStringResourceDataPtr(
TNode<ExternalString> object) {
return LoadExternalPointerFromObject(object,
ExternalString::kResourceDataOffset);
}
TNode<RawPtrT> LoadJSTypedArrayExternalPointerPtr(
TNode<JSTypedArray> holder) {
return LoadExternalPointerFromObject(holder,
JSTypedArray::kExternalPointerOffset);
}
void StoreJSTypedArrayExternalPointerPtr(TNode<JSTypedArray> holder,
TNode<RawPtrT> value) {
StoreExternalPointerToObject(holder, JSTypedArray::kExternalPointerOffset,
value);
}
// Load value from current parent frame by given offset in bytes.

View File

@ -121,6 +121,13 @@ ExternalReference ExternalReference::handle_scope_implementer_address(
return ExternalReference(isolate->handle_scope_implementer_address());
}
#ifdef V8_HEAP_SANDBOX
ExternalReference ExternalReference::external_pointer_table_address(
Isolate* isolate) {
return ExternalReference(isolate->external_pointer_table_address());
}
#endif
ExternalReference ExternalReference::interpreter_dispatch_table_address(
Isolate* isolate) {
return ExternalReference(isolate->interpreter()->dispatch_table_address());
@ -941,6 +948,11 @@ FUNCTION_REFERENCE(
js_finalization_registry_remove_cell_from_unregister_token_map,
JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap)
#ifdef V8_HEAP_SANDBOX
FUNCTION_REFERENCE(external_pointer_table_grow_table_function,
ExternalPointerTable::GrowTable)
#endif
bool operator==(ExternalReference lhs, ExternalReference rhs) {
return lhs.address() == rhs.address();
}

View File

@ -84,7 +84,17 @@ class StatsCounter;
V(re_check_stack_guard_state, \
"RegExpMacroAssembler*::CheckStackGuardState()") \
V(re_grow_stack, "NativeRegExpMacroAssembler::GrowStack()") \
V(re_word_character_map, "NativeRegExpMacroAssembler::word_character_map")
V(re_word_character_map, "NativeRegExpMacroAssembler::word_character_map") \
EXTERNAL_REFERENCE_LIST_WITH_ISOLATE_HEAP_SANDBOX(V)
#ifdef V8_HEAP_SANDBOX
#define EXTERNAL_REFERENCE_LIST_WITH_ISOLATE_HEAP_SANDBOX(V) \
V(external_pointer_table_address, \
"Isolate::external_pointer_table_address(" \
")")
#else
#define EXTERNAL_REFERENCE_LIST_WITH_ISOLATE_HEAP_SANDBOX(V)
#endif // V8_HEAP_SANDBOX
#define EXTERNAL_REFERENCE_LIST(V) \
V(abort_with_reason, "abort_with_reason") \
@ -233,7 +243,8 @@ class StatsCounter;
V(re_match_for_call_from_js, "IrregexpInterpreter::MatchForCallFromJs") \
V(re_experimental_match_for_call_from_js, \
"ExperimentalRegExp::MatchForCallFromJs") \
EXTERNAL_REFERENCE_LIST_INTL(V)
EXTERNAL_REFERENCE_LIST_INTL(V) \
EXTERNAL_REFERENCE_LIST_HEAP_SANDBOX(V)
#ifdef V8_INTL_SUPPORT
#define EXTERNAL_REFERENCE_LIST_INTL(V) \
@ -243,6 +254,14 @@ class StatsCounter;
#define EXTERNAL_REFERENCE_LIST_INTL(V)
#endif // V8_INTL_SUPPORT
#ifdef V8_HEAP_SANDBOX
#define EXTERNAL_REFERENCE_LIST_HEAP_SANDBOX(V) \
V(external_pointer_table_grow_table_function, \
"ExternalPointerTable::GrowTable")
#else
#define EXTERNAL_REFERENCE_LIST_HEAP_SANDBOX(V)
#endif // V8_HEAP_SANDBOX
// An ExternalReference represents a C++ address used in the generated
// code. All references to C++ functions and variables must be encapsulated
// in an ExternalReference instance. This is done in order to track the

View File

@ -344,10 +344,16 @@ void TurboAssembler::SaveRegisters(RegList registers) {
void TurboAssembler::LoadExternalPointerField(Register destination,
Operand field_operand) {
#ifdef V8_HEAP_SANDBOX
LoadAddress(kScratchRegister,
ExternalReference::external_pointer_table_address(isolate()));
movq(kScratchRegister,
Operand(kScratchRegister, Internals::kExternalPointerTableBufferOffset));
movl(destination, field_operand);
movq(destination, Operand(kScratchRegister, destination, times_8, 0));
#else
movq(destination, field_operand);
if (V8_HEAP_SANDBOX_BOOL) {
xorq(destination, Immediate(kExternalPointerSalt));
}
#endif // V8_HEAP_SANDBOX
}
void TurboAssembler::RestoreRegisters(RegList registers) {

View File

@ -12,18 +12,87 @@
namespace v8 {
namespace internal {
V8_INLINE ExternalPointer_t EncodeExternalPointer(Isolate* isolate,
Address external_pointer) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
if (!V8_HEAP_SANDBOX_BOOL) return external_pointer;
return external_pointer ^ kExternalPointerSalt;
}
V8_INLINE Address DecodeExternalPointer(const Isolate* isolate,
ExternalPointer_t encoded_pointer) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
if (!V8_HEAP_SANDBOX_BOOL) return encoded_pointer;
return encoded_pointer ^ kExternalPointerSalt;
#ifdef V8_HEAP_SANDBOX
uint32_t index = static_cast<uint32_t>(encoded_pointer);
return isolate->external_pointer_table().get(index);
#else
return encoded_pointer;
#endif
}
V8_INLINE void InitExternalPointerField(Address field_address,
Isolate* isolate) {
#ifdef V8_HEAP_SANDBOX
static_assert(kExternalPointerSize == kSystemPointerSize,
"Review the code below, once kExternalPointerSize is 4-byte "
"the address of the field will always be aligned");
ExternalPointer_t index = isolate->external_pointer_table().allocate();
base::WriteUnalignedValue<ExternalPointer_t>(field_address, index);
#else
// Nothing to do.
#endif // V8_HEAP_SANDBOX
}
V8_INLINE void InitExternalPointerField(Address field_address, Isolate* isolate,
Address value) {
#ifdef V8_HEAP_SANDBOX
ExternalPointer_t index = isolate->external_pointer_table().allocate();
isolate->external_pointer_table().set(static_cast<uint32_t>(index), value);
static_assert(kExternalPointerSize == kSystemPointerSize,
"Review the code below, once kExternalPointerSize is 4-byte "
"the address of the field will always be aligned");
base::WriteUnalignedValue<ExternalPointer_t>(field_address, index);
#else
// Pointer compression causes types larger than kTaggedSize to be unaligned.
constexpr bool v8_pointer_compression_unaligned =
kExternalPointerSize > kTaggedSize;
ExternalPointer_t encoded_value = static_cast<ExternalPointer_t>(value);
if (v8_pointer_compression_unaligned) {
base::WriteUnalignedValue<ExternalPointer_t>(field_address, encoded_value);
} else {
base::Memory<ExternalPointer_t>(field_address) = encoded_value;
}
#endif // V8_HEAP_SANDBOX
}
V8_INLINE Address ReadExternalPointerField(Address field_address,
const Isolate* isolate) {
// Pointer compression causes types larger than kTaggedSize to be unaligned.
constexpr bool v8_pointer_compression_unaligned =
kExternalPointerSize > kTaggedSize;
ExternalPointer_t encoded_value;
if (v8_pointer_compression_unaligned) {
encoded_value = base::ReadUnalignedValue<ExternalPointer_t>(field_address);
} else {
encoded_value = base::Memory<ExternalPointer_t>(field_address);
}
return DecodeExternalPointer(isolate, encoded_value);
}
V8_INLINE void WriteExternalPointerField(Address field_address,
Isolate* isolate, Address value) {
#ifdef V8_HEAP_SANDBOX
static_assert(kExternalPointerSize == kSystemPointerSize,
"Review the code below, once kExternalPointerSize is 4-byte "
"the address of the field will always be aligned");
ExternalPointer_t index =
base::ReadUnalignedValue<ExternalPointer_t>(field_address);
isolate->external_pointer_table().set(static_cast<uint32_t>(index), value);
#else
// Pointer compression causes types larger than kTaggedSize to be unaligned.
constexpr bool v8_pointer_compression_unaligned =
kExternalPointerSize > kTaggedSize;
ExternalPointer_t encoded_value = static_cast<ExternalPointer_t>(value);
if (v8_pointer_compression_unaligned) {
base::WriteUnalignedValue<ExternalPointer_t>(field_address, encoded_value);
} else {
base::Memory<ExternalPointer_t>(field_address) = encoded_value;
}
#endif // V8_HEAP_SANDBOX
}
} // namespace internal

View File

@ -10,23 +10,35 @@
namespace v8 {
namespace internal {
// See v8:10391 for details about V8 heap sandbox.
constexpr uint32_t kExternalPointerSalt =
0x7fffffff & ~static_cast<uint32_t>(kHeapObjectTagMask);
static_assert(static_cast<int32_t>(kExternalPointerSalt) >= 0,
"Salt value must be positive for better assembly code");
// Convert external pointer value into encoded form suitable for being stored
// on V8 heap.
V8_INLINE ExternalPointer_t EncodeExternalPointer(Isolate* isolate,
Address external_pointer);
// Convert external pointer from on-V8-heap representation to an actual external
// pointer value.
V8_INLINE Address DecodeExternalPointer(const Isolate* isolate,
ExternalPointer_t encoded_pointer);
constexpr ExternalPointer_t kNullExternalPointer = 0;
// Creates uninitialized entry in external pointer table and writes the entry id
// to the field.
// When sandbox is not enabled, it's a no-op.
V8_INLINE void InitExternalPointerField(Address field_address,
Isolate* isolate);
// Creates and initializes entry in external pointer table and writes the entry
// id to the field.
// Basically, it's InitExternalPointerField() followed by
// WriteExternalPointerField().
V8_INLINE void InitExternalPointerField(Address field_address, Isolate* isolate,
Address value);
// Reads external pointer for the field, and decodes it if the sandbox is
// enabled.
V8_INLINE Address ReadExternalPointerField(Address field_address,
const Isolate* isolate);
// Encodes value if the sandbox is enabled and writes it into the field.
V8_INLINE void WriteExternalPointerField(Address field_address,
Isolate* isolate, Address value);
} // namespace internal
} // namespace v8

View File

@ -295,7 +295,6 @@ STATIC_ASSERT(kPointerSize == (1 << kPointerSizeLog2));
// This type defines raw storage type for external (or off-V8 heap) pointers
// stored on V8 heap.
using ExternalPointer_t = Address;
constexpr int kExternalPointerSize = sizeof(ExternalPointer_t);
constexpr int kEmbedderDataSlotSize = kSystemPointerSize;

View File

@ -307,6 +307,7 @@ Reduction MemoryLowering::ReduceLoadElement(Node* node) {
}
Node* MemoryLowering::DecodeExternalPointer(Node* node) {
#ifdef V8_HEAP_SANDBOX
DCHECK(V8_HEAP_SANDBOX_BOOL);
DCHECK(node->opcode() == IrOpcode::kLoad ||
node->opcode() == IrOpcode::kPoisonedLoad);
@ -317,16 +318,25 @@ Node* MemoryLowering::DecodeExternalPointer(Node* node) {
// Clone the load node and put it here.
// TODO(turbofan): consider adding GraphAssembler::Clone() suitable for
// cloning nodes from arbitrary locaions in effect/control chains.
Node* node_copy = __ AddNode(graph()->CloneNode(node));
Node* index = __ AddNode(graph()->CloneNode(node));
// Uncomment this to generate a breakpoint for debugging purposes.
// __ DebugBreak();
// Decode loaded enternal pointer.
// Decode loaded external pointer.
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
Node* salt = __ IntPtrConstant(kExternalPointerSalt);
Node* decoded_ptr = __ WordXor(node_copy, salt);
Node* external_pointer_table_address = __ ExternalConstant(
ExternalReference::external_pointer_table_address(isolate()));
Node* table = __ Load(MachineType::Pointer(), external_pointer_table_address,
Internals::kExternalPointerTableBufferOffset);
// TODO(v8:10391, saelo): bounds check if table is not caged
Node* offset = __ Int32Mul(index, __ Int32Constant(8));
Node* decoded_ptr =
__ Load(MachineType::Pointer(), table, __ ChangeUint32ToUint64(offset));
return decoded_ptr;
#else
return node;
#endif // V8_HEAP_SANDBOX
}
Reduction MemoryLowering::ReduceLoadField(Node* node) {
@ -335,6 +345,11 @@ Reduction MemoryLowering::ReduceLoadField(Node* node) {
Node* offset = __ IntPtrConstant(access.offset - access.tag());
node->InsertInput(graph_zone(), 1, offset);
MachineType type = access.machine_type;
if (V8_HEAP_SANDBOX_BOOL &&
access.type.Is(Type::SandboxedExternalPointer())) {
// External pointer table indices are 32bit numbers
type = MachineType::Uint32();
}
if (NeedsPoisoning(access.load_sensitivity)) {
NodeProperties::ChangeOp(node, machine()->PoisonedLoad(type));
} else {

View File

@ -0,0 +1,22 @@
// Copyright 2020 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.
#include "src/execution/external-pointer-table.h"
namespace v8 {
namespace internal {
void ExternalPointerTable::GrowTable(ExternalPointerTable* table) {
// TODO(v8:10391, saelo): overflow check here and in the multiplication below
uint32_t new_capacity = table->capacity_ + table->capacity_ / 2;
table->buffer_ = reinterpret_cast<Address*>(
realloc(table->buffer_, new_capacity * sizeof(Address)));
CHECK(table->buffer_);
memset(&table->buffer_[table->capacity_], 0,
(new_capacity - table->capacity_) * sizeof(Address));
table->capacity_ = new_capacity;
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,80 @@
// Copyright 2020 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_EXECUTION_EXTERNAL_POINTER_TABLE_H_
#define V8_EXECUTION_EXTERNAL_POINTER_TABLE_H_
#include "src/common/external-pointer.h"
#include "src/utils/utils.h"
namespace v8 {
namespace internal {
class V8_EXPORT_PRIVATE ExternalPointerTable {
public:
static const int kExternalPointerTableInitialCapacity = 1024;
ExternalPointerTable()
: buffer_(reinterpret_cast<Address*>(
calloc(kExternalPointerTableInitialCapacity, sizeof(Address)))),
length_(1),
capacity_(kExternalPointerTableInitialCapacity),
freelist_head_(0) {
// Explicitly setup the invalid nullptr entry.
STATIC_ASSERT(kNullExternalPointer == 0);
buffer_[kNullExternalPointer] = kNullAddress;
}
~ExternalPointerTable() { ::free(buffer_); }
Address get(uint32_t index) const {
CHECK_LT(index, length_);
return buffer_[index];
}
void set(uint32_t index, Address value) {
DCHECK_NE(kNullExternalPointer, index);
CHECK_LT(index, length_);
buffer_[index] = value;
}
uint32_t allocate() {
uint32_t index = length_++;
if (index >= capacity_) {
GrowTable(this);
}
DCHECK_NE(kNullExternalPointer, index);
return index;
}
void free(uint32_t index) {
// TODO(v8:10391, saelo): implement simple free list here, i.e. set
// buffer_[index] to freelist_head_ and set freelist_head
// to index
DCHECK_NE(kNullExternalPointer, index);
}
// Returns true if the entry exists in the table and therefore it can be read.
bool is_valid_index(uint32_t index) const {
// TODO(v8:10391, saelo): also check here if entry is free
return index < length_;
}
uint32_t size() const { return length_; }
static void GrowTable(ExternalPointerTable* table);
private:
friend class Isolate;
Address* buffer_;
uint32_t length_;
uint32_t capacity_;
uint32_t freelist_head_;
};
} // namespace internal
} // namespace v8
#endif // V8_EXECUTION_EXTERNAL_POINTER_TABLE_H_

View File

@ -8,6 +8,7 @@
#include "src/builtins/builtins.h"
#include "src/codegen/constants-arch.h"
#include "src/codegen/external-reference-table.h"
#include "src/execution/external-pointer-table.h"
#include "src/execution/stack-guard.h"
#include "src/execution/thread-local-top.h"
#include "src/roots/roots.h"
@ -131,6 +132,7 @@ class IsolateData final {
V(kThreadLocalTopOffset, ThreadLocalTop::kSizeInBytes) \
V(kBuiltinEntryTableOffset, Builtins::builtin_count* kSystemPointerSize) \
V(kBuiltinsTableOffset, Builtins::builtin_count* kSystemPointerSize) \
FIELDS_HEAP_SANDBOX(V) \
V(kStackIsIterableOffset, kUInt8Size) \
/* This padding aligns IsolateData size by 8 bytes. */ \
V(kPaddingOffset, \
@ -138,6 +140,13 @@ class IsolateData final {
/* Total size. */ \
V(kSize, 0)
#ifdef V8_HEAP_SANDBOX
#define FIELDS_HEAP_SANDBOX(V) \
V(kExternalPointerTableOffset, kSystemPointerSize * 3)
#else
#define FIELDS_HEAP_SANDBOX(V)
#endif // V8_HEAP_SANDBOX
DEFINE_FIELD_OFFSET_CONSTANTS(0, FIELDS)
#undef FIELDS
@ -172,6 +181,11 @@ class IsolateData final {
// The entries in this array are tagged pointers to Code objects.
Address builtins_[Builtins::builtin_count] = {};
// Table containing pointers to external objects.
#ifdef V8_HEAP_SANDBOX
ExternalPointerTable external_pointer_table_;
#endif
// Whether the SafeStackFrameIterator can successfully iterate the current
// stack. Only valid values are 0 or 1.
uint8_t stack_is_iterable_ = 1;
@ -215,6 +229,10 @@ void IsolateData::AssertPredictableLayout() {
STATIC_ASSERT(offsetof(IsolateData, fast_c_call_caller_pc_) ==
kFastCCallCallerPCOffset);
STATIC_ASSERT(offsetof(IsolateData, stack_guard_) == kStackGuardOffset);
#ifdef V8_HEAP_SANDBOX
STATIC_ASSERT(offsetof(IsolateData, external_pointer_table_) ==
kExternalPointerTableOffset);
#endif
STATIC_ASSERT(offsetof(IsolateData, stack_is_iterable_) ==
kStackIsIterableOffset);
STATIC_ASSERT(sizeof(IsolateData) == IsolateData::kSize);

View File

@ -2982,6 +2982,15 @@ void Isolate::CheckIsolateLayout() {
Internals::kIsolateStackGuardOffset);
CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, isolate_data_.roots_)),
Internals::kIsolateRootsOffset);
#ifdef V8_HEAP_SANDBOX
CHECK_EQ(static_cast<int>(OFFSET_OF(ExternalPointerTable, buffer_)),
Internals::kExternalPointerTableBufferOffset);
CHECK_EQ(static_cast<int>(OFFSET_OF(ExternalPointerTable, length_)),
Internals::kExternalPointerTableLengthOffset);
CHECK_EQ(static_cast<int>(OFFSET_OF(ExternalPointerTable, capacity_)),
Internals::kExternalPointerTableCapacityOffset);
#endif
}
void Isolate::ClearSerializerData() {

View File

@ -23,6 +23,7 @@
#include "src/common/globals.h"
#include "src/debug/interface-types.h"
#include "src/execution/execution.h"
#include "src/execution/external-pointer-table.h"
#include "src/execution/futex-emulation.h"
#include "src/execution/isolate-data.h"
#include "src/execution/messages.h"
@ -1560,6 +1561,20 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
MaybeLocal<v8::Context> GetContextFromRecorderContextId(
v8::metrics::Recorder::ContextId id);
#ifdef V8_HEAP_SANDBOX
ExternalPointerTable& external_pointer_table() {
return isolate_data_.external_pointer_table_;
}
const ExternalPointerTable& external_pointer_table() const {
return isolate_data_.external_pointer_table_;
}
Address external_pointer_table_address() {
return reinterpret_cast<Address>(&isolate_data_.external_pointer_table_);
}
#endif
private:
explicit Isolate(std::unique_ptr<IsolateAllocator> isolate_allocator);
~Isolate();

View File

@ -94,10 +94,10 @@ void LocalEmbedderHeapTracer::ProcessingScope::TracePossibleWrapper(
void* pointer0;
void* pointer1;
if (EmbedderDataSlot(js_object, 0)
.ToAlignedPointer(tracer_->isolate_, &pointer0) &&
.ToAlignedPointerSafe(tracer_->isolate_, &pointer0) &&
pointer0 &&
EmbedderDataSlot(js_object, 1)
.ToAlignedPointer(tracer_->isolate_, &pointer1)) {
.ToAlignedPointerSafe(tracer_->isolate_, &pointer1)) {
wrapper_cache_.push_back({pointer0, pointer1});
}
FlushWrapperCacheIfFull();

View File

@ -473,6 +473,10 @@ Handle<EmbedderDataArray> Factory::NewEmbedderDataArray(int length) {
ObjectSlot end(array->slots_end());
size_t slot_count = end - start;
MemsetTagged(start, *undefined_value(), slot_count);
for (int i = 0; i < length; i++) {
// TODO(v8:10391, saelo): Handle external pointers in EmbedderDataSlot
EmbedderDataSlot(*array, i).AllocateExternalPointerEntry(isolate());
}
}
return array;
}
@ -834,6 +838,7 @@ Handle<StringClass> Factory::InternalizeExternalString(Handle<String> string) {
Handle<Map> map = GetInternalizedStringMap(this, string).ToHandleChecked();
Handle<StringClass> external_string(
StringClass::cast(New(map, AllocationType::kOld)), isolate());
external_string->AllocateExternalPointerEntries(isolate());
external_string->set_length(cast_string->length());
external_string->set_hash_field(cast_string->hash_field());
external_string->SetResource(isolate(), nullptr);
@ -959,6 +964,7 @@ MaybeHandle<String> Factory::NewExternalStringFromOneByte(
: uncached_external_one_byte_string_map();
Handle<ExternalOneByteString> external_string(
ExternalOneByteString::cast(New(map, AllocationType::kOld)), isolate());
external_string->AllocateExternalPointerEntries(isolate());
external_string->set_length(static_cast<int>(length));
external_string->set_hash_field(String::kEmptyHashField);
external_string->SetResource(isolate(), resource);
@ -979,6 +985,7 @@ MaybeHandle<String> Factory::NewExternalStringFromTwoByte(
: uncached_external_string_map();
Handle<ExternalTwoByteString> external_string(
ExternalTwoByteString::cast(New(map, AllocationType::kOld)), isolate());
external_string->AllocateExternalPointerEntries(isolate());
external_string->set_length(static_cast<int>(length));
external_string->set_hash_field(String::kEmptyHashField);
external_string->SetResource(isolate(), resource);
@ -1063,6 +1070,7 @@ Handle<NativeContext> Factory::NewNativeContext() {
AllocationType::kOld));
context->set_native_context_map(*map);
map->set_native_context(*context);
context->AllocateExternalPointerEntries(isolate());
context->set_scope_info(ReadOnlyRoots(isolate()).native_scope_info());
context->set_previous(Context::unchecked_cast(Smi::zero()));
context->set_extension(*undefined_value());
@ -1313,6 +1321,7 @@ Handle<Foreign> Factory::NewForeign(Address addr) {
HeapObject result = AllocateRawWithImmortalMap(map.instance_size(),
AllocationType::kYoung, map);
Handle<Foreign> foreign(Foreign::cast(result), isolate());
foreign->AllocateExternalPointerEntries(isolate());
foreign->set_foreign_address(isolate(), addr);
return foreign;
}
@ -1324,6 +1333,7 @@ Handle<WasmTypeInfo> Factory::NewWasmTypeInfo(Address type_address,
HeapObject result = AllocateRawWithImmortalMap(map.instance_size(),
AllocationType::kYoung, map);
Handle<WasmTypeInfo> info(WasmTypeInfo::cast(result), isolate());
info->AllocateExternalPointerEntries(isolate());
info->set_foreign_address(isolate(), type_address);
info->set_parent(*parent);
info->set_subtypes(*subtypes);
@ -2696,6 +2706,7 @@ Handle<JSTypedArray> Factory::NewJSTypedArray(ExternalArrayType type,
Handle<JSTypedArray> typed_array =
Handle<JSTypedArray>::cast(NewJSArrayBufferView(
map, empty_byte_array(), buffer, byte_offset, byte_length));
typed_array->AllocateExternalPointerEntries(isolate());
typed_array->set_length(length);
typed_array->SetOffHeapDataPtr(isolate(), buffer->backing_store(),
byte_offset);
@ -2709,6 +2720,7 @@ Handle<JSDataView> Factory::NewJSDataView(Handle<JSArrayBuffer> buffer,
isolate());
Handle<JSDataView> obj = Handle<JSDataView>::cast(NewJSArrayBufferView(
map, empty_fixed_array(), buffer, byte_offset, byte_length));
obj->AllocateExternalPointerEntries(isolate());
obj->set_data_pointer(
isolate(), static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
return obj;

View File

@ -211,8 +211,7 @@ void AccessorAssembler::HandleLoadAccessor(
TNode<Foreign> foreign = LoadObjectField<Foreign>(
call_handler_info, CallHandlerInfo::kJsCallbackOffset);
TNode<RawPtrT> callback =
DecodeExternalPointer(LoadForeignForeignAddress(foreign));
TNode<RawPtrT> callback = LoadForeignForeignAddressPtr(foreign);
TNode<Object> data =
LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset);
@ -1669,8 +1668,7 @@ void AccessorAssembler::HandleStoreICProtoHandler(
TNode<Foreign> foreign = LoadObjectField<Foreign>(
call_handler_info, CallHandlerInfo::kJsCallbackOffset);
TNode<RawPtrT> callback =
DecodeExternalPointer(LoadForeignForeignAddress(foreign));
TNode<RawPtrT> callback = LoadForeignForeignAddressPtr(foreign);
TNode<Object> data =
LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset);

View File

@ -5274,6 +5274,14 @@ Genesis::Genesis(
}
}
// TODO(v8:10391): The reason is that the NativeContext::microtask_queue
// serialization is not actually supported, and therefore the field is
// serialized as raw data instead of being serialized as ExternalReference.
// As a result, when V8 heap sandbox is enabled, the external pointer entry
// is not allocated for microtask queue field during deserialization, so we
// allocate it manually here.
native_context()->AllocateExternalPointerEntries(isolate);
native_context()->set_microtask_queue(
isolate, microtask_queue ? static_cast<MicrotaskQueue*>(microtask_queue)
: isolate->default_microtask_queue());

View File

@ -268,17 +268,18 @@ Map Context::GetInitialJSArrayMap(ElementsKind kind) const {
}
DEF_GETTER(NativeContext, microtask_queue, MicrotaskQueue*) {
ExternalPointer_t encoded_value =
ReadField<ExternalPointer_t>(kMicrotaskQueueOffset);
return reinterpret_cast<MicrotaskQueue*>(
DecodeExternalPointer(isolate, encoded_value));
ReadExternalPointerField(kMicrotaskQueueOffset, isolate));
}
void NativeContext::AllocateExternalPointerEntries(Isolate* isolate) {
InitExternalPointerField(kMicrotaskQueueOffset, isolate);
}
void NativeContext::set_microtask_queue(Isolate* isolate,
MicrotaskQueue* microtask_queue) {
ExternalPointer_t encoded_value = EncodeExternalPointer(
isolate, reinterpret_cast<Address>(microtask_queue));
WriteField<ExternalPointer_t>(kMicrotaskQueueOffset, encoded_value);
WriteExternalPointerField(kMicrotaskQueueOffset, isolate,
reinterpret_cast<Address>(microtask_queue));
}
void NativeContext::synchronized_set_script_context_table(

View File

@ -662,6 +662,8 @@ class NativeContext : public Context {
DECL_CAST(NativeContext)
// TODO(neis): Move some stuff from Context here.
inline void AllocateExternalPointerEntries(Isolate* isolate);
// [microtask_queue]: pointer to the MicrotaskQueue object.
DECL_GETTER(microtask_queue, MicrotaskQueue*)
inline void set_microtask_queue(Isolate* isolate, MicrotaskQueue* queue);

View File

@ -27,6 +27,19 @@ EmbedderDataSlot::EmbedderDataSlot(JSObject object, int embedder_field_index)
: SlotBase(FIELD_ADDR(
object, object.GetEmbedderFieldOffset(embedder_field_index))) {}
void EmbedderDataSlot::AllocateExternalPointerEntry(Isolate* isolate) {
#ifdef V8_HEAP_SANDBOX
// TODO(v8:10391, saelo): Use InitExternalPointerField() once
// ExternalPointer_t is 4-bytes.
uint32_t index = isolate->external_pointer_table().allocate();
// Object slots don't support storing raw values, so we just "reinterpret
// cast" the index value to Object.
Object index_as_object(index);
ObjectSlot(address() + kRawPayloadOffset).Relaxed_Store(index_as_object);
ObjectSlot(address() + kTaggedPayloadOffset).Relaxed_Store(Smi::zero());
#endif
}
Object EmbedderDataSlot::load_tagged() const {
return ObjectSlot(address() + kTaggedPayloadOffset).Relaxed_Load();
}
@ -61,7 +74,8 @@ void EmbedderDataSlot::store_tagged(JSObject object, int embedder_field_index,
.Relaxed_Store(value);
WRITE_BARRIER(object, slot_offset + kTaggedPayloadOffset, value);
#ifdef V8_COMPRESS_POINTERS
// See gc_safe_store() for the reasons behind two stores.
// See gc_safe_store() for the reasons behind two stores and why the second is
// only done if !V8_HEAP_SANDBOX_BOOL
ObjectSlot(FIELD_ADDR(object, slot_offset + kRawPayloadOffset))
.Relaxed_Store(Smi::zero());
#endif
@ -73,28 +87,57 @@ bool EmbedderDataSlot::ToAlignedPointer(const Isolate* isolate,
// are accessed this way only from the main thread via API during "mutator"
// phase which is propely synched with GC (concurrent marker may still look
// at the tagged part of the embedder slot but read-only access is ok).
#ifdef V8_COMPRESS_POINTERS
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
// fields (external pointers, doubles and BigInt data) are only kTaggedSize
// aligned so we have to use unaligned pointer friendly way of accessing them
// in order to avoid undefined behavior in C++ code.
Address raw_value = base::ReadUnalignedValue<Address>(address());
// We currently have to treat zero as nullptr in embedder slots.
if (raw_value) raw_value = DecodeExternalPointer(isolate, raw_value);
Address raw_value;
#ifdef V8_HEAP_SANDBOX
uint32_t index = base::Memory<uint32_t>(address() + kRawPayloadOffset);
raw_value = isolate->external_pointer_table().get(index);
#else
Address raw_value = *location();
if (COMPRESS_POINTERS_BOOL) {
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
// fields (external pointers, doubles and BigInt data) are only kTaggedSize
// aligned so we have to use unaligned pointer friendly way of accessing
// them in order to avoid undefined behavior in C++ code.
raw_value = base::ReadUnalignedValue<Address>(address());
} else {
raw_value = *location();
}
#endif
*out_pointer = reinterpret_cast<void*>(raw_value);
return HAS_SMI_TAG(raw_value);
}
bool EmbedderDataSlot::ToAlignedPointerSafe(const Isolate* isolate,
void** out_pointer) const {
#ifdef V8_HEAP_SANDBOX
uint32_t index = base::Memory<uint32_t>(address() + kRawPayloadOffset);
Address raw_value;
if (isolate->external_pointer_table().is_valid_index(index)) {
raw_value = isolate->external_pointer_table().get(index);
*out_pointer = reinterpret_cast<void*>(raw_value);
return true;
}
return false;
#else
return ToAlignedPointer(isolate, out_pointer);
#endif // V8_HEAP_SANDBOX
}
bool EmbedderDataSlot::store_aligned_pointer(Isolate* isolate, void* ptr) {
Address value = reinterpret_cast<Address>(ptr);
if (!HAS_SMI_TAG(value)) return false;
// We currently have to treat zero as nullptr in embedder slots.
if (value) value = EncodeExternalPointer(isolate, value);
DCHECK(HAS_SMI_TAG(value));
gc_safe_store(value);
#ifdef V8_HEAP_SANDBOX
if (V8_HEAP_SANDBOX_BOOL) {
AllocateExternalPointerEntry(isolate);
// Raw payload contains the table index. Object slots don't support loading
// of raw values, so we just "reinterpret cast" Object value to index.
Object index_as_object =
ObjectSlot(address() + kRawPayloadOffset).Relaxed_Load();
uint32_t index = static_cast<uint32_t>(index_as_object.ptr());
isolate->external_pointer_table().set(index, value);
return true;
}
#endif
gc_safe_store(isolate, value);
return true;
}
@ -109,10 +152,7 @@ EmbedderDataSlot::RawData EmbedderDataSlot::load_raw(
// fields (external pointers, doubles and BigInt data) are only kTaggedSize
// aligned so we have to use unaligned pointer friendly way of accessing them
// in order to avoid undefined behavior in C++ code.
Address value = base::ReadUnalignedValue<Address>(address());
// We currently have to treat zero as nullptr in embedder slots.
if (value) return DecodeExternalPointer(isolate, value);
return value;
return base::ReadUnalignedValue<EmbedderDataSlot::RawData>(address());
#else
return *location();
#endif
@ -121,16 +161,15 @@ EmbedderDataSlot::RawData EmbedderDataSlot::load_raw(
void EmbedderDataSlot::store_raw(Isolate* isolate,
EmbedderDataSlot::RawData data,
const DisallowGarbageCollection& no_gc) {
// We currently have to treat zero as nullptr in embedder slots.
if (data) data = EncodeExternalPointer(isolate, data);
gc_safe_store(data);
gc_safe_store(isolate, data);
}
void EmbedderDataSlot::gc_safe_store(Address value) {
void EmbedderDataSlot::gc_safe_store(Isolate* isolate, Address value) {
#ifdef V8_COMPRESS_POINTERS
STATIC_ASSERT(kSmiShiftSize == 0);
STATIC_ASSERT(SmiValuesAre31Bits());
STATIC_ASSERT(kTaggedSize == kInt32Size);
// We have to do two 32-bit stores here because
// 1) tagged part modifications must be atomic to be properly synchronized
// with the concurrent marker.

View File

@ -43,7 +43,11 @@ class EmbedderDataSlot
#endif
#ifdef V8_COMPRESS_POINTERS
// The raw payload is located in the other tagged part of the full pointer.
// The raw payload is located in the other "tagged" part of the full pointer
// and cotains the upper part of aligned address. The raw part is not expected
// to look like a tagged value.
// When V8_HEAP_SANDBOX is defined the raw payload contains an index into the
// external pointer table.
static constexpr int kRawPayloadOffset = kTaggedSize - kTaggedPayloadOffset;
#endif
static constexpr int kRequiredPtrAlignment = kSmiTagSize;
@ -51,6 +55,8 @@ class EmbedderDataSlot
// Opaque type used for storing raw embedder data.
using RawData = Address;
V8_INLINE void AllocateExternalPointerEntry(Isolate* isolate);
V8_INLINE Object load_tagged() const;
V8_INLINE void store_smi(Smi value);
@ -66,9 +72,24 @@ class EmbedderDataSlot
// the pointer-like value. Note, that some Smis could still look like an
// aligned pointers.
// Returns true on success.
// When V8 heap sandbox is enabled, calling this method when the raw part of
// the slot does not contain valid external pointer table index is undefined
// behaviour and most likely result in crashes.
V8_INLINE bool ToAlignedPointer(const Isolate* isolate,
void** out_result) const;
// Same as ToAlignedPointer() but with a workaround for V8 heap sandbox.
// When V8 heap sandbox is enabled, this method doesn't crash when the raw
// part of the slot contains "undefined" instead of a correct external table
// entry index (see Factory::InitializeJSObjectBody() for details).
// Returns true when the external pointer table index was pointing to a valid
// entry, otherwise false.
//
// Call this function if you are not sure whether the slot contains valid
// external pointer or not.
V8_INLINE bool ToAlignedPointerSafe(const Isolate* isolate,
void** out_result) const;
// Returns true if the pointer was successfully stored or false it the pointer
// was improperly aligned.
V8_INLINE V8_WARN_UNUSED_RESULT bool store_aligned_pointer(Isolate* isolate,
@ -82,7 +103,7 @@ class EmbedderDataSlot
private:
// Stores given value to the embedder data slot in a concurrent-marker
// friendly manner (tagged part of the slot is written atomically).
V8_INLINE void gc_safe_store(Address value);
V8_INLINE void gc_safe_store(Isolate* isolate, Address value);
};
} // namespace internal

View File

@ -27,15 +27,15 @@ bool Foreign::IsNormalized(Object value) {
}
DEF_GETTER(Foreign, foreign_address, Address) {
ExternalPointer_t encoded_value =
ReadField<ExternalPointer_t>(kForeignAddressOffset);
Address value = DecodeExternalPointer(isolate, encoded_value);
return value;
return ReadExternalPointerField(kForeignAddressOffset, isolate);
}
void Foreign::AllocateExternalPointerEntries(Isolate* isolate) {
InitExternalPointerField(kForeignAddressOffset, isolate);
}
void Foreign::set_foreign_address(Isolate* isolate, Address value) {
ExternalPointer_t encoded_value = EncodeExternalPointer(isolate, value);
WriteField<ExternalPointer_t>(kForeignAddressOffset, encoded_value);
WriteExternalPointerField(kForeignAddressOffset, isolate, value);
}
} // namespace internal

View File

@ -43,6 +43,8 @@ class Foreign : public TorqueGeneratedForeign<Foreign, HeapObject> {
friend class StartupSerializer;
friend class WasmTypeInfo;
inline void AllocateExternalPointerEntries(Isolate* isolate);
inline void set_foreign_address(Isolate* isolate, Address value);
TQ_OBJECT_CONSTRUCTORS(Foreign)

View File

@ -7,3 +7,6 @@
extern class Foreign extends HeapObject {
foreign_address: ExternalPointer;
}
extern operator '.foreign_address_ptr' macro LoadForeignForeignAddressPtr(
Foreign): RawPtr;

View File

@ -25,6 +25,10 @@ TQ_OBJECT_CONSTRUCTORS_IMPL(JSArrayBufferView)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSTypedArray)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSDataView)
void JSArrayBuffer::AllocateExternalPointerEntries(Isolate* isolate) {
InitExternalPointerField(kBackingStoreOffset, isolate);
}
size_t JSArrayBuffer::byte_length() const {
return ReadField<size_t>(kByteLengthOffset);
}
@ -34,26 +38,23 @@ void JSArrayBuffer::set_byte_length(size_t value) {
}
DEF_GETTER(JSArrayBuffer, backing_store, void*) {
ExternalPointer_t encoded_value =
ReadField<ExternalPointer_t>(kBackingStoreOffset);
return reinterpret_cast<void*>(DecodeExternalPointer(isolate, encoded_value));
Address value = ReadExternalPointerField(kBackingStoreOffset, isolate);
return reinterpret_cast<void*>(value);
}
void JSArrayBuffer::set_backing_store(Isolate* isolate, void* value) {
ExternalPointer_t encoded_value =
EncodeExternalPointer(isolate, reinterpret_cast<Address>(value));
WriteField<ExternalPointer_t>(kBackingStoreOffset, encoded_value);
WriteExternalPointerField(kBackingStoreOffset, isolate,
reinterpret_cast<Address>(value));
}
uint32_t JSArrayBuffer::GetBackingStoreRefForDeserialization() const {
ExternalPointer_t encoded_value =
ReadField<ExternalPointer_t>(kBackingStoreOffset);
return static_cast<uint32_t>(encoded_value);
return static_cast<uint32_t>(
ReadField<ExternalPointer_t>(kBackingStoreOffset));
}
void JSArrayBuffer::SetBackingStoreRefForSerialization(uint32_t ref) {
ExternalPointer_t encoded_value = ref;
WriteField<ExternalPointer_t>(kBackingStoreOffset, encoded_value);
WriteField<ExternalPointer_t>(kBackingStoreOffset,
static_cast<ExternalPointer_t>(ref));
}
ArrayBufferExtension* JSArrayBuffer::extension() const {
@ -160,7 +161,6 @@ BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_asmjs_memory,
BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_shared,
JSArrayBuffer::IsSharedBit)
size_t JSArrayBufferView::byte_offset() const {
return ReadField<size_t>(kByteOffsetOffset);
}
@ -181,6 +181,10 @@ bool JSArrayBufferView::WasDetached() const {
return JSArrayBuffer::cast(buffer()).was_detached();
}
void JSTypedArray::AllocateExternalPointerEntries(Isolate* isolate) {
InitExternalPointerField(kExternalPointerOffset, isolate);
}
size_t JSTypedArray::length() const { return ReadField<size_t>(kLengthOffset); }
void JSTypedArray::set_length(size_t value) {
@ -188,14 +192,15 @@ void JSTypedArray::set_length(size_t value) {
}
DEF_GETTER(JSTypedArray, external_pointer, Address) {
ExternalPointer_t encoded_value =
ReadField<ExternalPointer_t>(kExternalPointerOffset);
return DecodeExternalPointer(isolate, encoded_value);
return ReadExternalPointerField(kExternalPointerOffset, isolate);
}
DEF_GETTER(JSTypedArray, external_pointer_raw, ExternalPointer_t) {
return ReadField<ExternalPointer_t>(kExternalPointerOffset);
}
void JSTypedArray::set_external_pointer(Isolate* isolate, Address value) {
ExternalPointer_t encoded_value = EncodeExternalPointer(isolate, value);
WriteField<ExternalPointer_t>(kExternalPointerOffset, encoded_value);
WriteExternalPointerField(kExternalPointerOffset, isolate, value);
}
Address JSTypedArray::ExternalPointerCompensationForOnHeapArray(
@ -209,15 +214,14 @@ Address JSTypedArray::ExternalPointerCompensationForOnHeapArray(
uint32_t JSTypedArray::GetExternalBackingStoreRefForDeserialization() const {
DCHECK(!is_on_heap());
ExternalPointer_t encoded_value =
ReadField<ExternalPointer_t>(kExternalPointerOffset);
return static_cast<uint32_t>(encoded_value);
return static_cast<uint32_t>(
ReadField<ExternalPointer_t>(kExternalPointerOffset));
}
void JSTypedArray::SetExternalBackingStoreRefForSerialization(uint32_t ref) {
DCHECK(!is_on_heap());
ExternalPointer_t encoded_value = ref;
WriteField<ExternalPointer_t>(kExternalPointerOffset, encoded_value);
WriteField<ExternalPointer_t>(kExternalPointerOffset,
static_cast<ExternalPointer_t>(ref));
}
void JSTypedArray::RemoveExternalPointerCompensationForSerialization(
@ -227,9 +231,15 @@ void JSTypedArray::RemoveExternalPointerCompensationForSerialization(
// compensation by replacing external_pointer and base_pointer fields
// with one data_pointer field which can point to either external data
// backing store or into on-heap backing store.
set_external_pointer(
isolate,
external_pointer() - ExternalPointerCompensationForOnHeapArray(isolate));
Address offset =
external_pointer() - ExternalPointerCompensationForOnHeapArray(isolate);
#ifdef V8_HEAP_SANDBOX
// Write decompensated offset directly to the external pointer field, thus
// allowing the offset to be propagated through serialization-deserialization.
WriteField<ExternalPointer_t>(kExternalPointerOffset, offset);
#else
set_external_pointer(isolate, offset);
#endif
}
void* JSTypedArray::DataPtr() {
@ -287,15 +297,17 @@ MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate,
}
DEF_GETTER(JSDataView, data_pointer, void*) {
ExternalPointer_t encoded_value =
ReadField<ExternalPointer_t>(kDataPointerOffset);
return reinterpret_cast<void*>(DecodeExternalPointer(isolate, encoded_value));
return reinterpret_cast<void*>(
ReadExternalPointerField(kDataPointerOffset, isolate));
}
void JSDataView::AllocateExternalPointerEntries(Isolate* isolate) {
InitExternalPointerField(kDataPointerOffset, isolate);
}
void JSDataView::set_data_pointer(Isolate* isolate, void* value) {
WriteField<ExternalPointer_t>(
kDataPointerOffset,
EncodeExternalPointer(isolate, reinterpret_cast<Address>(value)));
WriteExternalPointerField(kDataPointerOffset, isolate,
reinterpret_cast<Address>(value));
}
} // namespace internal

View File

@ -44,6 +44,7 @@ void JSArrayBuffer::Setup(SharedFlag shared,
SetEmbedderField(i, Smi::zero());
}
set_extension(nullptr);
AllocateExternalPointerEntries(GetIsolate());
if (!backing_store) {
set_backing_store(GetIsolate(), nullptr);
set_byte_length(0);

View File

@ -30,6 +30,12 @@ class JSArrayBuffer
static constexpr size_t kMaxByteLength = kMaxSafeInteger;
#endif
// When soft sandbox is enabled, creates entries in external pointer table for
// all JSArrayBuffer's fields that require soft sandbox protection (backing
// store pointer, backing store length, etc.).
// When sandbox is not enabled, it's a no-op.
inline void AllocateExternalPointerEntries(Isolate* isolate);
// [byte_length]: length in bytes
DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
@ -258,6 +264,12 @@ class JSTypedArray
V8_EXPORT_PRIVATE Handle<JSArrayBuffer> GetBuffer();
// When soft sandbox is enabled, creates entries in external pointer table for
// all JSTypedArray's fields that require soft sandbox protection (external
// pointer, offset, length, etc.).
// When sandbox is not enabled, it's a no-op.
inline void AllocateExternalPointerEntries(Isolate* isolate);
// Use with care: returns raw pointer into heap.
inline void* DataPtr();
@ -324,6 +336,8 @@ class JSTypedArray
// [external_pointer]: TODO(v8:4153)
DECL_GETTER(external_pointer, Address)
DECL_GETTER(external_pointer_raw, ExternalPointer_t)
inline void set_external_pointer(Isolate* isolate, Address value);
TQ_OBJECT_CONSTRUCTORS(JSTypedArray)
@ -336,6 +350,12 @@ class JSDataView
DECL_GETTER(data_pointer, void*)
inline void set_data_pointer(Isolate* isolate, void* value);
// When soft sandbox is enabled, creates entries in external pointer table for
// all JSDataView's fields that require soft sandbox protection (data pointer,
// offset, length, etc.).
// When sandbox is not enabled, it's a no-op.
inline void AllocateExternalPointerEntries(Isolate* isolate);
// Dispatched behavior.
DECL_PRINTER(JSDataView)
DECL_VERIFIER(JSDataView)

View File

@ -49,6 +49,11 @@ extern class JSTypedArray extends JSArrayBufferView {
base_pointer: ByteArray|Smi;
}
extern operator '.external_pointer_ptr' macro
LoadJSTypedArrayExternalPointerPtr(JSTypedArray): RawPtr;
extern operator '.external_pointer_ptr=' macro
StoreJSTypedArrayExternalPointerPtr(JSTypedArray, RawPtr);
@generateCppClass
extern class JSDataView extends JSArrayBufferView {
data_pointer: ExternalPointer;

View File

@ -283,6 +283,10 @@ int JSObject::GetEmbedderFieldOffset(int index) {
return GetEmbedderFieldsStartOffset() + (kEmbedderDataSlotSize * index);
}
void JSObject::InitializeEmbedderField(Isolate* isolate, int index) {
EmbedderDataSlot(*this, index).AllocateExternalPointerEntry(isolate);
}
Object JSObject::GetEmbedderField(int index) {
return EmbedderDataSlot(*this, index).load_tagged();
}

View File

@ -567,6 +567,7 @@ class JSObject : public TorqueGeneratedJSObject<JSObject, JSReceiver> {
static inline int GetEmbedderFieldCount(Map map);
inline int GetEmbedderFieldCount() const;
inline int GetEmbedderFieldOffset(int index);
inline void InitializeEmbedderField(Isolate* isolate, int index);
inline Object GetEmbedderField(int index);
inline void SetEmbedderField(int index, Object value);
inline void SetEmbedderField(int index, Smi value);

View File

@ -15,6 +15,7 @@
#include "src/base/bits.h"
#include "src/base/memory.h"
#include "src/builtins/builtins.h"
#include "src/common/external-pointer-inl.h"
#include "src/handles/handles-inl.h"
#include "src/heap/factory.h"
#include "src/heap/heap-write-barrier-inl.h"
@ -640,6 +641,25 @@ MaybeHandle<Object> Object::SetElement(Isolate* isolate, Handle<Object> object,
return value;
}
void Object::InitExternalPointerField(size_t offset, Isolate* isolate) {
i::InitExternalPointerField(field_address(offset), isolate);
}
void Object::InitExternalPointerField(size_t offset, Isolate* isolate,
Address value) {
i::InitExternalPointerField(field_address(offset), isolate, value);
}
Address Object::ReadExternalPointerField(size_t offset,
const Isolate* isolate) const {
return i::ReadExternalPointerField(field_address(offset), isolate);
}
void Object::WriteExternalPointerField(size_t offset, Isolate* isolate,
Address value) {
i::WriteExternalPointerField(field_address(offset), isolate, value);
}
ObjectSlot HeapObject::RawField(int byte_offset) const {
return ObjectSlot(FIELD_ADDR(*this, byte_offset));
}

View File

@ -666,6 +666,17 @@ class Object : public TaggedImpl<HeapObjectReferenceType::STRONG, Address> {
}
}
//
// ExternalPointer_t field accessors.
//
inline void InitExternalPointerField(size_t offset, Isolate* isolate);
inline void InitExternalPointerField(size_t offset, Isolate* isolate,
Address value);
inline Address ReadExternalPointerField(size_t offset,
const Isolate* isolate) const;
inline void WriteExternalPointerField(size_t offset, Isolate* isolate,
Address value);
protected:
inline Address field_address(size_t offset) const {
return ptr() + offset - kHeapObjectTag;

View File

@ -612,17 +612,18 @@ bool ExternalString::is_uncached() const {
return (type & kUncachedExternalStringMask) == kUncachedExternalStringTag;
}
DEF_GETTER(ExternalString, resource_as_address, Address) {
ExternalPointer_t encoded_address =
ReadField<ExternalPointer_t>(kResourceOffset);
return DecodeExternalPointer(isolate, encoded_address);
void ExternalString::AllocateExternalPointerEntries(Isolate* isolate) {
InitExternalPointerField(kResourceOffset, isolate);
if (is_uncached()) return;
InitExternalPointerField(kResourceDataOffset, isolate);
}
void ExternalString::set_address_as_resource(Isolate* isolate,
Address address) {
const ExternalPointer_t encoded_address =
EncodeExternalPointer(isolate, address);
WriteField<ExternalPointer_t>(kResourceOffset, encoded_address);
DEF_GETTER(ExternalString, resource_as_address, Address) {
return ReadExternalPointerField(kResourceOffset, isolate);
}
void ExternalString::set_address_as_resource(Isolate* isolate, Address value) {
WriteExternalPointerField(kResourceOffset, isolate, value);
if (IsExternalOneByteString()) {
ExternalOneByteString::cast(*this).update_data_cache(isolate);
} else {
@ -630,48 +631,40 @@ void ExternalString::set_address_as_resource(Isolate* isolate,
}
}
uint32_t ExternalString::resource_as_uint32() {
uint32_t ExternalString::GetResourceRefForDeserialization() {
ExternalPointer_t encoded_address =
ReadField<ExternalPointer_t>(kResourceOffset);
return static_cast<uint32_t>(encoded_address);
}
void ExternalString::set_uint32_as_resource(Isolate* isolate, uint32_t value) {
WriteField<ExternalPointer_t>(kResourceOffset, value);
void ExternalString::SetResourceRefForSerialization(uint32_t ref) {
WriteField<ExternalPointer_t>(kResourceOffset,
static_cast<ExternalPointer_t>(ref));
if (is_uncached()) return;
WriteField<ExternalPointer_t>(kResourceDataOffset,
EncodeExternalPointer(isolate, kNullAddress));
WriteField<ExternalPointer_t>(kResourceDataOffset, kNullExternalPointer);
}
void ExternalString::DisposeResource(Isolate* isolate) {
const ExternalPointer_t encoded_address =
ReadField<ExternalPointer_t>(kResourceOffset);
Address value = ReadExternalPointerField(kResourceOffset, isolate);
v8::String::ExternalStringResourceBase* resource =
reinterpret_cast<v8::String::ExternalStringResourceBase*>(
DecodeExternalPointer(isolate, encoded_address));
reinterpret_cast<v8::String::ExternalStringResourceBase*>(value);
// Dispose of the C++ object if it has not already been disposed.
if (resource != nullptr) {
resource->Dispose();
const ExternalPointer_t encoded_address =
EncodeExternalPointer(isolate, kNullAddress);
WriteField<ExternalPointer_t>(kResourceOffset, encoded_address);
WriteExternalPointerField(kResourceOffset, isolate, kNullAddress);
}
}
DEF_GETTER(ExternalOneByteString, resource,
const ExternalOneByteString::Resource*) {
const ExternalPointer_t encoded_address =
ReadField<ExternalPointer_t>(kResourceOffset);
return reinterpret_cast<Resource*>(
DecodeExternalPointer(isolate, encoded_address));
return reinterpret_cast<Resource*>(resource_as_address(isolate));
}
void ExternalOneByteString::update_data_cache(Isolate* isolate) {
if (is_uncached()) return;
const ExternalPointer_t encoded_resource_data = EncodeExternalPointer(
isolate, reinterpret_cast<Address>(resource()->data()));
WriteField<ExternalPointer_t>(kResourceDataOffset, encoded_resource_data);
WriteExternalPointerField(kResourceDataOffset, isolate,
reinterpret_cast<Address>(resource()->data()));
}
void ExternalOneByteString::SetResource(
@ -685,9 +678,8 @@ void ExternalOneByteString::SetResource(
void ExternalOneByteString::set_resource(
Isolate* isolate, const ExternalOneByteString::Resource* resource) {
const ExternalPointer_t encoded_address =
EncodeExternalPointer(isolate, reinterpret_cast<Address>(resource));
WriteField<ExternalPointer_t>(kResourceOffset, encoded_address);
WriteExternalPointerField(kResourceOffset, isolate,
reinterpret_cast<Address>(resource));
if (resource != nullptr) update_data_cache(isolate);
}
@ -702,17 +694,13 @@ uint8_t ExternalOneByteString::Get(int index) {
DEF_GETTER(ExternalTwoByteString, resource,
const ExternalTwoByteString::Resource*) {
const ExternalPointer_t encoded_address =
ReadField<ExternalPointer_t>(kResourceOffset);
return reinterpret_cast<Resource*>(
DecodeExternalPointer(isolate, encoded_address));
return reinterpret_cast<Resource*>(resource_as_address(isolate));
}
void ExternalTwoByteString::update_data_cache(Isolate* isolate) {
if (is_uncached()) return;
const ExternalPointer_t encoded_resource_data = EncodeExternalPointer(
isolate, reinterpret_cast<Address>(resource()->data()));
WriteField<ExternalPointer_t>(kResourceDataOffset, encoded_resource_data);
WriteExternalPointerField(kResourceDataOffset, isolate,
reinterpret_cast<Address>(resource()->data()));
}
void ExternalTwoByteString::SetResource(
@ -726,9 +714,8 @@ void ExternalTwoByteString::SetResource(
void ExternalTwoByteString::set_resource(
Isolate* isolate, const ExternalTwoByteString::Resource* resource) {
const ExternalPointer_t encoded_address =
EncodeExternalPointer(isolate, reinterpret_cast<Address>(resource));
WriteField<ExternalPointer_t>(kResourceOffset, encoded_address);
WriteExternalPointerField(kResourceOffset, isolate,
reinterpret_cast<Address>(resource));
if (resource != nullptr) update_data_cache(isolate);
}

View File

@ -198,6 +198,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
this->synchronized_set_map(new_map);
ExternalTwoByteString self = ExternalTwoByteString::cast(*this);
self.AllocateExternalPointerEntries(isolate);
self.SetResource(isolate, resource);
isolate->heap()->RegisterExternalString(*this);
if (is_internalized) self.Hash(); // Force regeneration of the hash value.
@ -268,6 +269,7 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) {
this->synchronized_set_map(new_map);
ExternalOneByteString self = ExternalOneByteString::cast(*this);
self.AllocateExternalPointerEntries(isolate);
self.SetResource(isolate, resource);
isolate->heap()->RegisterExternalString(*this);
if (is_internalized) self.Hash(); // Force regeneration of the hash value.

View File

@ -721,6 +721,8 @@ class ExternalString : public String {
static const int kUncachedSize =
kResourceOffset + FIELD_SIZE(kResourceOffset);
inline void AllocateExternalPointerEntries(Isolate* isolate);
// Return whether the external string data pointer is not cached.
inline bool is_uncached() const;
// Size in bytes of the external payload.
@ -729,8 +731,8 @@ class ExternalString : public String {
// Used in the serializer/deserializer.
DECL_GETTER(resource_as_address, Address)
inline void set_address_as_resource(Isolate* isolate, Address address);
inline uint32_t resource_as_uint32();
inline void set_uint32_as_resource(Isolate* isolate, uint32_t value);
inline uint32_t GetResourceRefForDeserialization();
inline void SetResourceRefForSerialization(uint32_t ref);
// Disposes string's resource object if it has not already been disposed.
inline void DisposeResource(Isolate* isolate);
@ -755,6 +757,7 @@ class ExternalOneByteString : public ExternalString {
// It is assumed that the previous resource is null. If it is not null, then
// it is the responsability of the caller the handle the previous resource.
inline void SetResource(Isolate* isolate, const Resource* buffer);
// Used only during serialization.
inline void set_resource(Isolate* isolate, const Resource* buffer);
@ -796,6 +799,7 @@ class ExternalTwoByteString : public ExternalString {
// It is assumed that the previous resource is null. If it is not null, then
// it is the responsability of the caller the handle the previous resource.
inline void SetResource(Isolate* isolate, const Resource* buffer);
// Used only during serialization.
inline void set_resource(Isolate* isolate, const Resource* buffer);

View File

@ -25,6 +25,11 @@ extern class ExternalString extends String {
resource_data: ExternalPointer;
}
extern operator '.resource_ptr' macro LoadExternalStringResourcePtr(
ExternalString): RawPtr;
extern operator '.resource_data_ptr' macro LoadExternalStringResourceDataPtr(
ExternalString): RawPtr;
@doNotGenerateCast
extern class ExternalOneByteString extends ExternalString {
}

View File

@ -74,6 +74,7 @@ void ContextDeserializer::SetupOffHeapArrayBufferBackingStores() {
for (Handle<JSArrayBuffer> buffer : new_off_heap_array_buffers()) {
uint32_t store_index = buffer->GetBackingStoreRefForDeserialization();
auto bs = backing_store(store_index);
buffer->AllocateExternalPointerEntries(isolate());
SharedFlag shared =
bs && bs->is_shared() ? SharedFlag::kShared : SharedFlag::kNotShared;
buffer->Setup(shared, bs);

View File

@ -51,9 +51,8 @@ TSlot Deserializer::WriteAddress(TSlot dest, Address value) {
template <typename TSlot>
TSlot Deserializer::WriteExternalPointer(TSlot dest, Address value) {
value = EncodeExternalPointer(isolate(), value);
DCHECK(!next_reference_is_weak_);
memcpy(dest.ToVoidPtr(), &value, kExternalPointerSize);
InitExternalPointerField(dest.address(), isolate(), value);
STATIC_ASSERT(IsAligned(kExternalPointerSize, TSlot::kSlotDataSize));
return dest + (kExternalPointerSize / TSlot::kSlotDataSize);
}
@ -230,9 +229,10 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj,
#endif
} else if (obj.IsExternalString()) {
ExternalString string = ExternalString::cast(obj);
uint32_t index = string.resource_as_uint32();
uint32_t index = string.GetResourceRefForDeserialization();
Address address =
static_cast<Address>(isolate()->api_external_references()[index]);
string.AllocateExternalPointerEntries(isolate());
string.set_address_as_resource(isolate(), address);
isolate()->heap()->UpdateExternalString(string, 0,
string.ExternalPayloadSize());
@ -241,13 +241,14 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj,
JSDataView data_view = JSDataView::cast(obj);
JSArrayBuffer buffer = JSArrayBuffer::cast(data_view.buffer());
void* backing_store = nullptr;
if (buffer.backing_store() != nullptr) {
uint32_t store_index = buffer.GetBackingStoreRefForDeserialization();
if (store_index != kNullRefSentinel) {
// The backing store of the JSArrayBuffer has not been correctly restored
// yet, as that may trigger GC. The backing_store field currently contains
// a numbered reference to an already deserialized backing store.
uint32_t store_index = buffer.GetBackingStoreRefForDeserialization();
backing_store = backing_stores_[store_index]->buffer_start();
}
data_view.AllocateExternalPointerEntries(isolate());
data_view.set_data_pointer(
isolate(),
reinterpret_cast<uint8_t*>(backing_store) + data_view.byte_offset());
@ -255,9 +256,11 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj,
JSTypedArray typed_array = JSTypedArray::cast(obj);
// Fixup typed array pointers.
if (typed_array.is_on_heap()) {
Address raw_external_pointer = typed_array.external_pointer_raw();
typed_array.AllocateExternalPointerEntries(isolate());
typed_array.SetOnHeapDataPtr(isolate(),
HeapObject::cast(typed_array.base_pointer()),
typed_array.external_pointer());
raw_external_pointer);
} else {
// Serializer writes backing store ref as a DataPtr() value.
uint32_t store_index =
@ -266,14 +269,18 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj,
auto start = backing_store
? reinterpret_cast<byte*>(backing_store->buffer_start())
: nullptr;
typed_array.AllocateExternalPointerEntries(isolate());
typed_array.SetOffHeapDataPtr(isolate(), start,
typed_array.byte_offset());
}
} else if (obj.IsJSArrayBuffer()) {
JSArrayBuffer buffer = JSArrayBuffer::cast(obj);
// Postpone allocation of backing store to avoid triggering the GC.
if (buffer.backing_store() != nullptr) {
if (buffer.GetBackingStoreRefForDeserialization() != kNullRefSentinel) {
new_off_heap_array_buffers_.push_back(handle(buffer, isolate()));
} else {
buffer.AllocateExternalPointerEntries(isolate());
buffer.set_backing_store(isolate(), nullptr);
}
} else if (obj.IsBytecodeArray()) {
// TODO(mythria): Remove these once we store the default values for these

View File

@ -56,7 +56,9 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
can_rehash_(false) {
allocator()->DecodeReservation(data->Reservations());
// We start the indices here at 1, so that we can distinguish between an
// actual index and a nullptr in a deserialized object requiring fix-up.
// actual index and a nullptr (serialized as kNullRefSentinel) in a
// deserialized object requiring fix-up.
STATIC_ASSERT(kNullRefSentinel == 0);
backing_stores_.push_back({});
}

View File

@ -14,6 +14,12 @@ namespace internal {
SerializerAllocator::SerializerAllocator(Serializer* serializer)
: serializer_(serializer) {
// This assert should have been added close to seen_backing_stores_index_
// field definition, but the SerializerDeserializer header is not available
// there.
STATIC_ASSERT(Serializer::kNullRefSentinel == 0);
DCHECK_EQ(seen_backing_stores_index_, 1);
for (int i = 0; i < kNumberOfPreallocatedSpaces; i++) {
pending_chunk_[i] = 0;
}

View File

@ -58,10 +58,9 @@ class SerializerAllocator final {
uint32_t seen_large_objects_index_ = 0;
// Used to keep track of the off-heap backing stores used by TypedArrays/
// ArrayBuffers. Note that the index begins at 1 and not 0, because when a
// TypedArray has an on-heap backing store, the backing_store pointer in the
// corresponding ArrayBuffer will be null, which makes it indistinguishable
// from index 0.
// ArrayBuffers. Note that the index begins at 1 and not 0, because the latter
// value represents null backing store pointer (see usages of
// SerializerDeserializer::kNullRefSentinel).
uint32_t seen_backing_stores_index_ = 1;
uint32_t custom_chunk_size_ = 0;

View File

@ -303,6 +303,10 @@ class SerializerDeserializer : public RootVisitor {
RootIndex>;
using HotObject = BytecodeValueEncoder<kHotObject, 0, kHotObjectCount - 1>;
// This backing store reference value represents nullptr values during
// serialization/deserialization.
static const uint32_t kNullRefSentinel = 0;
// ---------- member variable ----------
HotObjectsList hot_objects_;
};

View File

@ -470,6 +470,10 @@ void Serializer::ObjectSerializer::SerializeJSArrayBuffer() {
ArrayBufferExtension* extension = buffer.extension();
// The embedder-allocated backing store only exists for the off-heap case.
#ifdef V8_HEAP_SANDBOX
uint32_t external_pointer_entry =
buffer.GetBackingStoreRefForDeserialization();
#endif
if (backing_store != nullptr) {
uint32_t ref = SerializeBackingStore(backing_store, byte_length);
buffer.SetBackingStoreRefForSerialization(ref);
@ -477,11 +481,17 @@ void Serializer::ObjectSerializer::SerializeJSArrayBuffer() {
// Ensure deterministic output by setting extension to null during
// serialization.
buffer.set_extension(nullptr);
} else {
buffer.SetBackingStoreRefForSerialization(kNullRefSentinel);
}
SerializeObject();
#ifdef V8_HEAP_SANDBOX
buffer.SetBackingStoreRefForSerialization(external_pointer_entry);
#else
buffer.set_backing_store(serializer_->isolate(), backing_store);
#endif
buffer.set_extension(extension);
}
@ -495,9 +505,16 @@ void Serializer::ObjectSerializer::SerializeExternalString() {
if (serializer_->external_reference_encoder_.TryEncode(resource).To(
&reference)) {
DCHECK(reference.is_from_api());
string.set_uint32_as_resource(serializer_->isolate(), reference.index());
#ifdef V8_HEAP_SANDBOX
uint32_t external_pointer_entry = string.GetResourceRefForDeserialization();
#endif
string.SetResourceRefForSerialization(reference.index());
SerializeObject();
#ifdef V8_HEAP_SANDBOX
string.SetResourceRefForSerialization(external_pointer_entry);
#else
string.set_address_as_resource(serializer_->isolate(), resource);
#endif
} else {
SerializeExternalStringAsSequentialString();
}

View File

@ -451,7 +451,13 @@ int WasmArray::SizeFor(Map map, int length) {
}
void WasmTypeInfo::clear_foreign_address(Isolate* isolate) {
#ifdef V8_HEAP_SANDBOX
// This field is not supposed to be read, so we don't bother allocating an
// external table entry.
WriteField<ExternalPointer_t>(kForeignAddressOffset, kNullExternalPointer);
#else
set_foreign_address(isolate, 0);
#endif
}
#include "src/objects/object-macros-undef.h"

View File

@ -1167,6 +1167,10 @@ UNINITIALIZED_TEST(CustomSnapshotDataBlobOnOrOffHeapTypedArray) {
CompileRun(code);
TestInt32Expectations(expectations);
i::Handle<i::JSArrayBuffer> buffer =
GetBufferFromTypedArray(CompileRun("x"));
// The resulting buffer should be on-heap.
CHECK_NULL(buffer->backing_store());
creator.SetDefaultContext(
context, v8::SerializeInternalFieldsCallback(
SerializeInternalFields, reinterpret_cast<void*>(2016)));