Commit 961c21b1 authored by Samuel Groß's avatar Samuel Groß Committed by Commit Bot

[sandbox] Access external pointers in embedder slots via bottlenecks

Bug: v8:10391
Change-Id: I55d4d33820c83711d3ea3c6a2f3a20a36707fe36
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2151354Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67566}
parent db8f64b8
......@@ -110,6 +110,16 @@ constexpr bool PointerCompressionIsEnabled() {
return kApiTaggedSize != kApiSystemPointerSize;
}
constexpr bool HeapSandboxIsEnabled() {
#ifdef V8_HEAP_SANDBOX
return true;
#else
return false;
#endif
}
using ExternalPointer_t = Address;
#ifdef V8_31BIT_SMIS_ON_64BIT_ARCH
using PlatformSmiTagging = SmiTagging<kApiInt32Size>;
#else
......@@ -329,11 +339,28 @@ class Internals {
#endif
}
V8_INLINE static internal::Address ReadExternalPointerField(
internal::Isolate* isolate, internal::Address heap_object_ptr,
int offset) {
#ifdef V8_COMPRESS_POINTERS
internal::Address value = ReadRawField<Address>(heap_object_ptr, offset);
// We currently have to treat zero as nullptr in embedder slots.
if (value) value = DecodeExternalPointer(isolate, value);
return value;
#else
return ReadRawField<internal::Address>(heap_object_ptr, offset);
#endif
}
#ifdef V8_COMPRESS_POINTERS
// See v8:7703 or src/ptr-compr.* for details about pointer compression.
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);
......@@ -344,6 +371,15 @@ class Internals {
internal::Address root = GetRootFromOnHeapAddress(heap_object_ptr);
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
};
......
......@@ -9571,7 +9571,8 @@ class V8_EXPORT V8 {
V8_INLINE static bool Initialize() {
const int kBuildConfiguration =
(internal::PointerCompressionIsEnabled() ? kPointerCompression : 0) |
(internal::SmiValuesAre31Bits() ? k31BitSmis : 0);
(internal::SmiValuesAre31Bits() ? k31BitSmis : 0) |
(internal::HeapSandboxIsEnabled() ? kHeapSandbox : 0);
return Initialize(kBuildConfiguration);
}
......@@ -9710,6 +9711,7 @@ class V8_EXPORT V8 {
enum BuildConfigurationFeatures {
kPointerCompression = 1 << 0,
k31BitSmis = 1 << 1,
kHeapSandbox = 1 << 2,
};
/**
......@@ -11351,7 +11353,10 @@ void* Object::GetAlignedPointerFromInternalField(int index) {
instance_type == I::kJSApiObjectType ||
instance_type == I::kJSSpecialApiObjectType)) {
int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index);
return I::ReadRawField<void*>(obj, offset);
internal::Isolate* isolate =
internal::IsolateFromNeverReadOnlySpaceObject(obj);
A value = I::ReadExternalPointerField(isolate, obj, offset);
return reinterpret_cast<void*>(value);
}
#endif
return SlowGetAlignedPointerFromInternalField(index);
......@@ -11983,7 +11988,10 @@ void* Context::GetAlignedPointerFromEmbedderData(int index) {
I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset);
int value_offset =
I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index);
return I::ReadRawField<void*>(embedder_data, value_offset);
internal::Isolate* isolate = internal::IsolateFromNeverReadOnlySpaceObject(
*reinterpret_cast<A*>(this));
return reinterpret_cast<void*>(
I::ReadExternalPointerField(isolate, embedder_data, value_offset));
#else
return SlowGetAlignedPointerFromEmbedderData(index);
#endif
......
......@@ -1314,21 +1314,25 @@ void Context::SetEmbedderData(int index, v8::Local<Value> value) {
void* Context::SlowGetAlignedPointerFromEmbedderData(int index) {
const char* location = "v8::Context::GetAlignedPointerFromEmbedderData()";
HandleScope handle_scope(GetIsolate());
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
i::HandleScope handle_scope(isolate);
i::Handle<i::EmbedderDataArray> data =
EmbedderDataFor(this, index, false, location);
if (data.is_null()) return nullptr;
void* result;
Utils::ApiCheck(i::EmbedderDataSlot(*data, index).ToAlignedPointer(&result),
location, "Pointer is not aligned");
Utils::ApiCheck(
i::EmbedderDataSlot(*data, index).ToAlignedPointer(isolate, &result),
location, "Pointer is not aligned");
return result;
}
void Context::SetAlignedPointerInEmbedderData(int index, void* value) {
const char* location = "v8::Context::SetAlignedPointerInEmbedderData()";
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
i::Handle<i::EmbedderDataArray> data =
EmbedderDataFor(this, index, true, location);
bool ok = i::EmbedderDataSlot(*data, index).store_aligned_pointer(value);
bool ok =
i::EmbedderDataSlot(*data, index).store_aligned_pointer(isolate, value);
Utils::ApiCheck(ok, location, "Pointer is not aligned");
DCHECK_EQ(value, GetAlignedPointerFromEmbedderData(index));
}
......@@ -5614,7 +5618,7 @@ void* v8::Object::SlowGetAlignedPointerFromInternalField(int index) {
if (!InternalFieldOK(obj, index, location)) return nullptr;
void* result;
Utils::ApiCheck(i::EmbedderDataSlot(i::JSObject::cast(*obj), index)
.ToAlignedPointer(&result),
.ToAlignedPointer(obj->GetIsolate(), &result),
location, "Unaligned pointer");
return result;
}
......@@ -5624,7 +5628,7 @@ void v8::Object::SetAlignedPointerInInternalField(int index, void* value) {
const char* location = "v8::Object::SetAlignedPointerInInternalField()";
if (!InternalFieldOK(obj, index, location)) return;
Utils::ApiCheck(i::EmbedderDataSlot(i::JSObject::cast(*obj), index)
.store_aligned_pointer(value),
.store_aligned_pointer(obj->GetIsolate(), value),
location, "Unaligned pointer");
DCHECK_EQ(value, GetAlignedPointerFromInternalField(index));
}
......@@ -5643,9 +5647,9 @@ void v8::Object::SetAlignedPointerInInternalFields(int argc, int indices[],
return;
}
void* value = values[i];
Utils::ApiCheck(
i::EmbedderDataSlot(js_obj, index).store_aligned_pointer(value),
location, "Unaligned pointer");
Utils::ApiCheck(i::EmbedderDataSlot(js_obj, index)
.store_aligned_pointer(obj->GetIsolate(), value),
location, "Unaligned pointer");
DCHECK_EQ(value, GetAlignedPointerFromInternalField(index));
}
}
......@@ -5686,6 +5690,15 @@ bool v8::V8::Initialize(const int build_config) {
kEmbedderSmiValueSize, internal::kSmiValueSize);
}
const bool kEmbedderHeapSandbox = (build_config & kHeapSandbox) != 0;
if (kEmbedderHeapSandbox != V8_HEAP_SANDBOX_BOOL) {
FATAL(
"Embedder-vs-V8 build configuration mismatch. On embedder side "
"heap sandbox is %s while on V8 side it's %s.",
kEmbedderHeapSandbox ? "ENABLED" : "DISABLED",
V8_HEAP_SANDBOX_BOOL ? "ENABLED" : "DISABLED");
}
i::V8::Initialize();
return true;
}
......
......@@ -921,9 +921,10 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
Internals::kJSObjectHeaderSize +
(Internals::kEmbedderDataSlotSize * wrapper_object_index);
FieldAccess access(kTaggedBase, offset, MaybeHandle<Name>(),
MaybeHandle<Map>(), Type::Any(), MachineType::Pointer(),
WriteBarrierKind::kNoWriteBarrier);
FieldAccess access(
kTaggedBase, offset, MaybeHandle<Name>(), MaybeHandle<Map>(),
V8_HEAP_SANDBOX_BOOL ? Type::SandboxedExternalPointer() : Type::Any(),
MachineType::Pointer(), WriteBarrierKind::kNoWriteBarrier);
TNode<RawPtrT> load = AddNode<RawPtrT>(graph()->NewNode(
simplified()->LoadField(access), node, effect(), control()));
return load;
......
......@@ -458,12 +458,13 @@ void PrintSloppyArgumentElements(std::ostream& os, ElementsKind kind,
}
}
void PrintEmbedderData(std::ostream& os, EmbedderDataSlot slot) {
void PrintEmbedderData(const Isolate* isolate, std::ostream& os,
EmbedderDataSlot slot) {
DisallowHeapAllocation no_gc;
Object value = slot.load_tagged();
os << Brief(value);
void* raw_pointer;
if (slot.ToAlignedPointer(&raw_pointer)) {
if (slot.ToAlignedPointer(isolate, &raw_pointer)) {
os << ", aligned pointer: " << raw_pointer;
}
}
......@@ -567,10 +568,11 @@ static void JSObjectPrintBody(std::ostream& os,
}
int embedder_fields = obj.GetEmbedderFieldCount();
if (embedder_fields > 0) {
const Isolate* isolate = GetIsolateForPtrCompr(obj);
os << " - embedder fields = {";
for (int i = 0; i < embedder_fields; i++) {
os << "\n ";
PrintEmbedderData(os, EmbedderDataSlot(obj, i));
PrintEmbedderData(isolate, os, EmbedderDataSlot(obj, i));
}
os << "\n }\n";
}
......@@ -772,13 +774,14 @@ void PrintWeakArrayElements(std::ostream& os, T* array) {
} // namespace
void EmbedderDataArray::EmbedderDataArrayPrint(std::ostream& os) {
const Isolate* isolate = GetIsolateForPtrCompr(*this);
PrintHeader(os, "EmbedderDataArray");
os << "\n - length: " << length();
EmbedderDataSlot start(*this, 0);
EmbedderDataSlot end(*this, length());
for (EmbedderDataSlot slot = start; slot < end; ++slot) {
os << "\n ";
PrintEmbedderData(os, slot);
PrintEmbedderData(isolate, os, slot);
}
os << "\n";
}
......
......@@ -381,10 +381,11 @@ namespace {
void ExtractInternalFields(JSObject jsobject, void** embedder_fields, int len) {
int field_count = jsobject.GetEmbedderFieldCount();
const Isolate* isolate = GetIsolateForPtrCompr(jsobject);
for (int i = 0; i < len; ++i) {
if (field_count == i) break;
void* pointer;
if (EmbedderDataSlot(jsobject, i).ToAlignedPointer(&pointer)) {
if (EmbedderDataSlot(jsobject, i).ToAlignedPointer(isolate, &pointer)) {
embedder_fields[i] = pointer;
}
}
......
......@@ -92,8 +92,11 @@ void LocalEmbedderHeapTracer::ProcessingScope::TracePossibleWrapper(
void* pointer0;
void* pointer1;
if (EmbedderDataSlot(js_object, 0).ToAlignedPointer(&pointer0) && pointer0 &&
EmbedderDataSlot(js_object, 1).ToAlignedPointer(&pointer1)) {
if (EmbedderDataSlot(js_object, 0)
.ToAlignedPointer(tracer_->isolate_, &pointer0) &&
pointer0 &&
EmbedderDataSlot(js_object, 1)
.ToAlignedPointer(tracer_->isolate_, &pointer1)) {
wrapper_cache_.push_back({pointer0, pointer1});
}
FlushWrapperCacheIfFull();
......
......@@ -67,7 +67,8 @@ void EmbedderDataSlot::store_tagged(JSObject object, int embedder_field_index,
#endif
}
bool EmbedderDataSlot::ToAlignedPointer(void** out_pointer) const {
bool EmbedderDataSlot::ToAlignedPointer(const Isolate* isolate,
void** out_pointer) const {
// We don't care about atomicity of access here because embedder slots
// 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
......@@ -78,6 +79,8 @@ bool EmbedderDataSlot::ToAlignedPointer(void** out_pointer) const {
// 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);
#else
Address raw_value = *location();
#endif
......@@ -85,15 +88,18 @@ bool EmbedderDataSlot::ToAlignedPointer(void** out_pointer) const {
return HAS_SMI_TAG(raw_value);
}
bool EmbedderDataSlot::store_aligned_pointer(void* ptr) {
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);
return true;
}
EmbedderDataSlot::RawData EmbedderDataSlot::load_raw(
const DisallowHeapAllocation& no_gc) const {
Isolate* isolate, const DisallowHeapAllocation& no_gc) const {
// We don't care about atomicity of access here because embedder slots
// are accessed this way only by serializer from the main thread when
// GC is not active (concurrent marker may still look at the tagged part
......@@ -103,14 +109,20 @@ 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.
return base::ReadUnalignedValue<Address>(address());
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;
#else
return *location();
#endif
}
void EmbedderDataSlot::store_raw(EmbedderDataSlot::RawData data,
void EmbedderDataSlot::store_raw(Isolate* isolate,
EmbedderDataSlot::RawData data,
const DisallowHeapAllocation& no_gc) {
// We currently have to treat zero as nullptr in embedder slots.
if (data) data = EncodeExternalPointer(isolate, data);
gc_safe_store(data);
}
......
......@@ -66,14 +66,18 @@ class EmbedderDataSlot
// the pointer-like value. Note, that some Smis could still look like an
// aligned pointers.
// Returns true on success.
V8_INLINE bool ToAlignedPointer(void** out_result) const;
V8_INLINE bool ToAlignedPointer(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(void* ptr);
V8_INLINE V8_WARN_UNUSED_RESULT bool store_aligned_pointer(Isolate* isolate,
void* ptr);
V8_INLINE RawData load_raw(const DisallowHeapAllocation& no_gc) const;
V8_INLINE void store_raw(RawData data, const DisallowHeapAllocation& no_gc);
V8_INLINE RawData load_raw(Isolate* isolate,
const DisallowHeapAllocation& no_gc) const;
V8_INLINE void store_raw(Isolate* isolate, RawData data,
const DisallowHeapAllocation& no_gc);
private:
// Stores given value to the embedder data slot in a concurrent-marker
......
......@@ -216,7 +216,8 @@ bool ContextSerializer::SerializeJSObjectWithEmbedderFields(Object obj) {
// onto the result.
for (int i = 0; i < embedder_fields_count; i++) {
EmbedderDataSlot embedder_data_slot(js_obj, i);
original_embedder_values.emplace_back(embedder_data_slot.load_raw(no_gc));
original_embedder_values.emplace_back(
embedder_data_slot.load_raw(isolate(), no_gc));
Object object = embedder_data_slot.load_tagged();
if (object.IsHeapObject()) {
DCHECK(IsValidHeapObject(isolate()->heap(), HeapObject::cast(object)));
......@@ -243,7 +244,7 @@ bool ContextSerializer::SerializeJSObjectWithEmbedderFields(Object obj) {
// with embedder callbacks.
for (int i = 0; i < embedder_fields_count; i++) {
if (!DataIsEmpty(serialized_data[i])) {
EmbedderDataSlot(js_obj, i).store_raw(kNullAddress, no_gc);
EmbedderDataSlot(js_obj, i).store_raw(isolate(), kNullAddress, no_gc);
}
}
......@@ -262,7 +263,8 @@ bool ContextSerializer::SerializeJSObjectWithEmbedderFields(Object obj) {
StartupData data = serialized_data[i];
if (DataIsEmpty(data)) continue;
// Restore original values from cleared fields.
EmbedderDataSlot(js_obj, i).store_raw(original_embedder_values[i], no_gc);
EmbedderDataSlot(js_obj, i).store_raw(isolate(),
original_embedder_values[i], no_gc);
embedder_fields_sink_.Put(kNewObject + static_cast<int>(reference.space()),
"embedder field holder");
embedder_fields_sink_.PutInt(reference.chunk_index(), "BackRefChunkIndex");
......
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