diff options
author | Anna Henningsen <anna.henningsen@mongodb.com> | 2022-12-21 13:00:47 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-21 12:00:47 +0000 |
commit | b66ae39c1e34d958c0109f90af84e9210f813341 (patch) | |
tree | ece8674a2dff89cb0d25254adc57c8ceaed93971 /deps/v8/src | |
parent | 51246139e75da78d7621e162731f7618356f97dc (diff) | |
download | node-new-b66ae39c1e34d958c0109f90af84e9210f813341.tar.gz |
deps: V8: backport 8ca9f77d0f7c
This fixes a crash when loading snapshots that contain empty
ArrayBuffer instances:
```js
const X = [];
X.push(new ArrayBuffer());
v8.startupSnapshot.setDeserializeMainFunction(() => {
for (let i = 0; i < 1000000; i++) { // trigger GC
X.push(new ArrayBuffer());
}
})
```
Original commit message:
[sandbox] Sandboxify JSArrayBuffer::extension external pointer
Bug: chromium:1335043
Change-Id: Id8e6883fc652b144f6380ff09b1c18e777e041c2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3706626
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Samuel Groß <saelo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84544}
Refs: https://github.com/v8/v8/commit/8ca9f77d0f7c2d5ec5ef8255b9689f5ac1c547a3
PR-URL: https://github.com/nodejs/node/pull/45871
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'deps/v8/src')
-rw-r--r-- | deps/v8/src/builtins/builtins-typed-array-gen.cc | 7 | ||||
-rw-r--r-- | deps/v8/src/compiler/code-assembler.h | 3 | ||||
-rw-r--r-- | deps/v8/src/objects/js-array-buffer-inl.h | 89 | ||||
-rw-r--r-- | deps/v8/src/objects/js-array-buffer.h | 15 | ||||
-rw-r--r-- | deps/v8/src/objects/js-array-buffer.tq | 2 | ||||
-rw-r--r-- | deps/v8/src/objects/objects-body-descriptors-inl.h | 2 | ||||
-rw-r--r-- | deps/v8/src/sandbox/external-pointer-table.h | 2 | ||||
-rw-r--r-- | deps/v8/src/snapshot/deserializer.cc | 1 |
8 files changed, 65 insertions, 56 deletions
diff --git a/deps/v8/src/builtins/builtins-typed-array-gen.cc b/deps/v8/src/builtins/builtins-typed-array-gen.cc index 47cd82846e..b770607f4e 100644 --- a/deps/v8/src/builtins/builtins-typed-array-gen.cc +++ b/deps/v8/src/builtins/builtins-typed-array-gen.cc @@ -70,8 +70,15 @@ TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( UintPtrConstant(0)); StoreSandboxedPointerToObject(buffer, JSArrayBuffer::kBackingStoreOffset, EmptyBackingStoreBufferConstant()); +#ifdef V8_COMPRESS_POINTERS + // When pointer compression is enabled, the extension slot contains a + // (lazily-initialized) external pointer handle. + StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset, + ExternalPointerHandleNullConstant()); +#else StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset, IntPtrConstant(0)); +#endif for (int offset = JSArrayBuffer::kHeaderSize; offset < JSArrayBuffer::kSizeWithEmbedderFields; offset += kTaggedSize) { // TODO(v8:10391, saelo): Handle external pointers in EmbedderDataSlot diff --git a/deps/v8/src/compiler/code-assembler.h b/deps/v8/src/compiler/code-assembler.h index ff09fa6403..06cf450431 100644 --- a/deps/v8/src/compiler/code-assembler.h +++ b/deps/v8/src/compiler/code-assembler.h @@ -553,6 +553,9 @@ class V8_EXPORT_PRIVATE CodeAssembler { TNode<BoolT> BoolConstant(bool value) { return value ? Int32TrueConstant() : Int32FalseConstant(); } + TNode<ExternalPointerHandleT> ExternalPointerHandleNullConstant() { + return ReinterpretCast<ExternalPointerHandleT>(Uint32Constant(0)); + } bool IsMapOffsetConstant(Node* node); diff --git a/deps/v8/src/objects/js-array-buffer-inl.h b/deps/v8/src/objects/js-array-buffer-inl.h index eef35d936e..77a348fcf3 100644 --- a/deps/v8/src/objects/js-array-buffer-inl.h +++ b/deps/v8/src/objects/js-array-buffer-inl.h @@ -88,65 +88,60 @@ void JSArrayBuffer::SetBackingStoreRefForSerialization(uint32_t ref) { ArrayBufferExtension* JSArrayBuffer::extension() const { #if V8_COMPRESS_POINTERS - // With pointer compression the extension-field might not be - // pointer-aligned. However on ARM64 this field needs to be aligned to - // perform atomic operations on it. Therefore we split the pointer into two - // 32-bit words that we update atomically. We don't have an ABA problem here - // since there can never be an Attach() after Detach() (transitions only - // from NULL --> some ptr --> NULL). - - // Synchronize with publishing release store of non-null extension - uint32_t lo = base::AsAtomic32::Acquire_Load(extension_lo()); - if (lo & kUninitializedTagMask) return nullptr; - - // Synchronize with release store of null extension - uint32_t hi = base::AsAtomic32::Acquire_Load(extension_hi()); - uint32_t verify_lo = base::AsAtomic32::Relaxed_Load(extension_lo()); - if (lo != verify_lo) return nullptr; - - uintptr_t address = static_cast<uintptr_t>(lo); - address |= static_cast<uintptr_t>(hi) << 32; - return reinterpret_cast<ArrayBufferExtension*>(address); + // We need Acquire semantics here when loading the entry, see below. + // Consider adding respective external pointer accessors if non-relaxed + // ordering semantics are ever needed in other places as well. + Isolate* isolate = GetIsolateFromWritableObject(*this); + ExternalPointerHandle handle = + base::AsAtomic32::Acquire_Load(extension_handle_location()); + return reinterpret_cast<ArrayBufferExtension*>( + isolate->external_pointer_table().Get(handle, kArrayBufferExtensionTag)); #else - return base::AsAtomicPointer::Acquire_Load(extension_location()); -#endif + return base::AsAtomicPointer::Acquire_Load(extension_location()); +#endif // V8_COMPRESS_POINTERS } void JSArrayBuffer::set_extension(ArrayBufferExtension* extension) { #if V8_COMPRESS_POINTERS - if (extension != nullptr) { - uintptr_t address = reinterpret_cast<uintptr_t>(extension); - base::AsAtomic32::Relaxed_Store(extension_hi(), - static_cast<uint32_t>(address >> 32)); - base::AsAtomic32::Release_Store(extension_lo(), - static_cast<uint32_t>(address)); - } else { - base::AsAtomic32::Relaxed_Store(extension_lo(), - 0 | kUninitializedTagMask); - base::AsAtomic32::Release_Store(extension_hi(), 0); - } + if (extension != nullptr) { + Isolate* isolate = GetIsolateFromWritableObject(*this); + ExternalPointerTable& table = isolate->external_pointer_table(); + + // The external pointer handle for the extension is initialized lazily and + // so has to be zero here since, once set, the extension field can only be + // cleared, but not changed. + DCHECK_EQ(0, base::AsAtomic32::Relaxed_Load(extension_handle_location())); + + // We need Release semantics here, see above. + ExternalPointerHandle handle = table.AllocateAndInitializeEntry( + isolate, reinterpret_cast<Address>(extension), + kArrayBufferExtensionTag); + base::AsAtomic32::Release_Store(extension_handle_location(), handle); + } else { + // This special handling of nullptr is required as it is used to initialize + // the slot, but is also beneficial when an ArrayBuffer is detached as it + // allows the external pointer table entry to be reclaimed while the + // ArrayBuffer is still alive. + base::AsAtomic32::Release_Store(extension_handle_location(), + kNullExternalPointerHandle); + } #else - base::AsAtomicPointer::Release_Store(extension_location(), extension); -#endif - WriteBarrier::Marking(*this, extension); -} - -ArrayBufferExtension** JSArrayBuffer::extension_location() const { - Address location = field_address(kExtensionOffset); - return reinterpret_cast<ArrayBufferExtension**>(location); + base::AsAtomicPointer::Release_Store(extension_location(), extension); +#endif // V8_COMPRESS_POINTERS + WriteBarrier::Marking(*this, extension); } #if V8_COMPRESS_POINTERS -uint32_t* JSArrayBuffer::extension_lo() const { +ExternalPointerHandle* JSArrayBuffer::extension_handle_location() const { Address location = field_address(kExtensionOffset); - return reinterpret_cast<uint32_t*>(location); + return reinterpret_cast<ExternalPointerHandle*>(location); } - -uint32_t* JSArrayBuffer::extension_hi() const { - Address location = field_address(kExtensionOffset) + sizeof(uint32_t); - return reinterpret_cast<uint32_t*>(location); +#else +ArrayBufferExtension** JSArrayBuffer::extension_location() const { + Address location = field_address(kExtensionOffset); + return reinterpret_cast<ArrayBufferExtension**>(location); } -#endif +#endif // V8_COMPRESS_POINTERS void JSArrayBuffer::clear_padding() { if (FIELD_SIZE(kOptionalPaddingOffset) != 0) { diff --git a/deps/v8/src/objects/js-array-buffer.h b/deps/v8/src/objects/js-array-buffer.h index 780f95781d..6c0784540d 100644 --- a/deps/v8/src/objects/js-array-buffer.h +++ b/deps/v8/src/objects/js-array-buffer.h @@ -168,14 +168,15 @@ class JSArrayBuffer private: void DetachInternal(bool force_for_wasm_memory, Isolate* isolate); - inline ArrayBufferExtension** extension_location() const; - #if V8_COMPRESS_POINTERS - static const int kUninitializedTagMask = 1; - - inline uint32_t* extension_lo() const; - inline uint32_t* extension_hi() const; -#endif + // When pointer compression is enabled, the pointer to the extension is + // stored in the external pointer table and the object itself only contains a + // 32-bit external pointer handles. This simplifies alignment requirements + // and is also necessary for the sandbox. + inline ExternalPointerHandle* extension_handle_location() const; +#else + inline ArrayBufferExtension** extension_location() const; +#endif // V8_COMPRESS_POINTERS TQ_OBJECT_CONSTRUCTORS(JSArrayBuffer) }; diff --git a/deps/v8/src/objects/js-array-buffer.tq b/deps/v8/src/objects/js-array-buffer.tq index 10d238d245..34e8bccce3 100644 --- a/deps/v8/src/objects/js-array-buffer.tq +++ b/deps/v8/src/objects/js-array-buffer.tq @@ -19,7 +19,7 @@ extern class JSArrayBuffer extends JSObjectWithEmbedderSlots { raw_max_byte_length: uintptr; // A SandboxedPtr if the sandbox is enabled backing_store: RawPtr; - extension: RawPtr; + extension: ExternalPointer; bit_field: JSArrayBufferFlags; // Pads header size to be a multiple of kTaggedSize. @if(TAGGED_SIZE_8_BYTES) optional_padding: uint32; diff --git a/deps/v8/src/objects/objects-body-descriptors-inl.h b/deps/v8/src/objects/objects-body-descriptors-inl.h index fab78f4923..7aac0e6a10 100644 --- a/deps/v8/src/objects/objects-body-descriptors-inl.h +++ b/deps/v8/src/objects/objects-body-descriptors-inl.h @@ -387,6 +387,8 @@ class JSArrayBuffer::BodyDescriptor final : public BodyDescriptorBase { // JSArrayBuffer instances contain raw data that the GC does not know about. IteratePointers(obj, kPropertiesOrHashOffset, kEndOfTaggedFieldsOffset, v); IterateJSObjectBodyImpl(map, obj, kHeaderSize, object_size, v); + v->VisitExternalPointer(map, obj.RawExternalPointerField(kExtensionOffset), + kArrayBufferExtensionTag); } static inline int SizeOf(Map map, HeapObject object) { diff --git a/deps/v8/src/sandbox/external-pointer-table.h b/deps/v8/src/sandbox/external-pointer-table.h index eb76c35e54..d76bd77185 100644 --- a/deps/v8/src/sandbox/external-pointer-table.h +++ b/deps/v8/src/sandbox/external-pointer-table.h @@ -98,7 +98,7 @@ class V8_EXPORT_PRIVATE ExternalPointerTable { // returning the previous value. The same tag is applied both to decode the // previous value and encode the given value. // - // This method is atomic and can call be called from background threads. + // This method is atomic and can be called from background threads. inline Address Exchange(ExternalPointerHandle handle, Address value, ExternalPointerTag tag); diff --git a/deps/v8/src/snapshot/deserializer.cc b/deps/v8/src/snapshot/deserializer.cc index 8ac4ee423c..0fb3655949 100644 --- a/deps/v8/src/snapshot/deserializer.cc +++ b/deps/v8/src/snapshot/deserializer.cc @@ -399,6 +399,7 @@ void Deserializer<Isolate>::PostProcessNewJSReceiver(Map map, auto buffer = JSArrayBuffer::cast(*obj); uint32_t store_index = buffer.GetBackingStoreRefForDeserialization(); if (store_index == kEmptyBackingStoreRefSentinel) { + buffer.set_extension(nullptr); buffer.set_backing_store(main_thread_isolate(), EmptyBackingStoreBuffer()); } else { |