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)) ==
// (the MSB) from the pointer at the same time.
// Note: this scheme assumes a 48-bit address space and will likely break if
// 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
enum ExternalPointerTag : uint64_t {
kExternalPointerNullTag = 0b0000000000000000ULL << 48,
kExternalPointerFreeEntryTag = 0b0111111110000000ULL << 48,
kExternalStringResourceTag = 0b1000000011111111ULL << 48,
kExternalStringResourceDataTag = 0b1000000101111111ULL << 48,
kForeignForeignAddressTag = 0b1000000110111111ULL << 48,
kNativeContextMicrotaskQueueTag = 0b1000000111011111ULL << 48,
kEmbedderDataSlotPayloadTag = 0b1000000111101111ULL << 48,
kCodeEntryPointTag = 0b1000000111110111ULL << 48,
kExternalObjectValueTag = 0b1000000111111011ULL << 48,
kExternalPointerNullTag = MAKE_TAG(0b0000000000000000),
kExternalPointerFreeEntryTag = MAKE_TAG(0b0111111110000000),
kExternalStringResourceTag = MAKE_TAG(0b1000000011111111),
kExternalStringResourceDataTag = MAKE_TAG(0b1000000101111111),
kForeignForeignAddressTag = MAKE_TAG(0b1000000110111111),
kNativeContextMicrotaskQueueTag = MAKE_TAG(0b1000000111011111),
kEmbedderDataSlotPayloadTag = MAKE_TAG(0b1000000111101111),
kCodeEntryPointTag = MAKE_TAG(0b1000000111110111),
kExternalObjectValueTag = MAKE_TAG(0b1000000111111011),
};
// clang-format on
constexpr uint64_t kExternalPointerTagMask = 0xffff000000000000;
#undef MAKE_TAG
// Converts encoded external pointer to address.
V8_EXPORT Address DecodeExternalPointerImpl(const Isolate* isolate,
......
......@@ -543,17 +543,6 @@ void JSExternalObject::set_value(Isolate* isolate, void* value) {
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) {
return TaggedField<Object, kNativeContextOffset>::Relaxed_Load(cage_base,
*this);
......
......@@ -902,10 +902,6 @@ class JSExternalObject
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;
class BodyDescriptor;
......
......@@ -458,24 +458,6 @@ void Deserializer<IsolateT>::PostProcessNewJSReceiver(
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>
void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map,
Handle<HeapObject> obj,
......@@ -571,9 +553,6 @@ void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map,
} else if (InstanceTypeChecker::IsExternalString(instance_type)) {
PostProcessExternalString(ExternalString::cast(raw_obj),
main_thread_isolate());
} else if (InstanceTypeChecker::IsJSExternalObject(instance_type)) {
PostProcessJSExternalObject(JSExternalObject::cast(raw_obj),
main_thread_isolate());
} else if (InstanceTypeChecker::IsJSReceiver(instance_type)) {
return PostProcessNewJSReceiver(raw_map, Handle<JSReceiver>::cast(obj),
JSReceiver::cast(raw_obj), instance_type,
......@@ -1041,8 +1020,8 @@ int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data,
Address address = ReadExternalReferenceCase();
if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL &&
data == kSandboxedExternalReference) {
return WriteExternalPointer(slot_accessor.slot(), address,
kForeignForeignAddressTag);
ExternalPointerTag tag = ReadExternalPointerTag();
return WriteExternalPointer(slot_accessor.slot(), address, tag);
} else {
DCHECK(!V8_SANDBOXED_EXTERNAL_POINTERS_BOOL);
return WriteAddress(slot_accessor.slot(), address);
......@@ -1205,8 +1184,8 @@ int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data,
}
if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL &&
data == kSandboxedApiReference) {
return WriteExternalPointer(slot_accessor.slot(), address,
kForeignForeignAddressTag);
ExternalPointerTag tag = ReadExternalPointerTag();
return WriteExternalPointer(slot_accessor.slot(), address, tag);
} else {
DCHECK(!V8_SANDBOXED_EXTERNAL_POINTERS_BOOL);
return WriteAddress(slot_accessor.slot(), address);
......@@ -1298,6 +1277,13 @@ Address Deserializer<IsolateT>::ReadExternalReferenceCase() {
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>
HeapObject Deserializer<IsolateT>::Allocate(AllocationType allocation, int size,
AllocationAlignment alignment) {
......
......@@ -184,6 +184,9 @@ class Deserializer : public SerializerDeserializer {
// A helper function for ReadData for reading external references.
inline Address ReadExternalReferenceCase();
// A helper function for reading external pointer tags.
ExternalPointerTag ReadExternalPointerTag();
Handle<HeapObject> ReadObject(SnapshotSpace space_number);
Handle<HeapObject> ReadMetaMap();
......@@ -198,7 +201,6 @@ class Deserializer : public SerializerDeserializer {
void PostProcessNewJSReceiver(Map map, Handle<JSReceiver> obj,
JSReceiver raw_obj, InstanceType instance_type,
SnapshotSpace space);
void PostProcessJSExternalObject(JSExternalObject object, Isolate* isolate);
HeapObject Allocate(AllocationType allocation, int size,
AllocationAlignment alignment);
......
......@@ -549,26 +549,6 @@ void Serializer::ObjectSerializer::SerializeJSArrayBuffer() {
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() {
// For external strings with known resources, we replace the resource field
// with the encoded external reference, which we restore upon deserialize.
......@@ -712,9 +692,6 @@ void Serializer::ObjectSerializer::Serialize() {
if (object_->IsExternalString(cage_base)) {
SerializeExternalString();
return;
} else if (object_->IsJSExternalObject(cage_base)) {
SerializeJSExternalObject();
return;
} else if (!ReadOnlyHeap::Contains(*object_)) {
// Only clear padding for strings outside the read-only heap. Read-only heap
// should have been cleared elsewhere.
......@@ -942,10 +919,10 @@ void Serializer::ObjectSerializer::VisitCodePointer(HeapObject host,
bytes_processed_so_far_ += kTaggedSize;
}
void Serializer::ObjectSerializer::OutputExternalReference(Address target,
int target_size,
bool sandboxify) {
void Serializer::ObjectSerializer::OutputExternalReference(
Address target, int target_size, bool sandboxify, ExternalPointerTag tag) {
DCHECK_LE(target_size, sizeof(target)); // Must fit in Address.
DCHECK_IMPLIES(sandboxify, tag != kExternalPointerNullTag);
ExternalReferenceEncoder::Value encoded_reference;
bool encoded_successfully;
......@@ -983,12 +960,17 @@ void Serializer::ObjectSerializer::OutputExternalReference(Address target,
}
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,
Address* p) {
// "Sandboxify" external reference.
OutputExternalReference(host.foreign_address(), kSystemPointerSize, true);
OutputExternalReference(host.foreign_address(), kSystemPointerSize, true,
kForeignForeignAddressTag);
bytes_processed_so_far_ += kExternalPointerSize;
}
......@@ -1042,7 +1024,33 @@ void Serializer::ObjectSerializer::VisitExternalReference(Code host,
DCHECK_IMPLIES(serializer_->EncodeExternalReference(target).is_from_api(),
!rinfo->IsCodedSpecially());
// 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,
......
......@@ -450,6 +450,7 @@ class Serializer::ObjectSerializer : public ObjectVisitor {
void VisitEmbeddedPointer(Code host, RelocInfo* target) override;
void VisitExternalReference(Foreign host, Address* p) override;
void VisitExternalReference(Code host, RelocInfo* rinfo) override;
void VisitExternalPointer(HeapObject host, ExternalPointer_t ptr) override;
void VisitInternalReference(Code host, RelocInfo* rinfo) override;
void VisitCodeTarget(Code host, RelocInfo* target) override;
void VisitRuntimeEntry(Code host, RelocInfo* reloc) override;
......@@ -465,14 +466,13 @@ class Serializer::ObjectSerializer : public ObjectVisitor {
// This function outputs or skips the raw data between the last pointer and
// up to the current position.
void SerializeContent(Map map, int size);
void OutputExternalReference(Address target, int target_size,
bool sandboxify);
void OutputExternalReference(Address target, int target_size, bool sandboxify,
ExternalPointerTag tag);
void OutputRawData(Address up_to);
void SerializeCode(Map map, int size);
uint32_t SerializeBackingStore(void* backing_store, int32_t byte_length);
void SerializeJSTypedArray();
void SerializeJSArrayBuffer();
void SerializeJSExternalObject();
void SerializeExternalString();
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