summaryrefslogtreecommitdiff
path: root/deps/v8/src
diff options
context:
space:
mode:
authorAnna Henningsen <anna.henningsen@mongodb.com>2022-12-21 13:00:47 +0100
committerGitHub <noreply@github.com>2022-12-21 12:00:47 +0000
commitb66ae39c1e34d958c0109f90af84e9210f813341 (patch)
treeece8674a2dff89cb0d25254adc57c8ceaed93971 /deps/v8/src
parent51246139e75da78d7621e162731f7618356f97dc (diff)
downloadnode-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.cc7
-rw-r--r--deps/v8/src/compiler/code-assembler.h3
-rw-r--r--deps/v8/src/objects/js-array-buffer-inl.h89
-rw-r--r--deps/v8/src/objects/js-array-buffer.h15
-rw-r--r--deps/v8/src/objects/js-array-buffer.tq2
-rw-r--r--deps/v8/src/objects/objects-body-descriptors-inl.h2
-rw-r--r--deps/v8/src/sandbox/external-pointer-table.h2
-rw-r--r--deps/v8/src/snapshot/deserializer.cc1
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 {