Commit 5f0ac36c authored by Jakob Gruber's avatar Jakob Gruber Committed by V8 LUCI CQ

[compiler] Consider IsPendingAllocation in Ref construction

The logic in JSHeapBroker::TryGetOrCreateData assumes that parts
of the object are safe to read. In particular, the instance type
must be readable for the chain of `Is##Name()` type checks.

This is guaranteed if

 - a global memory fence happened after object initialization and
   prior to the read by the compiler; or
 - the object was published through a release store and read through
   an acquire read.

The former is protected by the new call to ObjectMayBeUninitialized
(which internally calls IsPendingAllocation) in TryGetOrCreateData.

The latter must be marked explicitly by calling the new
MakeRefAssumeMemoryFence variant.

Note that support in this CL is expected to be incomplete and will
have to be extended in the future as more cases show up in which
MakeRef calls must be converted to MakeRefAssumeMemoryFence or to
TryMakeRef.

Bug: v8:7790,v8:11711
Change-Id: Ic2f7d9fc46e4bfc3f6bbe42816f73fc5ec174337
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2874663
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74474}
parent 58483154
This diff is collapsed.
...@@ -227,27 +227,21 @@ bool JSHeapBroker::IsArrayOrObjectPrototype(Handle<JSObject> object) const { ...@@ -227,27 +227,21 @@ bool JSHeapBroker::IsArrayOrObjectPrototype(Handle<JSObject> object) const {
array_and_object_prototypes_.end(); array_and_object_prototypes_.end();
} }
ObjectData* JSHeapBroker::TryGetOrCreateData( ObjectData* JSHeapBroker::TryGetOrCreateData(Object object,
Object object, bool crash_on_error, GetOrCreateDataFlags flags) {
ObjectRef::BackgroundSerialization background_serialization) { return TryGetOrCreateData(CanonicalPersistentHandle(object), flags);
return TryGetOrCreateData(CanonicalPersistentHandle(object), crash_on_error, }
background_serialization);
} ObjectData* JSHeapBroker::GetOrCreateData(Handle<Object> object,
GetOrCreateDataFlags flags) {
ObjectData* JSHeapBroker::GetOrCreateData( ObjectData* return_value = TryGetOrCreateData(object, flags | kCrashOnError);
Handle<Object> object,
ObjectRef::BackgroundSerialization background_serialization) {
ObjectData* return_value =
TryGetOrCreateData(object, true, background_serialization);
DCHECK_NOT_NULL(return_value); DCHECK_NOT_NULL(return_value);
return return_value; return return_value;
} }
ObjectData* JSHeapBroker::GetOrCreateData( ObjectData* JSHeapBroker::GetOrCreateData(Object object,
Object object, GetOrCreateDataFlags flags) {
ObjectRef::BackgroundSerialization background_serialization) { return GetOrCreateData(CanonicalPersistentHandle(object), flags);
return GetOrCreateData(CanonicalPersistentHandle(object),
background_serialization);
} }
bool JSHeapBroker::StackHasOverflowed() const { bool JSHeapBroker::StackHasOverflowed() const {
...@@ -259,8 +253,12 @@ bool JSHeapBroker::StackHasOverflowed() const { ...@@ -259,8 +253,12 @@ bool JSHeapBroker::StackHasOverflowed() const {
} }
bool JSHeapBroker::ObjectMayBeUninitialized(Handle<Object> object) const { bool JSHeapBroker::ObjectMayBeUninitialized(Handle<Object> object) const {
return !IsMainThread() && object->IsHeapObject() && if (!object->IsHeapObject()) return false;
isolate()->heap()->IsPendingAllocation(HeapObject::cast(*object)); return ObjectMayBeUninitialized(HeapObject::cast(*object));
}
bool JSHeapBroker::ObjectMayBeUninitialized(HeapObject object) const {
return !IsMainThread() && isolate()->heap()->IsPendingAllocation(object);
} }
bool CanInlineElementAccess(MapRef const& map) { bool CanInlineElementAccess(MapRef const& map) {
......
...@@ -79,6 +79,21 @@ struct PropertyAccessTarget { ...@@ -79,6 +79,21 @@ struct PropertyAccessTarget {
}; };
}; };
enum GetOrCreateDataFlag {
// If set, a failure to create the data object results in a crash.
kCrashOnError = 1 << 0,
// If set, data construction assumes that the given object is protected by
// a memory fence (e.g. acquire-release) and thus fields required for
// construction (like Object::map) are safe to read. The protection can
// extend to some other situations as well.
kAssumeMemoryFence = 1 << 1,
// Whether background serialization is allowed (only used for
// kPossiblyBackgroundSerialized refs).
kAllowBackgroundSerialization = 1 << 2,
};
using GetOrCreateDataFlags = base::Flags<GetOrCreateDataFlag>;
DEFINE_OPERATORS_FOR_FLAGS(GetOrCreateDataFlags)
class V8_EXPORT_PRIVATE JSHeapBroker { class V8_EXPORT_PRIVATE JSHeapBroker {
public: public:
JSHeapBroker(Isolate* isolate, Zone* broker_zone, bool tracing_enabled, JSHeapBroker(Isolate* isolate, Zone* broker_zone, bool tracing_enabled,
...@@ -151,25 +166,16 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -151,25 +166,16 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
Handle<Object> GetRootHandle(Object object); Handle<Object> GetRootHandle(Object object);
// Never returns nullptr. // Never returns nullptr.
ObjectData* GetOrCreateData( ObjectData* GetOrCreateData(Handle<Object> object,
Handle<Object>, GetOrCreateDataFlags flags = {});
ObjectRef::BackgroundSerialization background_serialization = ObjectData* GetOrCreateData(Object object, GetOrCreateDataFlags flags = {});
ObjectRef::BackgroundSerialization::kDisallowed);
// Like the previous but wraps argument in handle first (for convenience).
ObjectData* GetOrCreateData(
Object, ObjectRef::BackgroundSerialization background_serialization =
ObjectRef::BackgroundSerialization::kDisallowed);
// Gets data only if we have it. However, thin wrappers will be created for // Gets data only if we have it. However, thin wrappers will be created for
// smis, read-only objects and never-serialized objects. // smis, read-only objects and never-serialized objects.
ObjectData* TryGetOrCreateData( ObjectData* TryGetOrCreateData(Handle<Object> object,
Handle<Object>, bool crash_on_error = false, GetOrCreateDataFlags flags = {});
ObjectRef::BackgroundSerialization background_serialization = ObjectData* TryGetOrCreateData(Object object,
ObjectRef::BackgroundSerialization::kDisallowed); GetOrCreateDataFlags flags = {});
ObjectData* TryGetOrCreateData(
Object object, bool crash_on_error = false,
ObjectRef::BackgroundSerialization background_serialization =
ObjectRef::BackgroundSerialization::kDisallowed);
// Check if {object} is any native context's %ArrayPrototype% or // Check if {object} is any native context's %ArrayPrototype% or
// %ObjectPrototype%. // %ObjectPrototype%.
...@@ -391,6 +397,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -391,6 +397,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
// thus safe to read from a memory safety perspective. The converse does not // thus safe to read from a memory safety perspective. The converse does not
// necessarily hold. // necessarily hold.
bool ObjectMayBeUninitialized(Handle<Object> object) const; bool ObjectMayBeUninitialized(Handle<Object> object) const;
bool ObjectMayBeUninitialized(HeapObject object) const;
bool CanUseFeedback(const FeedbackNexus& nexus) const; bool CanUseFeedback(const FeedbackNexus& nexus) const;
const ProcessedFeedback& NewInsufficientFeedback(FeedbackSlotKind kind) const; const ProcessedFeedback& NewInsufficientFeedback(FeedbackSlotKind kind) const;
...@@ -580,8 +587,8 @@ class V8_NODISCARD UnparkedScopeIfNeeded { ...@@ -580,8 +587,8 @@ class V8_NODISCARD UnparkedScopeIfNeeded {
template <class T, template <class T,
typename = std::enable_if_t<std::is_convertible<T*, Object*>::value>> typename = std::enable_if_t<std::is_convertible<T*, Object*>::value>>
base::Optional<typename ref_traits<T>::ref_type> TryMakeRef( base::Optional<typename ref_traits<T>::ref_type> TryMakeRef(
JSHeapBroker* broker, T object) { JSHeapBroker* broker, T object, GetOrCreateDataFlags flags = {}) {
ObjectData* data = broker->TryGetOrCreateData(object); ObjectData* data = broker->TryGetOrCreateData(object, flags);
if (data == nullptr) { if (data == nullptr) {
TRACE_BROKER_MISSING(broker, "ObjectData for " << Brief(object)); TRACE_BROKER_MISSING(broker, "ObjectData for " << Brief(object));
return {}; return {};
...@@ -592,8 +599,8 @@ base::Optional<typename ref_traits<T>::ref_type> TryMakeRef( ...@@ -592,8 +599,8 @@ base::Optional<typename ref_traits<T>::ref_type> TryMakeRef(
template <class T, template <class T,
typename = std::enable_if_t<std::is_convertible<T*, Object*>::value>> typename = std::enable_if_t<std::is_convertible<T*, Object*>::value>>
base::Optional<typename ref_traits<T>::ref_type> TryMakeRef( base::Optional<typename ref_traits<T>::ref_type> TryMakeRef(
JSHeapBroker* broker, Handle<T> object) { JSHeapBroker* broker, Handle<T> object, GetOrCreateDataFlags flags = {}) {
ObjectData* data = broker->TryGetOrCreateData(object); ObjectData* data = broker->TryGetOrCreateData(object, flags);
if (data == nullptr) { if (data == nullptr) {
TRACE_BROKER_MISSING(broker, "ObjectData for " << Brief(*object)); TRACE_BROKER_MISSING(broker, "ObjectData for " << Brief(*object));
return {}; return {};
...@@ -614,6 +621,20 @@ typename ref_traits<T>::ref_type MakeRef(JSHeapBroker* broker, ...@@ -614,6 +621,20 @@ typename ref_traits<T>::ref_type MakeRef(JSHeapBroker* broker,
return TryMakeRef(broker, object).value(); return TryMakeRef(broker, object).value();
} }
template <class T,
typename = std::enable_if_t<std::is_convertible<T*, Object*>::value>>
typename ref_traits<T>::ref_type MakeRefAssumeMemoryFence(JSHeapBroker* broker,
T object) {
return TryMakeRef(broker, object, kAssumeMemoryFence).value();
}
template <class T,
typename = std::enable_if_t<std::is_convertible<T*, Object*>::value>>
typename ref_traits<T>::ref_type MakeRefAssumeMemoryFence(JSHeapBroker* broker,
Handle<T> object) {
return TryMakeRef(broker, object, kAssumeMemoryFence).value();
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
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