[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:
parent
5082a1d924
commit
32e2584405
2
BUILD.gn
2
BUILD.gn
@ -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",
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
22
src/execution/external-pointer-table.cc
Normal file
22
src/execution/external-pointer-table.cc
Normal 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
|
80
src/execution/external-pointer-table.h
Normal file
80
src/execution/external-pointer-table.h
Normal 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_
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -7,3 +7,6 @@
|
||||
extern class Foreign extends HeapObject {
|
||||
foreign_address: ExternalPointer;
|
||||
}
|
||||
|
||||
extern operator '.foreign_address_ptr' macro LoadForeignForeignAddressPtr(
|
||||
Foreign): RawPtr;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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({});
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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)));
|
||||
|
Loading…
Reference in New Issue
Block a user