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

[sandbox][x64] Switch to AND-based type checks

This change turns the previously used XOR-based type checks for external
pointers into AND-based type checks. With those, the type tag is ORed
into the top bits of an external pointer when it is written, and the
type check performed on every load is done by ANDing the value with the
inverted tag. This will later allow type checking and masking off the GC
marking bits of external pointers in a single operation.

Bug: v8:10391
Change-Id: I89f2b22588b3f7467c79c7916c11f25cd9bcc82d
Cq-Include-Trybots: luci.v8.try:v8_linux64_heap_sandbox_dbg_ng
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2850639
Commit-Queue: Samuel Groß <saelo@google.com>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74464}
parent 8da583ee
......@@ -128,23 +128,28 @@ constexpr bool HeapSandboxIsEnabled() {
using ExternalPointer_t = Address;
// If the heap sandbox is enabled, these tag values will be XORed with the
// If the heap sandbox is enabled, these tag values will be ORed with the
// external pointers in the external pointer table to prevent use of pointers of
// the wrong type.
enum ExternalPointerTag : Address {
kExternalPointerNullTag = static_cast<Address>(0ULL),
kArrayBufferBackingStoreTag = static_cast<Address>(1ULL << 48),
kTypedArrayExternalPointerTag = static_cast<Address>(2ULL << 48),
kDataViewDataPointerTag = static_cast<Address>(3ULL << 48),
kExternalStringResourceTag = static_cast<Address>(4ULL << 48),
kExternalStringResourceDataTag = static_cast<Address>(5ULL << 48),
kForeignForeignAddressTag = static_cast<Address>(6ULL << 48),
kNativeContextMicrotaskQueueTag = static_cast<Address>(7ULL << 48),
// TODO(v8:10391, saelo): Currently has to be zero so that raw zero values are
// also nullptr
kEmbedderDataSlotPayloadTag = static_cast<Address>(0ULL << 48),
// the wrong type. When a pointer is loaded, it is ANDed with the inverse of the
// expected type's tag. The tags are constructed in a way that guarantees that a
// failed type check will result in one or more of the top bits of the pointer
// to be set, rendering the pointer inacessible. This construction allows
// performing the type check and removing GC marking bits from the pointer at
// the same time.
enum ExternalPointerTag : uint64_t {
kExternalPointerNullTag = 0x0000000000000000,
kArrayBufferBackingStoreTag = 0x00ff000000000000, // 0b000000011111111
kTypedArrayExternalPointerTag = 0x017f000000000000, // 0b000000101111111
kDataViewDataPointerTag = 0x01bf000000000000, // 0b000000110111111
kExternalStringResourceTag = 0x01df000000000000, // 0b000000111011111
kExternalStringResourceDataTag = 0x01ef000000000000, // 0b000000111101111
kForeignForeignAddressTag = 0x01f7000000000000, // 0b000000111110111
kNativeContextMicrotaskQueueTag = 0x01fb000000000000, // 0b000000111111011
kEmbedderDataSlotPayloadTag = 0x01fd000000000000, // 0b000000111111101
};
constexpr uint64_t kExternalPointerTagMask = 0xffff000000000000;
#ifdef V8_31BIT_SMIS_ON_64BIT_ARCH
using PlatformSmiTagging = SmiTagging<kApiInt32Size>;
#else
......
......@@ -1575,8 +1575,8 @@ TNode<RawPtrT> CodeStubAssembler::LoadExternalPointerFromObject(
TNode<UintPtrT> entry = Load<UintPtrT>(table, table_offset);
if (external_pointer_tag != 0) {
TNode<UintPtrT> tag = UintPtrConstant(external_pointer_tag);
entry = UncheckedCast<UintPtrT>(WordXor(entry, tag));
TNode<UintPtrT> tag = UintPtrConstant(~external_pointer_tag);
entry = UncheckedCast<UintPtrT>(WordAnd(entry, tag));
}
return UncheckedCast<RawPtrT>(UncheckedCast<WordT>(entry));
#else
......@@ -1604,7 +1604,7 @@ void CodeStubAssembler::StoreExternalPointerToObject(
TNode<UintPtrT> value = UncheckedCast<UintPtrT>(pointer);
if (external_pointer_tag != 0) {
TNode<UintPtrT> tag = UintPtrConstant(external_pointer_tag);
value = UncheckedCast<UintPtrT>(WordXor(pointer, tag));
value = UncheckedCast<UintPtrT>(WordOr(pointer, tag));
}
StoreNoWriteBarrier(MachineType::PointerRepresentation(), table, table_offset,
value);
......
......@@ -390,8 +390,8 @@ void TurboAssembler::LoadExternalPointerField(
movl(destination, field_operand);
movq(destination, Operand(scratch, destination, times_8, 0));
if (tag != 0) {
movq(scratch, Immediate64(tag));
xorq(destination, scratch);
movq(scratch, Immediate64(~tag));
andq(destination, scratch);
}
#else
movq(destination, field_operand);
......
......@@ -18,7 +18,7 @@ V8_INLINE Address DecodeExternalPointer(const Isolate* isolate,
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
#ifdef V8_HEAP_SANDBOX
uint32_t index = static_cast<uint32_t>(encoded_pointer);
return isolate->external_pointer_table().get(index) ^ tag;
return isolate->external_pointer_table().get(index) & ~tag;
#else
return encoded_pointer;
#endif
......@@ -40,9 +40,10 @@ V8_INLINE void InitExternalPointerField(Address field_address,
V8_INLINE void InitExternalPointerField(Address field_address, Isolate* isolate,
Address value, ExternalPointerTag tag) {
#ifdef V8_HEAP_SANDBOX
DCHECK_EQ(value & kExternalPointerTagMask, 0);
ExternalPointer_t index = isolate->external_pointer_table().allocate();
isolate->external_pointer_table().set(static_cast<uint32_t>(index),
value ^ tag);
value | tag);
static_assert(kExternalPointerSize == kSystemPointerSize,
"Review the code below, once kExternalPointerSize is 4-byte "
"the address of the field will always be aligned");
......@@ -82,11 +83,12 @@ V8_INLINE void WriteExternalPointerField(Address field_address,
static_assert(kExternalPointerSize == kSystemPointerSize,
"Review the code below, once kExternalPointerSize is 4-byte "
"the address of the field will always be aligned");
DCHECK_EQ(value & kExternalPointerTagMask, 0);
ExternalPointer_t index =
base::ReadUnalignedValue<ExternalPointer_t>(field_address);
isolate->external_pointer_table().set(static_cast<uint32_t>(index),
value ^ tag);
value | tag);
#else
// Pointer compression causes types larger than kTaggedSize to be unaligned.
constexpr bool v8_pointer_compression_unaligned =
......
......@@ -760,7 +760,7 @@ FieldAccess AccessBuilder::ForExternalStringResourceData() {
ConstFieldInfo::None(),
false,
#ifdef V8_HEAP_SANDBOX
kExternalStringResourceTag,
kExternalStringResourceDataTag,
#endif
};
return access;
......
......@@ -367,8 +367,8 @@ Node* MemoryLowering::DecodeExternalPointer(
Node* decoded_ptr =
__ Load(MachineType::Pointer(), table, __ ChangeUint32ToUint64(offset));
if (external_pointer_tag != 0) {
Node* tag = __ IntPtrConstant(external_pointer_tag);
decoded_ptr = __ WordXor(decoded_ptr, tag);
Node* tag = __ IntPtrConstant(~external_pointer_tag);
decoded_ptr = __ WordAnd(decoded_ptr, tag);
}
return decoded_ptr;
#else
......
......@@ -3134,8 +3134,8 @@ Node* WasmGraphBuilder::BuildLoadCallTargetFromExportedFunctionData(
Internals::kExternalPointerTableBufferOffset);
Node* offset = gasm_->Int32Mul(index, gasm_->Int32Constant(8));
Node* decoded_ptr = gasm_->Load(MachineType::Pointer(), table, offset);
Node* tag = gasm_->IntPtrConstant(kForeignForeignAddressTag);
return gasm_->WordXor(decoded_ptr, tag);
Node* tag = gasm_->IntPtrConstant(~kForeignForeignAddressTag);
return gasm_->WordAnd(decoded_ptr, tag);
#else
return gasm_->LoadFromObject(
MachineType::Pointer(), function_data,
......
......@@ -27,7 +27,7 @@ class V8_EXPORT_PRIVATE ExternalPointerTable {
buffer_[kNullExternalPointer] = kNullAddress;
}
~ExternalPointerTable() { ::free(buffer_); }
~ExternalPointerTable() { base::Free(buffer_); }
Address get(uint32_t index) const {
CHECK_LT(index, length_);
......@@ -49,13 +49,6 @@ class V8_EXPORT_PRIVATE ExternalPointerTable {
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
......
......@@ -90,8 +90,8 @@ bool EmbedderDataSlot::ToAlignedPointer(Isolate* isolate,
Address raw_value;
#ifdef V8_HEAP_SANDBOX
uint32_t index = base::Memory<uint32_t>(address() + kRawPayloadOffset);
raw_value = isolate->external_pointer_table().get(index) ^
kEmbedderDataSlotPayloadTag;
raw_value = isolate->external_pointer_table().get(index) &
~kEmbedderDataSlotPayloadTag;
#else
if (COMPRESS_POINTERS_BOOL) {
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
......@@ -113,8 +113,8 @@ bool EmbedderDataSlot::ToAlignedPointerSafe(Isolate* isolate,
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) ^
kEmbedderDataSlotPayloadTag;
raw_value = isolate->external_pointer_table().get(index) &
~kEmbedderDataSlotPayloadTag;
*out_pointer = reinterpret_cast<void*>(raw_value);
return true;
}
......@@ -136,7 +136,7 @@ bool EmbedderDataSlot::store_aligned_pointer(Isolate* isolate, void* ptr) {
ObjectSlot(address() + kRawPayloadOffset).Relaxed_Load();
uint32_t index = static_cast<uint32_t>(index_as_object.ptr());
isolate->external_pointer_table().set(index,
value ^ kEmbedderDataSlotPayloadTag);
value | kEmbedderDataSlotPayloadTag);
return true;
}
#endif
......
......@@ -464,6 +464,9 @@ void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
DCHECK(InstanceTypeChecker::IsStrongDescriptorArray(instance_type));
Handle<DescriptorArray> descriptors = Handle<DescriptorArray>::cast(obj);
new_descriptor_arrays_.push_back(descriptors);
} else if (InstanceTypeChecker::IsNativeContext(instance_type)) {
Handle<NativeContext> context = Handle<NativeContext>::cast(obj);
context->AllocateExternalPointerEntries(isolate());
}
// Check alignment.
......
......@@ -1453,6 +1453,9 @@ static void TestExternalPointerWrapping() {
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
int* ptr = new int;
expected_ptr = ptr;
v8::Local<v8::Value> data = v8::External::New(isolate, expected_ptr);
v8::Local<v8::Object> obj = v8::Object::New(isolate);
......@@ -1468,6 +1471,8 @@ static void TestExternalPointerWrapping() {
"}\n"
"foo(), true")
->BooleanValue(isolate));
delete ptr;
}
......@@ -3049,7 +3054,9 @@ THREADED_TEST(InternalFieldsAlignedPointers) {
int stack_allocated[100];
CheckAlignedPointerInInternalField(obj, stack_allocated);
void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1));
// The aligned pointer must have the top bits be zero on 64-bit machines (at
// least if the heap sandbox is enabled).
void* huge = reinterpret_cast<void*>(0x0000fffffffffffe);
CheckAlignedPointerInInternalField(obj, huge);
v8::Global<v8::Object> persistent(isolate, obj);
......@@ -3125,7 +3132,9 @@ THREADED_TEST(EmbedderDataAlignedPointers) {
CheckAlignedPointerInEmbedderData(&env, 2, stack_allocated);
CHECK_EQ(3, (*env)->GetNumberOfEmbedderDataFields());
void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1));
// The aligned pointer must have the top bits be zero on 64-bit machines (at
// least if the heap sandbox is enabled).
void* huge = reinterpret_cast<void*>(0x0000fffffffffffe);
CheckAlignedPointerInEmbedderData(&env, 3, huge);
CHECK_EQ(4, (*env)->GetNumberOfEmbedderDataFields());
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