Commit d16de301 authored by Samuel Groß's avatar Samuel Groß Committed by V8 LUCI CQ

Add ObjectSerializer::VisitExternalPointer

This method now handles external pointers in HeapObjects during
serialization by encoding the representation of the external pointer
(sandboxed, raw), the origin (internal, api) and potentially the
external pointer tag. It is currently only used to handle
JSExternalObjects but could, in the future, be extended to handle all
external pointers that need special handling during
serialization/deserialization.

Bug: v8:12700
Change-Id: Ib0747d765ddc632e4ca4ee94521616d0271be0bc
Cq-Include-Trybots: luci.v8.try:v8_linux64_heap_sandbox_dbg_ng,v8_linux_arm64_sim_heap_sandbox_dbg_ng
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3521904Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Samuel Groß <saelo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79496}
parent 09de56b0
...@@ -293,21 +293,23 @@ static_assert((1 << (32 - kExternalPointerIndexShift)) == ...@@ -293,21 +293,23 @@ static_assert((1 << (32 - kExternalPointerIndexShift)) ==
// (the MSB) from the pointer at the same time. // (the MSB) from the pointer at the same time.
// Note: this scheme assumes a 48-bit address space and will likely break if // Note: this scheme assumes a 48-bit address space and will likely break if
// more virtual address bits are used. // more virtual address bits are used.
constexpr uint64_t kExternalPointerTagMask = 0xffff000000000000;
constexpr uint64_t kExternalPointerTagShift = 48;
#define MAKE_TAG(v) (static_cast<uint64_t>(v) << kExternalPointerTagShift)
// clang-format off // clang-format off
enum ExternalPointerTag : uint64_t { enum ExternalPointerTag : uint64_t {
kExternalPointerNullTag = 0b0000000000000000ULL << 48, kExternalPointerNullTag = MAKE_TAG(0b0000000000000000),
kExternalPointerFreeEntryTag = 0b0111111110000000ULL << 48, kExternalPointerFreeEntryTag = MAKE_TAG(0b0111111110000000),
kExternalStringResourceTag = 0b1000000011111111ULL << 48, kExternalStringResourceTag = MAKE_TAG(0b1000000011111111),
kExternalStringResourceDataTag = 0b1000000101111111ULL << 48, kExternalStringResourceDataTag = MAKE_TAG(0b1000000101111111),
kForeignForeignAddressTag = 0b1000000110111111ULL << 48, kForeignForeignAddressTag = MAKE_TAG(0b1000000110111111),
kNativeContextMicrotaskQueueTag = 0b1000000111011111ULL << 48, kNativeContextMicrotaskQueueTag = MAKE_TAG(0b1000000111011111),
kEmbedderDataSlotPayloadTag = 0b1000000111101111ULL << 48, kEmbedderDataSlotPayloadTag = MAKE_TAG(0b1000000111101111),
kCodeEntryPointTag = 0b1000000111110111ULL << 48, kCodeEntryPointTag = MAKE_TAG(0b1000000111110111),
kExternalObjectValueTag = 0b1000000111111011ULL << 48, kExternalObjectValueTag = MAKE_TAG(0b1000000111111011),
}; };
// clang-format on // clang-format on
#undef MAKE_TAG
constexpr uint64_t kExternalPointerTagMask = 0xffff000000000000;
// Converts encoded external pointer to address. // Converts encoded external pointer to address.
V8_EXPORT Address DecodeExternalPointerImpl(const Isolate* isolate, V8_EXPORT Address DecodeExternalPointerImpl(const Isolate* isolate,
......
...@@ -543,17 +543,6 @@ void JSExternalObject::set_value(Isolate* isolate, void* value) { ...@@ -543,17 +543,6 @@ void JSExternalObject::set_value(Isolate* isolate, void* value) {
kExternalObjectValueTag); kExternalObjectValueTag);
} }
uint32_t JSExternalObject::GetValueRefForDeserialization() {
ExternalPointer_t encoded_address =
ReadField<ExternalPointer_t>(kValueOffset);
return static_cast<uint32_t>(encoded_address);
}
void JSExternalObject::SetValueRefForSerialization(uint32_t ref) {
WriteField<ExternalPointer_t>(kValueOffset,
static_cast<ExternalPointer_t>(ref));
}
DEF_GETTER(JSGlobalObject, native_context_unchecked, Object) { DEF_GETTER(JSGlobalObject, native_context_unchecked, Object) {
return TaggedField<Object, kNativeContextOffset>::Relaxed_Load(cage_base, return TaggedField<Object, kNativeContextOffset>::Relaxed_Load(cage_base,
*this); *this);
......
...@@ -902,10 +902,6 @@ class JSExternalObject ...@@ -902,10 +902,6 @@ class JSExternalObject
inline void set_value(Isolate* isolate, void* value); inline void set_value(Isolate* isolate, void* value);
// Used in the serializer/deserializer.
inline uint32_t GetValueRefForDeserialization();
inline void SetValueRefForSerialization(uint32_t ref);
static constexpr int kEndOfTaggedFieldsOffset = JSObject::kHeaderSize; static constexpr int kEndOfTaggedFieldsOffset = JSObject::kHeaderSize;
class BodyDescriptor; class BodyDescriptor;
......
...@@ -458,24 +458,6 @@ void Deserializer<IsolateT>::PostProcessNewJSReceiver( ...@@ -458,24 +458,6 @@ void Deserializer<IsolateT>::PostProcessNewJSReceiver(
HeapObject::RequiredAlignment(map))); HeapObject::RequiredAlignment(map)));
} }
template <typename IsolateT>
void Deserializer<IsolateT>::PostProcessJSExternalObject(
JSExternalObject object, Isolate* isolate) {
DisallowGarbageCollection no_gc;
uint32_t index = object.GetValueRefForDeserialization();
Address address;
if (main_thread_isolate()->api_external_references()) {
DCHECK_WITH_MSG(index < num_api_references_,
"too few external references provided through the API");
address = static_cast<Address>(
main_thread_isolate()->api_external_references()[index]);
} else {
address = reinterpret_cast<Address>(NoExternalReferencesCallback);
}
object.AllocateExternalPointerEntries(isolate);
object.set_value(isolate, reinterpret_cast<void*>(address));
}
template <typename IsolateT> template <typename IsolateT>
void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map, void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map,
Handle<HeapObject> obj, Handle<HeapObject> obj,
...@@ -571,9 +553,6 @@ void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map, ...@@ -571,9 +553,6 @@ void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map,
} else if (InstanceTypeChecker::IsExternalString(instance_type)) { } else if (InstanceTypeChecker::IsExternalString(instance_type)) {
PostProcessExternalString(ExternalString::cast(raw_obj), PostProcessExternalString(ExternalString::cast(raw_obj),
main_thread_isolate()); main_thread_isolate());
} else if (InstanceTypeChecker::IsJSExternalObject(instance_type)) {
PostProcessJSExternalObject(JSExternalObject::cast(raw_obj),
main_thread_isolate());
} else if (InstanceTypeChecker::IsJSReceiver(instance_type)) { } else if (InstanceTypeChecker::IsJSReceiver(instance_type)) {
return PostProcessNewJSReceiver(raw_map, Handle<JSReceiver>::cast(obj), return PostProcessNewJSReceiver(raw_map, Handle<JSReceiver>::cast(obj),
JSReceiver::cast(raw_obj), instance_type, JSReceiver::cast(raw_obj), instance_type,
...@@ -1041,8 +1020,8 @@ int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data, ...@@ -1041,8 +1020,8 @@ int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data,
Address address = ReadExternalReferenceCase(); Address address = ReadExternalReferenceCase();
if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL &&
data == kSandboxedExternalReference) { data == kSandboxedExternalReference) {
return WriteExternalPointer(slot_accessor.slot(), address, ExternalPointerTag tag = ReadExternalPointerTag();
kForeignForeignAddressTag); return WriteExternalPointer(slot_accessor.slot(), address, tag);
} else { } else {
DCHECK(!V8_SANDBOXED_EXTERNAL_POINTERS_BOOL); DCHECK(!V8_SANDBOXED_EXTERNAL_POINTERS_BOOL);
return WriteAddress(slot_accessor.slot(), address); return WriteAddress(slot_accessor.slot(), address);
...@@ -1205,8 +1184,8 @@ int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data, ...@@ -1205,8 +1184,8 @@ int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data,
} }
if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL &&
data == kSandboxedApiReference) { data == kSandboxedApiReference) {
return WriteExternalPointer(slot_accessor.slot(), address, ExternalPointerTag tag = ReadExternalPointerTag();
kForeignForeignAddressTag); return WriteExternalPointer(slot_accessor.slot(), address, tag);
} else { } else {
DCHECK(!V8_SANDBOXED_EXTERNAL_POINTERS_BOOL); DCHECK(!V8_SANDBOXED_EXTERNAL_POINTERS_BOOL);
return WriteAddress(slot_accessor.slot(), address); return WriteAddress(slot_accessor.slot(), address);
...@@ -1298,6 +1277,13 @@ Address Deserializer<IsolateT>::ReadExternalReferenceCase() { ...@@ -1298,6 +1277,13 @@ Address Deserializer<IsolateT>::ReadExternalReferenceCase() {
reference_id); reference_id);
} }
template <typename IsolateT>
ExternalPointerTag Deserializer<IsolateT>::ReadExternalPointerTag() {
uint64_t shifted_tag = static_cast<uint64_t>(source_.GetInt());
return static_cast<ExternalPointerTag>(shifted_tag
<< kExternalPointerTagShift);
}
template <typename IsolateT> template <typename IsolateT>
HeapObject Deserializer<IsolateT>::Allocate(AllocationType allocation, int size, HeapObject Deserializer<IsolateT>::Allocate(AllocationType allocation, int size,
AllocationAlignment alignment) { AllocationAlignment alignment) {
......
...@@ -184,6 +184,9 @@ class Deserializer : public SerializerDeserializer { ...@@ -184,6 +184,9 @@ class Deserializer : public SerializerDeserializer {
// A helper function for ReadData for reading external references. // A helper function for ReadData for reading external references.
inline Address ReadExternalReferenceCase(); inline Address ReadExternalReferenceCase();
// A helper function for reading external pointer tags.
ExternalPointerTag ReadExternalPointerTag();
Handle<HeapObject> ReadObject(SnapshotSpace space_number); Handle<HeapObject> ReadObject(SnapshotSpace space_number);
Handle<HeapObject> ReadMetaMap(); Handle<HeapObject> ReadMetaMap();
...@@ -198,7 +201,6 @@ class Deserializer : public SerializerDeserializer { ...@@ -198,7 +201,6 @@ class Deserializer : public SerializerDeserializer {
void PostProcessNewJSReceiver(Map map, Handle<JSReceiver> obj, void PostProcessNewJSReceiver(Map map, Handle<JSReceiver> obj,
JSReceiver raw_obj, InstanceType instance_type, JSReceiver raw_obj, InstanceType instance_type,
SnapshotSpace space); SnapshotSpace space);
void PostProcessJSExternalObject(JSExternalObject object, Isolate* isolate);
HeapObject Allocate(AllocationType allocation, int size, HeapObject Allocate(AllocationType allocation, int size,
AllocationAlignment alignment); AllocationAlignment alignment);
......
...@@ -549,26 +549,6 @@ void Serializer::ObjectSerializer::SerializeJSArrayBuffer() { ...@@ -549,26 +549,6 @@ void Serializer::ObjectSerializer::SerializeJSArrayBuffer() {
buffer->set_extension(extension); buffer->set_extension(extension);
} }
void Serializer::ObjectSerializer::SerializeJSExternalObject() {
Handle<JSExternalObject> obj = Handle<JSExternalObject>::cast(object_);
Address value = reinterpret_cast<Address>(obj->value());
ExternalReferenceEncoder::Value reference =
serializer_->EncodeExternalReference(value);
DCHECK(reference.is_from_api());
#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
// TODO(saelo) unify this code with the non-sandbox version maybe?
STATIC_ASSERT(sizeof(ExternalPointer_t) == sizeof(uint32_t));
uint32_t external_pointer_entry = obj->GetValueRefForDeserialization();
#endif
obj->SetValueRefForSerialization(reference.index());
SerializeObject();
#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
obj->SetValueRefForSerialization(external_pointer_entry);
#else
obj->set_value(isolate(), reinterpret_cast<void*>(value));
#endif
}
void Serializer::ObjectSerializer::SerializeExternalString() { void Serializer::ObjectSerializer::SerializeExternalString() {
// For external strings with known resources, we replace the resource field // For external strings with known resources, we replace the resource field
// with the encoded external reference, which we restore upon deserialize. // with the encoded external reference, which we restore upon deserialize.
...@@ -712,9 +692,6 @@ void Serializer::ObjectSerializer::Serialize() { ...@@ -712,9 +692,6 @@ void Serializer::ObjectSerializer::Serialize() {
if (object_->IsExternalString(cage_base)) { if (object_->IsExternalString(cage_base)) {
SerializeExternalString(); SerializeExternalString();
return; return;
} else if (object_->IsJSExternalObject(cage_base)) {
SerializeJSExternalObject();
return;
} else if (!ReadOnlyHeap::Contains(*object_)) { } else if (!ReadOnlyHeap::Contains(*object_)) {
// Only clear padding for strings outside the read-only heap. Read-only heap // Only clear padding for strings outside the read-only heap. Read-only heap
// should have been cleared elsewhere. // should have been cleared elsewhere.
...@@ -942,10 +919,10 @@ void Serializer::ObjectSerializer::VisitCodePointer(HeapObject host, ...@@ -942,10 +919,10 @@ void Serializer::ObjectSerializer::VisitCodePointer(HeapObject host,
bytes_processed_so_far_ += kTaggedSize; bytes_processed_so_far_ += kTaggedSize;
} }
void Serializer::ObjectSerializer::OutputExternalReference(Address target, void Serializer::ObjectSerializer::OutputExternalReference(
int target_size, Address target, int target_size, bool sandboxify, ExternalPointerTag tag) {
bool sandboxify) {
DCHECK_LE(target_size, sizeof(target)); // Must fit in Address. DCHECK_LE(target_size, sizeof(target)); // Must fit in Address.
DCHECK_IMPLIES(sandboxify, tag != kExternalPointerNullTag);
ExternalReferenceEncoder::Value encoded_reference; ExternalReferenceEncoder::Value encoded_reference;
bool encoded_successfully; bool encoded_successfully;
...@@ -983,12 +960,17 @@ void Serializer::ObjectSerializer::OutputExternalReference(Address target, ...@@ -983,12 +960,17 @@ void Serializer::ObjectSerializer::OutputExternalReference(Address target,
} }
sink_->PutInt(encoded_reference.index(), "reference index"); sink_->PutInt(encoded_reference.index(), "reference index");
} }
if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && sandboxify) {
sink_->PutInt(static_cast<uint32_t>(tag >> kExternalPointerTagShift),
"external pointer tag");
}
} }
void Serializer::ObjectSerializer::VisitExternalReference(Foreign host, void Serializer::ObjectSerializer::VisitExternalReference(Foreign host,
Address* p) { Address* p) {
// "Sandboxify" external reference. // "Sandboxify" external reference.
OutputExternalReference(host.foreign_address(), kSystemPointerSize, true); OutputExternalReference(host.foreign_address(), kSystemPointerSize, true,
kForeignForeignAddressTag);
bytes_processed_so_far_ += kExternalPointerSize; bytes_processed_so_far_ += kExternalPointerSize;
} }
...@@ -1042,7 +1024,33 @@ void Serializer::ObjectSerializer::VisitExternalReference(Code host, ...@@ -1042,7 +1024,33 @@ void Serializer::ObjectSerializer::VisitExternalReference(Code host,
DCHECK_IMPLIES(serializer_->EncodeExternalReference(target).is_from_api(), DCHECK_IMPLIES(serializer_->EncodeExternalReference(target).is_from_api(),
!rinfo->IsCodedSpecially()); !rinfo->IsCodedSpecially());
// Don't "sandboxify" external references embedded in the code. // Don't "sandboxify" external references embedded in the code.
OutputExternalReference(target, rinfo->target_address_size(), false); OutputExternalReference(target, rinfo->target_address_size(), false,
kExternalPointerNullTag);
}
void Serializer::ObjectSerializer::VisitExternalPointer(HeapObject host,
ExternalPointer_t ptr) {
// TODO(v8:12700) handle other external references here as well. This should
// allow removing some of the other Visit* methods, should unify the sandbox
// vs no-sandbox implementation, and should allow removing various
// XYZForSerialization methods throughout the codebase.
if (host.IsJSExternalObject()) {
#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
// TODO(saelo) maybe add a helper method for this conversion if also needed
// in other places? This might require a ExternalPointerTable::Get variant
// that drops the pointer tag completely.
uint32_t index = ptr >> kExternalPointerIndexShift;
Address value =
isolate()->external_pointer_table().Get(index, kExternalObjectValueTag);
#else
Address value = ptr;
#endif
// TODO(v8:12700) should we specify here whether we expect the references to
// be internal or external (or either)?
OutputExternalReference(value, kSystemPointerSize, true,
kExternalObjectValueTag);
bytes_processed_so_far_ += kExternalPointerSize;
}
} }
void Serializer::ObjectSerializer::VisitInternalReference(Code host, void Serializer::ObjectSerializer::VisitInternalReference(Code host,
......
...@@ -450,6 +450,7 @@ class Serializer::ObjectSerializer : public ObjectVisitor { ...@@ -450,6 +450,7 @@ class Serializer::ObjectSerializer : public ObjectVisitor {
void VisitEmbeddedPointer(Code host, RelocInfo* target) override; void VisitEmbeddedPointer(Code host, RelocInfo* target) override;
void VisitExternalReference(Foreign host, Address* p) override; void VisitExternalReference(Foreign host, Address* p) override;
void VisitExternalReference(Code host, RelocInfo* rinfo) override; void VisitExternalReference(Code host, RelocInfo* rinfo) override;
void VisitExternalPointer(HeapObject host, ExternalPointer_t ptr) override;
void VisitInternalReference(Code host, RelocInfo* rinfo) override; void VisitInternalReference(Code host, RelocInfo* rinfo) override;
void VisitCodeTarget(Code host, RelocInfo* target) override; void VisitCodeTarget(Code host, RelocInfo* target) override;
void VisitRuntimeEntry(Code host, RelocInfo* reloc) override; void VisitRuntimeEntry(Code host, RelocInfo* reloc) override;
...@@ -465,14 +466,13 @@ class Serializer::ObjectSerializer : public ObjectVisitor { ...@@ -465,14 +466,13 @@ class Serializer::ObjectSerializer : public ObjectVisitor {
// This function outputs or skips the raw data between the last pointer and // This function outputs or skips the raw data between the last pointer and
// up to the current position. // up to the current position.
void SerializeContent(Map map, int size); void SerializeContent(Map map, int size);
void OutputExternalReference(Address target, int target_size, void OutputExternalReference(Address target, int target_size, bool sandboxify,
bool sandboxify); ExternalPointerTag tag);
void OutputRawData(Address up_to); void OutputRawData(Address up_to);
void SerializeCode(Map map, int size); void SerializeCode(Map map, int size);
uint32_t SerializeBackingStore(void* backing_store, int32_t byte_length); uint32_t SerializeBackingStore(void* backing_store, int32_t byte_length);
void SerializeJSTypedArray(); void SerializeJSTypedArray();
void SerializeJSArrayBuffer(); void SerializeJSArrayBuffer();
void SerializeJSExternalObject();
void SerializeExternalString(); void SerializeExternalString();
void SerializeExternalStringAsSequentialString(); void SerializeExternalStringAsSequentialString();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment