8bdce52713
This is a reland of e0c1a349ea
The issue was passing SentinelPointer (== +1) through T*.
The fix is disabling cfi unrelated cast diagnostic for the bottlenecks
(Get()). This means that nullptr is treated the same as
kSentinelPointer.
The alternative would be a DCHECK that Get() does not return
kSentinelPointer and adjusting all Member and Persistent logic that
uses Get() to work on void*. This is quite intrusive as it involves
Swap(), heterogeneous assignments, comparisons, etc.
Original change's description:
> cppgc: Properly clear (Weak)Peristent and WeakMember pointers
>
> The CL addresses two issues with (Weak)Persistent and WeakMember:
> 1. (Weak)Persistent pointers are cleared on heap teardown. Before this
> CL the pointers would contain stale values which could lead to UAF.
> 2. WeakPersistent and WeakMember are cleared using a combination of
> internal clearing methods and mutable fields which avoids the use
> of const_cast<>.
>
> Bug: chromium:1056170
> Change-Id: Ibf2b0f0856771b4f6906608cde13a6d43ebf81f3
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2248190
> Reviewed-by: Omer Katz <omerkatz@chromium.org>
> Reviewed-by: Anton Bikineev <bikineev@chromium.org>
> Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#68394}
Bug: chromium:1056170
Change-Id: I3d74b43464c2973df1956f51b1419d755dd9f519
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2250240
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68426}
202 lines
6.7 KiB
C++
202 lines
6.7 KiB
C++
// Copyright 2020 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.
|
|
|
|
#ifndef INCLUDE_CPPGC_VISITOR_H_
|
|
#define INCLUDE_CPPGC_VISITOR_H_
|
|
|
|
#include "cppgc/garbage-collected.h"
|
|
#include "cppgc/internal/logging.h"
|
|
#include "cppgc/internal/pointer-policies.h"
|
|
#include "cppgc/liveness-broker.h"
|
|
#include "cppgc/member.h"
|
|
#include "cppgc/source-location.h"
|
|
#include "cppgc/trace-trait.h"
|
|
|
|
namespace cppgc {
|
|
|
|
namespace internal {
|
|
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
|
|
typename CheckingPolicy>
|
|
class BasicPersistent;
|
|
class VisitorBase;
|
|
} // namespace internal
|
|
|
|
using WeakCallback = void (*)(const LivenessBroker&, const void*);
|
|
|
|
/**
|
|
* Visitor passed to trace methods. All managed pointers must have called the
|
|
* Visitor's trace method on them.
|
|
*
|
|
* \code
|
|
* class Foo final : public GarbageCollected<Foo> {
|
|
* public:
|
|
* void Trace(Visitor* visitor) const {
|
|
* visitor->Trace(foo_);
|
|
* visitor->Trace(weak_foo_);
|
|
* }
|
|
* private:
|
|
* Member<Foo> foo_;
|
|
* WeakMember<Foo> weak_foo_;
|
|
* };
|
|
* \endcode
|
|
*/
|
|
class Visitor {
|
|
public:
|
|
/**
|
|
* Trace method for Member.
|
|
*
|
|
* \param member Member reference retaining an object.
|
|
*/
|
|
template <typename T>
|
|
void Trace(const Member<T>& member) {
|
|
const T* value = member.GetRawAtomic();
|
|
CPPGC_DCHECK(value != kSentinelPointer);
|
|
Trace(value);
|
|
}
|
|
|
|
/**
|
|
* Trace method for WeakMember.
|
|
*
|
|
* \param weak_member WeakMember reference weakly retaining an object.
|
|
*/
|
|
template <typename T>
|
|
void Trace(const WeakMember<T>& weak_member) {
|
|
static_assert(sizeof(T), "Pointee type must be fully defined.");
|
|
static_assert(internal::IsGarbageCollectedType<T>::value,
|
|
"T must be GarbageCollected or GarbageCollectedMixin type");
|
|
|
|
const T* value = weak_member.GetRawAtomic();
|
|
|
|
// Bailout assumes that WeakMember emits write barrier.
|
|
if (!value) {
|
|
return;
|
|
}
|
|
|
|
// TODO(chromium:1056170): DCHECK (or similar) for deleted values as they
|
|
// should come in at a different path.
|
|
VisitWeak(value, TraceTrait<T>::GetTraceDescriptor(value),
|
|
&HandleWeak<WeakMember<T>>, &weak_member);
|
|
}
|
|
|
|
/**
|
|
* Trace method for inlined objects that are not allocated themselves but
|
|
* otherwise follow managed heap layout and have a Trace() method.
|
|
*
|
|
* \param object reference of the inlined object.
|
|
*/
|
|
template <typename T>
|
|
void Trace(const T& object) {
|
|
#if V8_ENABLE_CHECKS
|
|
// This object is embedded in potentially multiple nested objects. The
|
|
// outermost object must not be in construction as such objects are (a) not
|
|
// processed immediately, and (b) only processed conservatively if not
|
|
// otherwise possible.
|
|
CheckObjectNotInConstruction(&object);
|
|
#endif // V8_ENABLE_CHECKS
|
|
TraceTrait<T>::Trace(this, &object);
|
|
}
|
|
|
|
/**
|
|
* Registers a weak callback method on the object of type T. See
|
|
* LivenessBroker for an usage example.
|
|
*
|
|
* \param object of type T specifying a weak callback method.
|
|
*/
|
|
template <typename T, void (T::*method)(const LivenessBroker&)>
|
|
void RegisterWeakCallbackMethod(const T* object) {
|
|
RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>, object);
|
|
}
|
|
|
|
/**
|
|
* Registers a weak callback that is invoked during garbage collection.
|
|
*
|
|
* \param callback to be invoked.
|
|
* \param data custom data that is passed to the callback.
|
|
*/
|
|
virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {}
|
|
|
|
protected:
|
|
virtual void Visit(const void* self, TraceDescriptor) {}
|
|
virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback,
|
|
const void* weak_member) {}
|
|
virtual void VisitRoot(const void*, TraceDescriptor) {}
|
|
virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback,
|
|
const void* weak_root) {}
|
|
|
|
private:
|
|
template <typename T, void (T::*method)(const LivenessBroker&)>
|
|
static void WeakCallbackMethodDelegate(const LivenessBroker& info,
|
|
const void* self) {
|
|
// Callback is registered through a potential const Trace method but needs
|
|
// to be able to modify fields. See HandleWeak.
|
|
(const_cast<T*>(static_cast<const T*>(self))->*method)(info);
|
|
}
|
|
|
|
template <typename PointerType>
|
|
static void HandleWeak(const LivenessBroker& info, const void* object) {
|
|
const PointerType* weak = static_cast<const PointerType*>(object);
|
|
// Sentinel values are preserved for weak pointers.
|
|
if (*weak == kSentinelPointer) return;
|
|
const auto* raw = weak->Get();
|
|
if (!info.IsHeapObjectAlive(raw)) {
|
|
weak->ClearFromGC();
|
|
}
|
|
}
|
|
|
|
Visitor() = default;
|
|
|
|
template <typename Persistent,
|
|
std::enable_if_t<Persistent::IsStrongPersistent::value>* = nullptr>
|
|
void TraceRoot(const Persistent& p, const SourceLocation& loc) {
|
|
using PointeeType = typename Persistent::PointeeType;
|
|
static_assert(sizeof(PointeeType),
|
|
"Persistent's pointee type must be fully defined");
|
|
static_assert(internal::IsGarbageCollectedType<PointeeType>::value,
|
|
"Persistent's pointee type must be GarbageCollected or "
|
|
"GarbageCollectedMixin");
|
|
if (!p.Get()) {
|
|
return;
|
|
}
|
|
VisitRoot(p.Get(), TraceTrait<PointeeType>::GetTraceDescriptor(p.Get()));
|
|
}
|
|
|
|
template <
|
|
typename WeakPersistent,
|
|
std::enable_if_t<!WeakPersistent::IsStrongPersistent::value>* = nullptr>
|
|
void TraceRoot(const WeakPersistent& p, const SourceLocation& loc) {
|
|
using PointeeType = typename WeakPersistent::PointeeType;
|
|
static_assert(sizeof(PointeeType),
|
|
"Persistent's pointee type must be fully defined");
|
|
static_assert(internal::IsGarbageCollectedType<PointeeType>::value,
|
|
"Persistent's pointee type must be GarbageCollected or "
|
|
"GarbageCollectedMixin");
|
|
VisitWeakRoot(p.Get(), TraceTrait<PointeeType>::GetTraceDescriptor(p.Get()),
|
|
&HandleWeak<WeakPersistent>, &p);
|
|
}
|
|
|
|
template <typename T>
|
|
void Trace(const T* t) {
|
|
static_assert(sizeof(T), "Pointee type must be fully defined.");
|
|
static_assert(internal::IsGarbageCollectedType<T>::value,
|
|
"T must be GarbageCollected or GarbageCollectedMixin type");
|
|
if (!t) {
|
|
return;
|
|
}
|
|
Visit(t, TraceTrait<T>::GetTraceDescriptor(t));
|
|
}
|
|
|
|
#if V8_ENABLE_CHECKS
|
|
V8_EXPORT void CheckObjectNotInConstruction(const void* address);
|
|
#endif // V8_ENABLE_CHECKS
|
|
|
|
friend class internal::VisitorBase;
|
|
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
|
|
typename CheckingPolicy>
|
|
friend class internal::BasicPersistent;
|
|
};
|
|
|
|
} // namespace cppgc
|
|
|
|
#endif // INCLUDE_CPPGC_VISITOR_H_
|