diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index d2b7b508132e7e..1917b6793af10e 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 10 #define V8_MINOR_VERSION 2 #define V8_BUILD_NUMBER 154 -#define V8_PATCH_LEVEL 15 +#define V8_PATCH_LEVEL 19 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/compiler/access-info.cc b/deps/v8/src/compiler/access-info.cc index 53cab92b8e72bb..6ad6a68092efc3 100644 --- a/deps/v8/src/compiler/access-info.cc +++ b/deps/v8/src/compiler/access-info.cc @@ -454,9 +454,15 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( map, descriptor, details_representation)); } else if (details_representation.IsHeapObject()) { if (descriptors_field_type->IsNone()) { - // Store is not safe if the field type was cleared. - if (access_mode == AccessMode::kStore) { - return Invalid(); + switch (access_mode) { + case AccessMode::kStore: + case AccessMode::kStoreInLiteral: + case AccessMode::kDefine: + // Store is not safe if the field type was cleared. + return Invalid(); + case AccessMode::kLoad: + case AccessMode::kHas: + break; } // The field type was cleared by the GC, so we don't know anything diff --git a/deps/v8/src/compiler/compilation-dependencies.cc b/deps/v8/src/compiler/compilation-dependencies.cc index 6f5514289b7646..62bd0f8c46d1e5 100644 --- a/deps/v8/src/compiler/compilation-dependencies.cc +++ b/deps/v8/src/compiler/compilation-dependencies.cc @@ -35,7 +35,8 @@ namespace compiler { V(Protector) \ V(PrototypeProperty) \ V(StableMap) \ - V(Transition) + V(Transition) \ + V(ObjectSlotValue) CompilationDependencies::CompilationDependencies(JSHeapBroker* broker, Zone* zone) @@ -863,6 +864,42 @@ class ProtectorDependency final : public CompilationDependency { const PropertyCellRef cell_; }; +// Check that an object slot will not change during compilation. +class ObjectSlotValueDependency final : public CompilationDependency { + public: + explicit ObjectSlotValueDependency(const HeapObjectRef& object, int offset, + const ObjectRef& value) + : CompilationDependency(kObjectSlotValue), + object_(object.object()), + offset_(offset), + value_(value.object()) {} + + bool IsValid() const override { + PtrComprCageBase cage_base = GetPtrComprCageBase(*object_); + Object current_value = + offset_ == HeapObject::kMapOffset + ? object_->map() + : TaggedField::Relaxed_Load(cage_base, *object_, offset_); + return *value_ == current_value; + } + void Install(PendingDependencies* deps) const override {} + + private: + size_t Hash() const override { + return base::hash_combine(object_.address(), offset_, value_.address()); + } + + bool Equals(const CompilationDependency* that) const override { + const ObjectSlotValueDependency* const zat = that->AsObjectSlotValue(); + return object_->address() == zat->object_->address() && + offset_ == zat->offset_ && value_.address() == zat->value_.address(); + } + + Handle object_; + int offset_; + Handle value_; +}; + class ElementsKindDependency final : public CompilationDependency { public: ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind) @@ -1110,6 +1147,12 @@ void CompilationDependencies::DependOnElementsKind( } } +void CompilationDependencies::DependOnObjectSlotValue( + const HeapObjectRef& object, int offset, const ObjectRef& value) { + RecordDependency( + zone_->New(object, offset, value)); +} + void CompilationDependencies::DependOnOwnConstantElement( const JSObjectRef& holder, uint32_t index, const ObjectRef& element) { RecordDependency( diff --git a/deps/v8/src/compiler/compilation-dependencies.h b/deps/v8/src/compiler/compilation-dependencies.h index aa8ff7b82abd2b..c6a18c400fe757 100644 --- a/deps/v8/src/compiler/compilation-dependencies.h +++ b/deps/v8/src/compiler/compilation-dependencies.h @@ -93,6 +93,10 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject { // Record the assumption that {site}'s {ElementsKind} doesn't change. void DependOnElementsKind(const AllocationSiteRef& site); + // Check that an object slot will not change during compilation. + void DependOnObjectSlotValue(const HeapObjectRef& object, int offset, + const ObjectRef& value); + void DependOnOwnConstantElement(const JSObjectRef& holder, uint32_t index, const ObjectRef& element); diff --git a/deps/v8/src/compiler/js-create-lowering.cc b/deps/v8/src/compiler/js-create-lowering.cc index fab65507ea0566..25a2ec03d2f4ac 100644 --- a/deps/v8/src/compiler/js-create-lowering.cc +++ b/deps/v8/src/compiler/js-create-lowering.cc @@ -1677,6 +1677,10 @@ base::Optional JSCreateLowering::TryAllocateFastLiteral( // Now that we hold the migration lock, get the current map. MapRef boilerplate_map = boilerplate.map(); + // Protect against concurrent changes to the boilerplate object by checking + // for an identical value at the end of the compilation. + dependencies()->DependOnObjectSlotValue(boilerplate, HeapObject::kMapOffset, + boilerplate_map); { base::Optional current_boilerplate_map = boilerplate.map_direct_read(); @@ -1841,10 +1845,18 @@ base::Optional JSCreateLowering::TryAllocateFastLiteralElements( boilerplate.elements(kRelaxedLoad); if (!maybe_boilerplate_elements.has_value()) return {}; FixedArrayBaseRef boilerplate_elements = maybe_boilerplate_elements.value(); + // Protect against concurrent changes to the boilerplate object by checking + // for an identical value at the end of the compilation. + dependencies()->DependOnObjectSlotValue( + boilerplate, JSObject::kElementsOffset, boilerplate_elements); // Empty or copy-on-write elements just store a constant. int const elements_length = boilerplate_elements.length(); MapRef elements_map = boilerplate_elements.map(); + // Protect against concurrent changes to the boilerplate object by checking + // for an identical value at the end of the compilation. + dependencies()->DependOnObjectSlotValue(boilerplate_elements, + HeapObject::kMapOffset, elements_map); if (boilerplate_elements.length() == 0 || elements_map.IsFixedCowArrayMap()) { if (allocation == AllocationType::kOld && !boilerplate.IsElementsTenured(boilerplate_elements)) { diff --git a/deps/v8/src/objects/value-serializer.cc b/deps/v8/src/objects/value-serializer.cc index 911ee9a7c662b1..44603cc7f9a75f 100644 --- a/deps/v8/src/objects/value-serializer.cc +++ b/deps/v8/src/objects/value-serializer.cc @@ -64,6 +64,17 @@ static const uint32_t kLatestVersion = 15; static_assert(kLatestVersion == v8::CurrentValueSerializerFormatVersion(), "Exported format version must match latest version."); +namespace { +// For serializing JSArrayBufferView flags. Instead of serializing / +// deserializing the flags directly, we serialize them bit by bit. This is for +// ensuring backwards compatilibity in the case where the representation +// changes. Note that the ValueSerializer data can be stored on disk. +using JSArrayBufferViewIsLengthTracking = base::BitField; +using JSArrayBufferViewIsBackedByRab = + JSArrayBufferViewIsLengthTracking::Next; + +} // namespace + template static size_t BytesNeededForVarint(T value) { static_assert(std::is_integral::value && std::is_unsigned::value, @@ -922,6 +933,8 @@ Maybe ValueSerializer::WriteJSArrayBuffer( if (byte_length > std::numeric_limits::max()) { return ThrowDataCloneError(MessageTemplate::kDataCloneError, array_buffer); } + // TODO(v8:11111): Support RAB / GSAB. The wire version will need to be + // bumped. WriteTag(SerializationTag::kArrayBuffer); WriteVarint(byte_length); WriteRawBytes(array_buffer->backing_store(), byte_length); @@ -950,7 +963,10 @@ Maybe ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView view) { WriteVarint(static_cast(tag)); WriteVarint(static_cast(view.byte_offset())); WriteVarint(static_cast(view.byte_length())); - WriteVarint(static_cast(view.bit_field())); + uint32_t flags = + JSArrayBufferViewIsLengthTracking::encode(view.is_length_tracking()) | + JSArrayBufferViewIsBackedByRab::encode(view.is_backed_by_rab()); + WriteVarint(flags); return ThrowIfOutOfMemory(); } @@ -1979,7 +1995,7 @@ MaybeHandle ValueDeserializer::ReadTransferredJSArrayBuffer() { MaybeHandle ValueDeserializer::ReadJSArrayBufferView( Handle buffer) { - uint32_t buffer_byte_length = static_cast(buffer->byte_length()); + uint32_t buffer_byte_length = static_cast(buffer->GetByteLength()); uint8_t tag = 0; uint32_t byte_offset = 0; uint32_t byte_length = 0; @@ -2004,7 +2020,9 @@ MaybeHandle ValueDeserializer::ReadJSArrayBufferView( Handle data_view = isolate_->factory()->NewJSDataView(buffer, byte_offset, byte_length); AddObjectWithID(id, data_view); - data_view->set_bit_field(flags); + if (!ValidateAndSetJSArrayBufferViewFlags(*data_view, *buffer, flags)) { + return MaybeHandle(); + } return data_view; } #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ @@ -2021,11 +2039,39 @@ MaybeHandle ValueDeserializer::ReadJSArrayBufferView( } Handle typed_array = isolate_->factory()->NewJSTypedArray( external_array_type, buffer, byte_offset, byte_length / element_size); - typed_array->set_bit_field(flags); + if (!ValidateAndSetJSArrayBufferViewFlags(*typed_array, *buffer, flags)) { + return MaybeHandle(); + } AddObjectWithID(id, typed_array); return typed_array; } +bool ValueDeserializer::ValidateAndSetJSArrayBufferViewFlags( + JSArrayBufferView view, JSArrayBuffer buffer, uint32_t serialized_flags) { + bool is_length_tracking = + JSArrayBufferViewIsLengthTracking::decode(serialized_flags); + bool is_backed_by_rab = + JSArrayBufferViewIsBackedByRab::decode(serialized_flags); + + // TODO(marja): When the version number is bumped the next time, check that + // serialized_flags doesn't contain spurious 1-bits. + + if (is_backed_by_rab || is_length_tracking) { + if (!FLAG_harmony_rab_gsab) { + return false; + } + if (!buffer.is_resizable()) { + return false; + } + if (is_backed_by_rab && buffer.is_shared()) { + return false; + } + } + view.set_is_length_tracking(is_length_tracking); + view.set_is_backed_by_rab(is_backed_by_rab); + return true; +} + MaybeHandle ValueDeserializer::ReadJSError() { uint32_t id = next_id_++; diff --git a/deps/v8/src/objects/value-serializer.h b/deps/v8/src/objects/value-serializer.h index d6804e04a74a86..b07274a430c4ab 100644 --- a/deps/v8/src/objects/value-serializer.h +++ b/deps/v8/src/objects/value-serializer.h @@ -295,6 +295,9 @@ class ValueDeserializer { V8_WARN_UNUSED_RESULT; MaybeHandle ReadJSArrayBufferView( Handle buffer) V8_WARN_UNUSED_RESULT; + bool ValidateAndSetJSArrayBufferViewFlags( + JSArrayBufferView view, JSArrayBuffer buffer, + uint32_t serialized_flags) V8_WARN_UNUSED_RESULT; MaybeHandle ReadJSError() V8_WARN_UNUSED_RESULT; #if V8_ENABLE_WEBASSEMBLY MaybeHandle ReadWasmModuleTransfer() V8_WARN_UNUSED_RESULT; diff --git a/deps/v8/src/wasm/baseline/liftoff-compiler.cc b/deps/v8/src/wasm/baseline/liftoff-compiler.cc index 34b51e37c65027..508a91ecb8aa06 100644 --- a/deps/v8/src/wasm/baseline/liftoff-compiler.cc +++ b/deps/v8/src/wasm/baseline/liftoff-compiler.cc @@ -1417,9 +1417,11 @@ class LiftoffCompiler { __ MergeFullStackWith(c->label_state, *__ cache_state()); __ emit_jump(c->label.get()); } - // Merge the else state into the end state. + // Merge the else state into the end state. Set this state as the current + // state first so helper functions know which registers are in use. __ bind(c->else_state->label.get()); - __ MergeFullStackWith(c->label_state, c->else_state->state); + __ cache_state()->Steal(c->else_state->state); + __ MergeFullStackWith(c->label_state, *__ cache_state()); __ cache_state()->Steal(c->label_state); } else if (c->reachable()) { // No merge yet at the end of the if, but we need to create a merge for @@ -1431,9 +1433,11 @@ class LiftoffCompiler { c->stack_depth + c->num_exceptions); __ MergeFullStackWith(c->label_state, *__ cache_state()); __ emit_jump(c->label.get()); - // Merge the else state into the end state. + // Merge the else state into the end state. Set this state as the current + // state first so helper functions know which registers are in use. __ bind(c->else_state->label.get()); - __ MergeFullStackWith(c->label_state, c->else_state->state); + __ cache_state()->Steal(c->else_state->state); + __ MergeFullStackWith(c->label_state, *__ cache_state()); __ cache_state()->Steal(c->label_state); } else { // No merge needed, just continue with the else state. diff --git a/deps/v8/test/mjsunit/rab-gsab-valueserializer.js b/deps/v8/test/mjsunit/rab-gsab-valueserializer.js new file mode 100644 index 00000000000000..f523648095f250 --- /dev/null +++ b/deps/v8/test/mjsunit/rab-gsab-valueserializer.js @@ -0,0 +1,17 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-rab-gsab + +"use strict"; + +(function FlagMismatch() { + // Length tracking TA, buffer not resizable. + const data1 = new Uint8Array([255, 15, 66, 4, 3, 5, 7, 11, 86, 66, 1, 2, 1]); + assertThrows(() => { d8.serializer.deserialize(data1.buffer); }); + + // RAB backed TA, buffer not resizable. + const data2 = new Uint8Array([255, 15, 66, 4, 3, 5, 7, 11, 86, 66, 1, 2, 2]); + assertThrows(() => { d8.serializer.deserialize(data2.buffer); }); +})();