From 42ed4928cdd5d26e8808d23665bfb3164ee407f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Gro=C3=9F?= Date: Thu, 2 Dec 2021 10:45:48 +0100 Subject: [PATCH] Use CagedPointers for ArrayBuffer backing stores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL turns references to ArrayBuffer backing stores from JSArrayBuffers, JSTypedArrays, and JSDataViews into CagedPointers when those are enabled. CagedPointers cannot generally represent nullptr, as NULL usually lies outside the cage. As such, nullptr backing stores are replaced with a special empty backing store value, which, in the current implementation, points to the end of the cage, right in front of the trailing guard regions. Due to this, it is no longer correct to compare a backing store pointer against nullptr. Bug: chromium:1218005 Change-Id: I4a6c7a82aabb4debcb6bb2babe4035ba2da8e79f 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/+/3244419 Commit-Queue: Samuel Groß Reviewed-by: Jakob Gruber Reviewed-by: Leszek Swirski Reviewed-by: Igor Sheludko Cr-Commit-Position: refs/heads/main@{#78218} --- include/v8-internal.h | 19 ++++--- src/builtins/builtins-typed-array-gen.cc | 6 +-- src/codegen/code-stub-assembler.cc | 40 ++++++++++---- src/codegen/code-stub-assembler.h | 25 ++++----- src/codegen/external-reference.cc | 10 +++- src/codegen/external-reference.h | 7 +-- src/codegen/machine-type.h | 4 +- src/compiler/access-builder.cc | 10 ++++ .../arm64/instruction-selector-arm64.cc | 8 --- .../backend/x64/instruction-selector-x64.cc | 8 --- src/flags/flag-definitions.h | 6 --- src/objects/backing-store.cc | 5 +- src/objects/backing-store.h | 4 +- src/objects/js-array-buffer-inl.h | 54 ++++++++----------- src/objects/js-array-buffer.cc | 17 ++++-- src/objects/js-array-buffer.h | 8 +-- src/objects/objects-inl.h | 2 - src/objects/objects.h | 4 +- src/security/caged-pointer-inl.h | 23 ++++---- src/security/caged-pointer.h | 10 ++-- src/security/vm-cage.cc | 16 ++++++ src/security/vm-cage.h | 40 ++++++++++++++ src/snapshot/deserializer.cc | 16 +++--- src/snapshot/serializer.cc | 2 +- 24 files changed, 203 insertions(+), 141 deletions(-) diff --git a/include/v8-internal.h b/include/v8-internal.h index dfcb81c35e..f25a3a00a3 100644 --- a/include/v8-internal.h +++ b/include/v8-internal.h @@ -494,6 +494,11 @@ constexpr bool VirtualMemoryCageIsEnabled() { #endif } +// CagedPointers are guaranteed to point into the virtual memory cage. This is +// achieved for example by storing them as offset from the cage base rather +// than as raw pointers. +using CagedPointer_t = Address; + #ifdef V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE #define GB (1ULL << 30) @@ -511,17 +516,11 @@ constexpr size_t kVirtualMemoryCageSize = 1ULL << kVirtualMemoryCageSizeLog2; constexpr size_t kVirtualMemoryCageAlignment = Internals::kPtrComprCageBaseAlignment; -#ifdef V8_CAGED_POINTERS -// CagedPointers are guaranteed to point into the virtual memory cage. This is -// achieved by storing them as offset from the cage base rather than as raw -// pointers. -using CagedPointer_t = Address; - -// For efficiency, the offset is stored shifted to the left, so that -// it is guaranteed that the offset is smaller than the cage size after -// shifting it to the right again. This constant specifies the shift amount. +// Caged pointers are stored inside the heap as offset from the cage base +// shifted to the left. This way, it is guaranteed that the offset is smaller +// than the cage size after shifting it to the right again. This constant +// specifies the shift amount. constexpr uint64_t kCagedPointerShift = 64 - kVirtualMemoryCageSizeLog2; -#endif // Size of the guard regions surrounding the virtual memory cage. This assumes a // worst-case scenario of a 32-bit unsigned index being used to access an array diff --git a/src/builtins/builtins-typed-array-gen.cc b/src/builtins/builtins-typed-array-gen.cc index 72cd0bbe72..00b040f03f 100644 --- a/src/builtins/builtins-typed-array-gen.cc +++ b/src/builtins/builtins-typed-array-gen.cc @@ -65,8 +65,8 @@ TNode TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset, UintPtrConstant(0)); - StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset, - PointerConstant(nullptr)); + StoreCagedPointerToObject(buffer, JSArrayBuffer::kBackingStoreOffset, + EmptyBackingStoreBufferConstant()); StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset, IntPtrConstant(0)); for (int offset = JSArrayBuffer::kHeaderSize; @@ -437,10 +437,10 @@ void TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr( TNode ptr_compr_cage_base = IntPtrSub(full_base, Signed(ChangeUint32ToWord(compressed_base))); // Add JSTypedArray::ExternalPointerCompensationForOnHeapArray() to offset. + // See JSTypedArray::AddExternalPointerCompensationForDeserialization(). DCHECK_EQ( isolate()->cage_base(), JSTypedArray::ExternalPointerCompensationForOnHeapArray(isolate())); - // See JSTypedArray::SetOnHeapDataPtr() for details. offset = Unsigned(IntPtrAdd(offset, ptr_compr_cage_base)); } diff --git a/src/codegen/code-stub-assembler.cc b/src/codegen/code-stub-assembler.cc index 046329d1c1..aad26ca461 100644 --- a/src/codegen/code-stub-assembler.cc +++ b/src/codegen/code-stub-assembler.cc @@ -1539,16 +1539,21 @@ void CodeStubAssembler::BranchIfToBooleanIsTrue(TNode value, } } -#ifdef V8_CAGED_POINTERS - -TNode CodeStubAssembler::LoadCagedPointerFromObject( +TNode CodeStubAssembler::LoadCagedPointerFromObject( TNode object, TNode field_offset) { - return LoadObjectField(object, field_offset); +#ifdef V8_CAGED_POINTERS + return ReinterpretCast( + LoadObjectField(object, field_offset)); +#else + return LoadObjectField(object, field_offset); +#endif // V8_CAGED_POINTERS } void CodeStubAssembler::StoreCagedPointerToObject(TNode object, TNode offset, - TNode pointer) { + TNode pointer) { +#ifdef V8_CAGED_POINTERS + TNode caged_pointer = ReinterpretCast(pointer); #ifdef DEBUG // Verify pointer points into the cage. TNode cage_base_address = @@ -1557,13 +1562,26 @@ void CodeStubAssembler::StoreCagedPointerToObject(TNode object, ExternalConstant(ExternalReference::virtual_memory_cage_end_address()); TNode cage_base = Load(cage_base_address); TNode cage_end = Load(cage_end_address); - CSA_CHECK(this, UintPtrGreaterThanOrEqual(pointer, cage_base)); - CSA_CHECK(this, UintPtrLessThan(pointer, cage_end)); -#endif - StoreObjectFieldNoWriteBarrier(object, offset, pointer); + CSA_DCHECK(this, UintPtrGreaterThanOrEqual(caged_pointer, cage_base)); + CSA_DCHECK(this, UintPtrLessThan(caged_pointer, cage_end)); +#endif // DEBUG + StoreObjectFieldNoWriteBarrier(object, offset, caged_pointer); +#else + StoreObjectFieldNoWriteBarrier(object, offset, pointer); +#endif // V8_CAGED_POINTERS } +TNode CodeStubAssembler::EmptyBackingStoreBufferConstant() { +#ifdef V8_CAGED_POINTERS + // TODO(chromium:1218005) consider creating a LoadCagedPointerConstant() if + // more of these constants are required later on. + TNode empty_backing_store_buffer = + ExternalConstant(ExternalReference::empty_backing_store_buffer()); + return Load(empty_backing_store_buffer); +#else + return ReinterpretCast(IntPtrConstant(0)); #endif // V8_CAGED_POINTERS +} TNode CodeStubAssembler::ChangeUint32ToExternalPointer( TNode value) { @@ -13860,8 +13878,8 @@ void CodeStubAssembler::ThrowIfArrayBufferViewBufferIsDetached( TNode CodeStubAssembler::LoadJSArrayBufferBackingStorePtr( TNode array_buffer) { - return LoadObjectField(array_buffer, - JSArrayBuffer::kBackingStoreOffset); + return LoadCagedPointerFromObject(array_buffer, + JSArrayBuffer::kBackingStoreOffset); } TNode CodeStubAssembler::LoadJSArrayBufferViewBuffer( diff --git a/src/codegen/code-stub-assembler.h b/src/codegen/code-stub-assembler.h index 3acf3aa551..a1e50f661d 100644 --- a/src/codegen/code-stub-assembler.h +++ b/src/codegen/code-stub-assembler.h @@ -1043,32 +1043,29 @@ class V8_EXPORT_PRIVATE CodeStubAssembler // Works only with V8_ENABLE_FORCE_SLOW_PATH compile time flag. Nop otherwise. void GotoIfForceSlowPath(Label* if_true); -#ifdef V8_CAGED_POINTERS - // // Caged pointer related functionality. // // Load a caged pointer value from an object. - TNode LoadCagedPointerFromObject(TNode object, - int offset) { + TNode LoadCagedPointerFromObject(TNode object, + int offset) { return LoadCagedPointerFromObject(object, IntPtrConstant(offset)); } - TNode LoadCagedPointerFromObject(TNode object, - TNode offset); + TNode LoadCagedPointerFromObject(TNode object, + TNode offset); // Stored a caged pointer value to an object. void StoreCagedPointerToObject(TNode object, int offset, - TNode pointer) { + TNode pointer) { StoreCagedPointerToObject(object, IntPtrConstant(offset), pointer); } void StoreCagedPointerToObject(TNode object, - TNode offset, - TNode pointer); + TNode offset, TNode pointer); -#endif // V8_CAGED_POINTERS + TNode EmptyBackingStoreBufferConstant(); // // ExternalPointerT-related functionality. @@ -1148,14 +1145,14 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode LoadJSTypedArrayExternalPointerPtr( TNode holder) { - return LoadObjectField(holder, - JSTypedArray::kExternalPointerOffset); + return LoadCagedPointerFromObject(holder, + JSTypedArray::kExternalPointerOffset); } void StoreJSTypedArrayExternalPointerPtr(TNode holder, TNode value) { - StoreObjectFieldNoWriteBarrier( - holder, JSTypedArray::kExternalPointerOffset, value); + StoreCagedPointerToObject(holder, JSTypedArray::kExternalPointerOffset, + value); } // Load value from current parent frame by given offset in bytes. diff --git a/src/codegen/external-reference.cc b/src/codegen/external-reference.cc index 5d03c539cb..075eaf8c09 100644 --- a/src/codegen/external-reference.cc +++ b/src/codegen/external-reference.cc @@ -226,7 +226,7 @@ ExternalReference ExternalReference::handle_scope_implementer_address( return ExternalReference(isolate->handle_scope_implementer_address()); } -#ifdef V8_VIRTUAL_MEMORY_CAGE +#ifdef V8_CAGED_POINTERS ExternalReference ExternalReference::virtual_memory_cage_base_address() { return ExternalReference(GetProcessWideVirtualMemoryCage()->base_address()); } @@ -234,7 +234,13 @@ ExternalReference ExternalReference::virtual_memory_cage_base_address() { ExternalReference ExternalReference::virtual_memory_cage_end_address() { return ExternalReference(GetProcessWideVirtualMemoryCage()->end_address()); } -#endif + +ExternalReference ExternalReference::empty_backing_store_buffer() { + return ExternalReference(GetProcessWideVirtualMemoryCage() + ->constants() + .empty_backing_store_buffer_address()); +} +#endif // V8_CAGED_POINTERS #ifdef V8_HEAP_SANDBOX ExternalReference ExternalReference::external_pointer_table_address( diff --git a/src/codegen/external-reference.h b/src/codegen/external-reference.h index 45f8edcd27..a0c27d207e 100644 --- a/src/codegen/external-reference.h +++ b/src/codegen/external-reference.h @@ -318,13 +318,14 @@ class StatsCounter; #define EXTERNAL_REFERENCE_LIST_INTL(V) #endif // V8_INTL_SUPPORT -#ifdef V8_VIRTUAL_MEMORY_CAGE +#ifdef V8_CAGED_POINTERS #define EXTERNAL_REFERENCE_LIST_VIRTUAL_MEMORY_CAGE(V) \ V(virtual_memory_cage_base_address, "V8VirtualMemoryCage::base()") \ - V(virtual_memory_cage_end_address, "V8VirtualMemoryCage::end()") + V(virtual_memory_cage_end_address, "V8VirtualMemoryCage::end()") \ + V(empty_backing_store_buffer, "EmptyBackingStoreBuffer()") #else #define EXTERNAL_REFERENCE_LIST_VIRTUAL_MEMORY_CAGE(V) -#endif // V8_VIRTUAL_MEMORY_CAGE +#endif // V8_CAGED_POINTERS #ifdef V8_HEAP_SANDBOX #define EXTERNAL_REFERENCE_LIST_HEAP_SANDBOX(V) \ diff --git a/src/codegen/machine-type.h b/src/codegen/machine-type.h index b3f8ef56b5..981ac9783f 100644 --- a/src/codegen/machine-type.h +++ b/src/codegen/machine-type.h @@ -40,7 +40,9 @@ enum class MachineRepresentation : uint8_t { kTagged, // (uncompressed) Object (Smi or HeapObject) kCompressedPointer, // (compressed) HeapObject kCompressed, // (compressed) Object (Smi or HeapObject) - kCagedPointer, // Guaranteed to point into the virtual memory cage. + // A 64-bit pointer encoded in a way (e.g. as offset) that guarantees it will + // point into the virtual memory cage. + kCagedPointer, // FP and SIMD representations must be last, and in order of increasing size. kFloat32, kFloat64, diff --git a/src/compiler/access-builder.cc b/src/compiler/access-builder.cc index fda0727dd1..f929b98b0c 100644 --- a/src/compiler/access-builder.cc +++ b/src/compiler/access-builder.cc @@ -421,8 +421,13 @@ FieldAccess AccessBuilder::ForJSTypedArrayExternalPointer() { JSTypedArray::kExternalPointerOffset, MaybeHandle(), MaybeHandle(), +#ifdef V8_CAGED_POINTERS + Type::CagedPointer(), + MachineType::CagedPointer(), +#else Type::ExternalPointer(), MachineType::Pointer(), +#endif kNoWriteBarrier, ConstFieldInfo::None(), false, @@ -437,8 +442,13 @@ FieldAccess AccessBuilder::ForJSDataViewDataPointer() { JSDataView::kDataPointerOffset, MaybeHandle(), MaybeHandle(), +#ifdef V8_CAGED_POINTERS + Type::CagedPointer(), + MachineType::CagedPointer(), +#else Type::ExternalPointer(), MachineType::Pointer(), +#endif kNoWriteBarrier, ConstFieldInfo::None(), false, diff --git a/src/compiler/backend/arm64/instruction-selector-arm64.cc b/src/compiler/backend/arm64/instruction-selector-arm64.cc index ad32aee12b..7f34b6594e 100644 --- a/src/compiler/backend/arm64/instruction-selector-arm64.cc +++ b/src/compiler/backend/arm64/instruction-selector-arm64.cc @@ -840,13 +840,9 @@ void InstructionSelector::VisitLoad(Node* node) { immediate_mode = kLoadStoreImm64; break; case MachineRepresentation::kCagedPointer: -#ifdef V8_CAGED_POINTERS opcode = kArm64LdrDecodeCagedPointer; immediate_mode = kLoadStoreImm64; break; -#else - UNREACHABLE(); -#endif case MachineRepresentation::kSimd128: opcode = kArm64LdrQ; immediate_mode = kNoImmediate; @@ -948,13 +944,9 @@ void InstructionSelector::VisitStore(Node* node) { COMPRESS_POINTERS_BOOL ? kLoadStoreImm32 : kLoadStoreImm64; break; case MachineRepresentation::kCagedPointer: -#ifdef V8_CAGED_POINTERS opcode = kArm64StrEncodeCagedPointer; immediate_mode = kLoadStoreImm64; break; -#else - UNREACHABLE(); -#endif case MachineRepresentation::kWord64: opcode = kArm64Str; immediate_mode = kLoadStoreImm64; diff --git a/src/compiler/backend/x64/instruction-selector-x64.cc b/src/compiler/backend/x64/instruction-selector-x64.cc index 3073988656..2c6e4ad671 100644 --- a/src/compiler/backend/x64/instruction-selector-x64.cc +++ b/src/compiler/backend/x64/instruction-selector-x64.cc @@ -298,12 +298,8 @@ ArchOpcode GetLoadOpcode(LoadRepresentation load_rep) { opcode = kX64Movq; break; case MachineRepresentation::kCagedPointer: -#ifdef V8_CAGED_POINTERS opcode = kX64MovqDecodeCagedPointer; break; -#else - UNREACHABLE(); -#endif case MachineRepresentation::kSimd128: opcode = kX64Movdqu; break; @@ -341,11 +337,7 @@ ArchOpcode GetStoreOpcode(StoreRepresentation store_rep) { case MachineRepresentation::kWord64: return kX64Movq; case MachineRepresentation::kCagedPointer: -#ifdef V8_CAGED_POINTERS return kX64MovqEncodeCagedPointer; -#else - UNREACHABLE(); -#endif case MachineRepresentation::kSimd128: return kX64Movdqu; case MachineRepresentation::kNone: // Fall through. diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h index d60956984c..3462e69975 100644 --- a/src/flags/flag-definitions.h +++ b/src/flags/flag-definitions.h @@ -181,12 +181,6 @@ struct MaybeBoolFlag { #define V8_VIRTUAL_MEMORY_CAGE_BOOL false #endif -#ifdef V8_CAGED_POINTERS -#define V8_CAGED_POINTERS_BOOL true -#else -#define V8_CAGED_POINTERS_BOOL false -#endif - // D8's MultiMappedAllocator is only available on Linux, and only if the virtual // memory cage is not enabled. #if V8_OS_LINUX && !V8_VIRTUAL_MEMORY_CAGE_BOOL diff --git a/src/objects/backing-store.cc b/src/objects/backing-store.cc index ca11a89cee..5dca72929a 100644 --- a/src/objects/backing-store.cc +++ b/src/objects/backing-store.cc @@ -194,6 +194,8 @@ BackingStore::BackingStore(void* buffer_start, size_t byte_length, DCHECK_IMPLIES(is_resizable_, free_on_destruct_); DCHECK_IMPLIES(!is_wasm_memory && !is_resizable_, byte_length_ == max_byte_length_); + DCHECK_GE(max_byte_length_, byte_length_); + DCHECK_GE(byte_capacity_, max_byte_length_); } BackingStore::~BackingStore() { @@ -323,10 +325,9 @@ std::unique_ptr BackingStore::Allocate( counters->array_buffer_new_size_failures()->AddSample(mb_length); return {}; } - - DCHECK(IsValidBackingStorePointer(buffer_start)); } + DCHECK(IsValidBackingStorePointer(buffer_start)); auto result = new BackingStore(buffer_start, // start byte_length, // length byte_length, // max length diff --git a/src/objects/backing-store.h b/src/objects/backing-store.h index c5093a2be0..beaa9e8f30 100644 --- a/src/objects/backing-store.h +++ b/src/objects/backing-store.h @@ -99,8 +99,8 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { bool free_on_destruct() const { return free_on_destruct_; } bool IsEmpty() const { - DCHECK_GE(max_byte_length_, byte_length_); - return max_byte_length_ == 0; + DCHECK_GE(byte_capacity_, byte_length_); + return byte_capacity_ == 0; } enum ResizeOrGrowResult { kSuccess, kFailure, kRace }; diff --git a/src/objects/js-array-buffer-inl.h b/src/objects/js-array-buffer-inl.h index 2cda966436..0fd66630ca 100644 --- a/src/objects/js-array-buffer-inl.h +++ b/src/objects/js-array-buffer-inl.h @@ -36,12 +36,14 @@ void JSArrayBuffer::set_byte_length(size_t value) { } DEF_GETTER(JSArrayBuffer, backing_store, void*) { - return reinterpret_cast(ReadField
(kBackingStoreOffset)); + Address value = ReadCagedPointerField(kBackingStoreOffset, cage_base); + return reinterpret_cast(value); } -void JSArrayBuffer::set_backing_store(void* value) { +void JSArrayBuffer::set_backing_store(Isolate* isolate, void* value) { DCHECK(IsValidBackingStorePointer(value)); - WriteField
(kBackingStoreOffset, reinterpret_cast
(value)); + Address addr = reinterpret_cast
(value); + WriteCagedPointerField(kBackingStoreOffset, isolate, addr); } std::shared_ptr JSArrayBuffer::GetBackingStore() const { @@ -249,16 +251,12 @@ void JSTypedArray::set_length(size_t value) { } DEF_GETTER(JSTypedArray, external_pointer, Address) { - return ReadField
(kExternalPointerOffset); -} - -DEF_GETTER(JSTypedArray, external_pointer_raw, Address) { - return ReadField
(kExternalPointerOffset); + return ReadCagedPointerField(kExternalPointerOffset, cage_base); } void JSTypedArray::set_external_pointer(Isolate* isolate, Address value) { DCHECK(IsValidBackingStorePointer(reinterpret_cast(value))); - WriteField
(kExternalPointerOffset, value); + WriteCagedPointerField(kExternalPointerOffset, isolate, value); } Address JSTypedArray::ExternalPointerCompensationForOnHeapArray( @@ -283,19 +281,17 @@ void JSTypedArray::SetExternalBackingStoreRefForSerialization(uint32_t ref) { void JSTypedArray::RemoveExternalPointerCompensationForSerialization( Isolate* isolate) { DCHECK(is_on_heap()); - // TODO(v8:10391): once we have an external table, avoid the need for - // 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. 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(kExternalPointerOffset, offset); -#else - set_external_pointer(isolate, offset); -#endif + WriteField
(kExternalPointerOffset, offset); +} + +void JSTypedArray::AddExternalPointerCompensationForDeserialization( + Isolate* isolate) { + DCHECK(is_on_heap()); + Address pointer = ReadField
(kExternalPointerOffset) + + ExternalPointerCompensationForOnHeapArray(isolate); + set_external_pointer(isolate, pointer); } void* JSTypedArray::DataPtr() { @@ -322,14 +318,6 @@ void JSTypedArray::SetOffHeapDataPtr(Isolate* isolate, void* base, DCHECK_EQ(address, reinterpret_cast
(DataPtr())); } -void JSTypedArray::SetOnHeapDataPtr(Isolate* isolate, HeapObject base, - Address offset) { - set_base_pointer(base); - set_external_pointer( - isolate, offset + ExternalPointerCompensationForOnHeapArray(isolate)); - DCHECK_EQ(base.ptr() + offset, reinterpret_cast
(DataPtr())); -} - bool JSTypedArray::is_on_heap() const { // Keep synced with `is_on_heap(AcquireLoadTag)`. DisallowGarbageCollection no_gc; @@ -378,12 +366,14 @@ MaybeHandle JSTypedArray::Validate(Isolate* isolate, } DEF_GETTER(JSDataView, data_pointer, void*) { - return reinterpret_cast(ReadField
(kDataPointerOffset)); + Address value = ReadCagedPointerField(kDataPointerOffset, cage_base); + return reinterpret_cast(value); } -void JSDataView::set_data_pointer(Isolate* isolate, void* value) { - DCHECK(IsValidBackingStorePointer(value)); - WriteField
(kDataPointerOffset, reinterpret_cast
(value)); +void JSDataView::set_data_pointer(Isolate* isolate, void* ptr) { + DCHECK(IsValidBackingStorePointer(ptr)); + Address value = reinterpret_cast
(ptr); + WriteCagedPointerField(kDataPointerOffset, isolate, value); } } // namespace internal diff --git a/src/objects/js-array-buffer.cc b/src/objects/js-array-buffer.cc index a1b14995ae..dac3c8b563 100644 --- a/src/objects/js-array-buffer.cc +++ b/src/objects/js-array-buffer.cc @@ -56,7 +56,7 @@ void JSArrayBuffer::Setup(SharedFlag shared, ResizableFlag resizable, } set_extension(nullptr); if (!backing_store) { - set_backing_store(nullptr); + set_backing_store(GetIsolate(), EmptyBackingStoreBuffer()); set_byte_length(0); set_max_byte_length(0); } else { @@ -76,7 +76,16 @@ void JSArrayBuffer::Attach(std::shared_ptr backing_store) { !backing_store->is_wasm_memory() && !backing_store->is_resizable(), backing_store->byte_length() == backing_store->max_byte_length()); DCHECK(!was_detached()); - set_backing_store(backing_store->buffer_start()); + DCHECK(IsValidBackingStorePointer(backing_store->buffer_start())); + Isolate* isolate = GetIsolate(); + + if (backing_store->IsEmpty()) { + set_backing_store(isolate, EmptyBackingStoreBuffer()); + } else { + DCHECK_NE(nullptr, backing_store->buffer_start()); + set_backing_store(isolate, backing_store->buffer_start()); + } + if (is_shared() && is_resizable()) { // GSABs need to read their byte_length from the BackingStore. Maintain the // invariant that their byte_length field is always 0. @@ -91,7 +100,7 @@ void JSArrayBuffer::Attach(std::shared_ptr backing_store) { size_t bytes = backing_store->PerIsolateAccountingLength(); extension->set_accounting_length(bytes); extension->set_backing_store(std::move(backing_store)); - GetIsolate()->heap()->AppendArrayBufferExtension(*this, extension); + isolate->heap()->AppendArrayBufferExtension(*this, extension); } void JSArrayBuffer::Detach(bool force_for_wasm_memory) { @@ -120,7 +129,7 @@ void JSArrayBuffer::Detach(bool force_for_wasm_memory) { DCHECK(!is_shared()); DCHECK(!is_asmjs_memory()); - set_backing_store(nullptr); + set_backing_store(isolate, EmptyBackingStoreBuffer()); set_byte_length(0); set_was_detached(true); } diff --git a/src/objects/js-array-buffer.h b/src/objects/js-array-buffer.h index 9c35603487..8e5446c687 100644 --- a/src/objects/js-array-buffer.h +++ b/src/objects/js-array-buffer.h @@ -39,7 +39,7 @@ class JSArrayBuffer // [backing_store]: backing memory for this array // It should not be assumed that this will be nullptr for empty ArrayBuffers. DECL_GETTER(backing_store, void*) - inline void set_backing_store(void* value); + inline void set_backing_store(Isolate* isolate, void* value); // [extension]: extension object used for GC DECL_PRIMITIVE_ACCESSORS(extension, ArrayBufferExtension*) @@ -289,8 +289,6 @@ class JSTypedArray inline void* DataPtr(); inline void SetOffHeapDataPtr(Isolate* isolate, void* base, Address offset); - inline void SetOnHeapDataPtr(Isolate* isolate, HeapObject base, - Address offset); // Whether the buffer's backing store is on-heap or off-heap. inline bool is_on_heap() const; @@ -329,6 +327,9 @@ class JSTypedArray // Subtracts external pointer compensation from the external pointer value. inline void RemoveExternalPointerCompensationForSerialization( Isolate* isolate); + // Adds external pointer compensation to the external pointer value. + inline void AddExternalPointerCompensationForDeserialization( + Isolate* isolate); static inline MaybeHandle Validate(Isolate* isolate, Handle receiver, @@ -365,7 +366,6 @@ class JSTypedArray inline size_t LengthUnchecked() const; DECL_GETTER(external_pointer, Address) - DECL_GETTER(external_pointer_raw, ExternalPointer_t) DECL_SETTER(base_pointer, Object) DECL_RELEASE_SETTER(base_pointer, Object) diff --git a/src/objects/objects-inl.h b/src/objects/objects-inl.h index 4511732ef2..adecaa2a94 100644 --- a/src/objects/objects-inl.h +++ b/src/objects/objects-inl.h @@ -630,7 +630,6 @@ MaybeHandle Object::SetElement(Isolate* isolate, Handle object, return value; } -#ifdef V8_CAGED_POINTERS Address Object::ReadCagedPointerField(size_t offset, PtrComprCageBase cage_base) const { return i::ReadCagedPointerField(field_address(offset), cage_base); @@ -646,7 +645,6 @@ void Object::WriteCagedPointerField(size_t offset, Isolate* isolate, i::WriteCagedPointerField(field_address(offset), PtrComprCageBase(isolate), value); } -#endif // V8_CAGED_POINTERS void Object::InitExternalPointerField(size_t offset, Isolate* isolate) { i::InitExternalPointerField(field_address(offset), isolate); diff --git a/src/objects/objects.h b/src/objects/objects.h index f49f064bf5..52edbc272c 100644 --- a/src/objects/objects.h +++ b/src/objects/objects.h @@ -700,16 +700,14 @@ class Object : public TaggedImpl { } // - // CagedPointer field accessors. + // CagedPointer_t field accessors. // -#ifdef V8_CAGED_POINTERS inline Address ReadCagedPointerField(size_t offset, PtrComprCageBase cage_base) const; inline void WriteCagedPointerField(size_t offset, PtrComprCageBase cage_base, Address value); inline void WriteCagedPointerField(size_t offset, Isolate* isolate, Address value); -#endif // V8_CAGED_POINTERS // // ExternalPointer_t field accessors. diff --git a/src/security/caged-pointer-inl.h b/src/security/caged-pointer-inl.h index 5c0959db25..93cd95a6bf 100644 --- a/src/security/caged-pointer-inl.h +++ b/src/security/caged-pointer-inl.h @@ -12,23 +12,27 @@ namespace v8 { namespace internal { +V8_INLINE Address ReadCagedPointerField(Address field_address, + PtrComprCageBase cage_base) { #ifdef V8_CAGED_POINTERS - -V8_INLINE CagedPointer_t ReadCagedPointerField(Address field_address, - PtrComprCageBase cage_base) { // Caged pointers are currently only used if the sandbox is enabled. DCHECK(V8_HEAP_SANDBOX_BOOL); - Address caged_pointer = base::ReadUnalignedValue
(field_address); + CagedPointer_t caged_pointer = + base::ReadUnalignedValue(field_address); Address offset = caged_pointer >> kCagedPointerShift; Address pointer = cage_base.address() + offset; return pointer; +#else + return base::ReadUnalignedValue
(field_address); +#endif } V8_INLINE void WriteCagedPointerField(Address field_address, PtrComprCageBase cage_base, - CagedPointer_t pointer) { + Address pointer) { +#ifdef V8_CAGED_POINTERS // Caged pointers are currently only used if the sandbox is enabled. DCHECK(V8_HEAP_SANDBOX_BOOL); @@ -36,12 +40,13 @@ V8_INLINE void WriteCagedPointerField(Address field_address, DCHECK(GetProcessWideVirtualMemoryCage()->Contains(pointer)); Address offset = pointer - cage_base.address(); - Address caged_pointer = offset << kCagedPointerShift; - base::WriteUnalignedValue
(field_address, caged_pointer); + CagedPointer_t caged_pointer = offset << kCagedPointerShift; + base::WriteUnalignedValue(field_address, caged_pointer); +#else + base::WriteUnalignedValue
(field_address, pointer); +#endif } -#endif // V8_CAGED_POINTERS - } // namespace internal } // namespace v8 diff --git a/src/security/caged-pointer.h b/src/security/caged-pointer.h index 5b15f63844..30c3b40db8 100644 --- a/src/security/caged-pointer.h +++ b/src/security/caged-pointer.h @@ -10,16 +10,12 @@ namespace v8 { namespace internal { -#ifdef V8_CAGED_POINTERS - -V8_INLINE CagedPointer_t ReadCagedPointerField(Address field_address, - PtrComprCageBase cage_base); +V8_INLINE Address ReadCagedPointerField(Address field_address, + PtrComprCageBase cage_base); V8_INLINE void WriteCagedPointerField(Address field_address, PtrComprCageBase cage_base, - CagedPointer_t value); - -#endif // V8_CAGED_POINTERS + Address value); } // namespace internal } // namespace v8 diff --git a/src/security/vm-cage.cc b/src/security/vm-cage.cc index 38067bf86c..ac2efdb5ca 100644 --- a/src/security/vm-cage.cc +++ b/src/security/vm-cage.cc @@ -11,6 +11,7 @@ #include "src/base/lazy-instance.h" #include "src/base/utils/random-number-generator.h" #include "src/flags/flags.h" +#include "src/security/caged-pointer.h" #include "src/utils/allocation.h" #if defined(V8_OS_WIN) @@ -338,6 +339,8 @@ bool V8VirtualMemoryCage::Initialize(v8::PageAllocator* page_allocator, initialized_ = true; is_fake_cage_ = false; + InitializeConstants(); + return true; } @@ -400,9 +403,19 @@ bool V8VirtualMemoryCage::InitializeAsFakeCage( cage_page_allocator_ = std::make_unique( page_allocator_, base_, size_, reservation_size_); + InitializeConstants(); + return true; } +void V8VirtualMemoryCage::InitializeConstants() { +#ifdef V8_CAGED_POINTERS + // Place the empty backing store buffer at the end of the cage, so that any + // accidental access to it will most likely hit a guard page. + constants_.set_empty_backing_store_buffer(base_ + size_ - 1); +#endif +} + void V8VirtualMemoryCage::TearDown() { if (initialized_) { cage_page_allocator_.reset(); @@ -416,6 +429,9 @@ void V8VirtualMemoryCage::TearDown() { initialized_ = false; is_fake_cage_ = false; page_allocator_ = nullptr; +#ifdef V8_CAGED_POINTERS + constants_.Reset(); +#endif } disabled_ = false; } diff --git a/src/security/vm-cage.h b/src/security/vm-cage.h index 26aa2c8f37..b2cf959284 100644 --- a/src/security/vm-cage.h +++ b/src/security/vm-cage.h @@ -92,6 +92,27 @@ class V8_EXPORT_PRIVATE V8VirtualMemoryCage { return Contains(reinterpret_cast
(ptr)); } +#ifdef V8_CAGED_POINTERS + class CagedPointerConstants final { + public: + Address empty_backing_store_buffer() const { + return empty_backing_store_buffer_; + } + Address empty_backing_store_buffer_address() const { + return reinterpret_cast
(&empty_backing_store_buffer_); + } + void set_empty_backing_store_buffer(Address value) { + empty_backing_store_buffer_ = value; + } + + void Reset() { empty_backing_store_buffer_ = 0; } + + private: + Address empty_backing_store_buffer_ = 0; + }; + const CagedPointerConstants& constants() const { return constants_; } +#endif + private: // The SequentialUnmapperTest calls the private Initialize method to create a // cage without guard regions, which would otherwise consume too much memory. @@ -114,6 +135,10 @@ class V8_EXPORT_PRIVATE V8VirtualMemoryCage { bool InitializeAsFakeCage(v8::PageAllocator* page_allocator, size_t size, size_t size_to_reserve); + // Initialize the caged pointer constants for this cage. Called by the + // Initialize methods above. + void InitializeConstants(); + Address base_ = kNullAddress; Address end_ = kNullAddress; size_t size_ = 0; @@ -132,6 +157,11 @@ class V8_EXPORT_PRIVATE V8VirtualMemoryCage { v8::PageAllocator* page_allocator_ = nullptr; // The allocator to allocate pages inside the cage. std::unique_ptr cage_page_allocator_; + +#ifdef V8_CAGED_POINTERS + // CagedPointer constants inside this cage. + CagedPointerConstants constants_; +#endif }; #endif // V8_VIRTUAL_MEMORY_CAGE_IS_AVAILABLE @@ -151,6 +181,16 @@ V8_INLINE bool IsValidBackingStorePointer(void* ptr) { #endif } +V8_INLINE void* EmptyBackingStoreBuffer() { +#ifdef V8_CAGED_POINTERS + return reinterpret_cast(GetProcessWideVirtualMemoryCage() + ->constants() + .empty_backing_store_buffer()); +#else + return nullptr; +#endif +} + } // namespace internal } // namespace v8 diff --git a/src/snapshot/deserializer.cc b/src/snapshot/deserializer.cc index 3f7f0bc08a..c5dd158f84 100644 --- a/src/snapshot/deserializer.cc +++ b/src/snapshot/deserializer.cc @@ -486,7 +486,7 @@ void Deserializer::PostProcessNewObject(Handle map, } else if (InstanceTypeChecker::IsJSDataView(instance_type)) { Handle data_view = Handle::cast(obj); JSArrayBuffer buffer = JSArrayBuffer::cast(data_view->buffer()); - void* backing_store = nullptr; + void* backing_store = EmptyBackingStoreBuffer(); uint32_t store_index = buffer.GetBackingStoreRefForDeserialization(); if (store_index != kEmptyBackingStoreRefSentinel) { // The backing store of the JSArrayBuffer has not been correctly restored @@ -501,18 +501,15 @@ void Deserializer::PostProcessNewObject(Handle map, Handle typed_array = Handle::cast(obj); // Fixup typed array pointers. if (typed_array->is_on_heap()) { - Address raw_external_pointer = typed_array->external_pointer_raw(); - typed_array->SetOnHeapDataPtr( - main_thread_isolate(), HeapObject::cast(typed_array->base_pointer()), - raw_external_pointer); + typed_array->AddExternalPointerCompensationForDeserialization( + main_thread_isolate()); } else { // Serializer writes backing store ref as a DataPtr() value. uint32_t store_index = typed_array->GetExternalBackingStoreRefForDeserialization(); auto backing_store = backing_stores_[store_index]; - auto start = backing_store - ? reinterpret_cast(backing_store->buffer_start()) - : nullptr; + void* start = backing_store ? backing_store->buffer_start() + : EmptyBackingStoreBuffer(); typed_array->SetOffHeapDataPtr(main_thread_isolate(), start, typed_array->byte_offset()); } @@ -523,7 +520,8 @@ void Deserializer::PostProcessNewObject(Handle map, kEmptyBackingStoreRefSentinel) { new_off_heap_array_buffers_.push_back(buffer); } else { - buffer->set_backing_store(nullptr); + buffer->set_backing_store(main_thread_isolate(), + EmptyBackingStoreBuffer()); } } else if (InstanceTypeChecker::IsBytecodeArray(instance_type)) { // TODO(mythria): Remove these once we store the default values for these diff --git a/src/snapshot/serializer.cc b/src/snapshot/serializer.cc index 478bea4d8c..2ae6fc17b1 100644 --- a/src/snapshot/serializer.cc +++ b/src/snapshot/serializer.cc @@ -545,7 +545,7 @@ void Serializer::ObjectSerializer::SerializeJSArrayBuffer() { SerializeObject(); - buffer->set_backing_store(backing_store); + buffer->set_backing_store(isolate(), backing_store); buffer->set_extension(extension); }