Commit 2c80e714 authored by Omer Katz's avatar Omer Katz Committed by V8 LUCI CQ

cppgc: Check mark bit on assignment from prefinalizer.

Check that the marked bit of an object is set if assigned during a
prefinalizer to a Member in a live object or a Persistent.

Bug: v8:11749
Change-Id: I993c0d226a4157698591e1f7bc0c55e5c79239b6
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2897093
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74672}
parent 16e90e0d
...@@ -283,6 +283,10 @@ declare_args() { ...@@ -283,6 +283,10 @@ declare_args() {
# TODO(v8:11785): Enable by default when running with the verifier. # TODO(v8:11785): Enable by default when running with the verifier.
cppgc_enable_verify_live_bytes = false cppgc_enable_verify_live_bytes = false
# Enable assignment checks for Members/Persistents during prefinalizer invocations.
# TODO(v8:11749): Enable by default after fixing any existing issues in Blink.
cppgc_enable_check_assignments_in_prefinalizers = false
# Enable young generation in cppgc. # Enable young generation in cppgc.
cppgc_enable_young_generation = false cppgc_enable_young_generation = false
...@@ -711,6 +715,10 @@ config("features") { ...@@ -711,6 +715,10 @@ config("features") {
defines += [ "CPPGC_VERIFY_LIVE_BYTES" ] defines += [ "CPPGC_VERIFY_LIVE_BYTES" ]
} }
if (cppgc_enable_check_assignments_in_prefinalizers) {
defines += [ "CPPGC_CHECK_ASSIGNMENTS_IN_PREFINALIZERS" ]
}
if (v8_embedder_string != "") { if (v8_embedder_string != "") {
defines += [ "V8_EMBEDDER_STRING=\"$v8_embedder_string\"" ] defines += [ "V8_EMBEDDER_STRING=\"$v8_embedder_string\"" ]
} }
......
...@@ -82,7 +82,7 @@ class V8_EXPORT EnabledCheckingPolicy { ...@@ -82,7 +82,7 @@ class V8_EXPORT EnabledCheckingPolicy {
class DisabledCheckingPolicy { class DisabledCheckingPolicy {
protected: protected:
void CheckPointer(const void* raw) {} void CheckPointer(const void*) {}
}; };
#if V8_ENABLE_CHECKS #if V8_ENABLE_CHECKS
...@@ -92,6 +92,10 @@ using DefaultPersistentCheckingPolicy = EnabledCheckingPolicy; ...@@ -92,6 +92,10 @@ using DefaultPersistentCheckingPolicy = EnabledCheckingPolicy;
using DefaultMemberCheckingPolicy = DisabledCheckingPolicy; using DefaultMemberCheckingPolicy = DisabledCheckingPolicy;
using DefaultPersistentCheckingPolicy = DisabledCheckingPolicy; using DefaultPersistentCheckingPolicy = DisabledCheckingPolicy;
#endif #endif
// For CT(W)P neither marking information (for value), nor objectstart bitmap
// (for slot) are guaranteed to be present because there's no synchonization
// between heaps after marking.
using DefaultCrossThreadPersistentCheckingPolicy = DisabledCheckingPolicy;
class KeepLocationPolicy { class KeepLocationPolicy {
public: public:
...@@ -154,7 +158,7 @@ struct WeakCrossThreadPersistentPolicy { ...@@ -154,7 +158,7 @@ struct WeakCrossThreadPersistentPolicy {
// Forward declarations setting up the default policies. // Forward declarations setting up the default policies.
template <typename T, typename WeaknessPolicy, template <typename T, typename WeaknessPolicy,
typename LocationPolicy = DefaultLocationPolicy, typename LocationPolicy = DefaultLocationPolicy,
typename CheckingPolicy = DisabledCheckingPolicy> typename CheckingPolicy = DefaultCrossThreadPersistentCheckingPolicy>
class BasicCrossThreadPersistent; class BasicCrossThreadPersistent;
template <typename T, typename WeaknessPolicy, template <typename T, typename WeaknessPolicy,
typename LocationPolicy = DefaultLocationPolicy, typename LocationPolicy = DefaultLocationPolicy,
......
...@@ -114,6 +114,9 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -114,6 +114,9 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
PreFinalizerHandler* prefinalizer_handler() { PreFinalizerHandler* prefinalizer_handler() {
return prefinalizer_handler_.get(); return prefinalizer_handler_.get();
} }
const PreFinalizerHandler* prefinalizer_handler() const {
return prefinalizer_handler_.get();
}
MarkerBase* marker() const { return marker_.get(); } MarkerBase* marker() const { return marker_.get(); }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "src/heap/cppgc/heap-page.h" #include "src/heap/cppgc/heap-page.h"
#include "src/heap/cppgc/heap.h" #include "src/heap/cppgc/heap.h"
#include "src/heap/cppgc/page-memory.h" #include "src/heap/cppgc/page-memory.h"
#include "src/heap/cppgc/prefinalizer-handler.h"
#include "src/heap/cppgc/process-heap.h" #include "src/heap/cppgc/process-heap.h"
namespace cppgc { namespace cppgc {
...@@ -67,7 +68,20 @@ void EnabledCheckingPolicy::CheckPointerImpl(const void* ptr, ...@@ -67,7 +68,20 @@ void EnabledCheckingPolicy::CheckPointerImpl(const void* ptr,
DCHECK(!header->IsFree()); DCHECK(!header->IsFree());
} }
// TODO(v8:11749): Check mark bits when during pre-finalizer phase. #ifdef CPPGC_CHECK_ASSIGNMENTS_IN_PREFINALIZERS
if (heap_->prefinalizer_handler()->IsInvokingPreFinalizers()) {
// During prefinalizers invocation, check that |ptr| refers to a live object
// and that it is assigned to a live slot.
DCHECK(header->IsMarked());
// Slot can be in a large object.
const auto* slot_page = BasePage::FromInnerAddress(heap_, this);
// Off-heap slots (from other heaps or on-stack) are considered live.
bool slot_is_live =
!slot_page || slot_page->ObjectHeaderFromInnerAddress(this).IsMarked();
DCHECK(slot_is_live);
USE(slot_is_live);
}
#endif // CPPGC_CHECK_ASSIGNMENTS_IN_PREFINALIZERS
} }
PersistentRegion& StrongPersistentPolicy::GetPersistentRegion( PersistentRegion& StrongPersistentPolicy::GetPersistentRegion(
......
...@@ -53,6 +53,7 @@ void PreFinalizerHandler::InvokePreFinalizers() { ...@@ -53,6 +53,7 @@ void PreFinalizerHandler::InvokePreFinalizers() {
DCHECK(CurrentThreadIsCreationThread()); DCHECK(CurrentThreadIsCreationThread());
LivenessBroker liveness_broker = LivenessBrokerFactory::Create(); LivenessBroker liveness_broker = LivenessBrokerFactory::Create();
is_invoking_ = true;
ordered_pre_finalizers_.erase( ordered_pre_finalizers_.erase(
ordered_pre_finalizers_.begin(), ordered_pre_finalizers_.begin(),
std::remove_if(ordered_pre_finalizers_.rbegin(), std::remove_if(ordered_pre_finalizers_.rbegin(),
...@@ -61,6 +62,7 @@ void PreFinalizerHandler::InvokePreFinalizers() { ...@@ -61,6 +62,7 @@ void PreFinalizerHandler::InvokePreFinalizers() {
return (pf.callback)(liveness_broker, pf.object); return (pf.callback)(liveness_broker, pf.object);
}) })
.base()); .base());
is_invoking_ = false;
ordered_pre_finalizers_.shrink_to_fit(); ordered_pre_finalizers_.shrink_to_fit();
} }
......
...@@ -25,6 +25,8 @@ class PreFinalizerHandler final { ...@@ -25,6 +25,8 @@ class PreFinalizerHandler final {
void InvokePreFinalizers(); void InvokePreFinalizers();
bool IsInvokingPreFinalizers() const { return is_invoking_; }
private: private:
// Checks that the current thread is the thread that created the heap. // Checks that the current thread is the thread that created the heap.
bool CurrentThreadIsCreationThread(); bool CurrentThreadIsCreationThread();
...@@ -36,6 +38,7 @@ class PreFinalizerHandler final { ...@@ -36,6 +38,7 @@ class PreFinalizerHandler final {
std::vector<PreFinalizer> ordered_pre_finalizers_; std::vector<PreFinalizer> ordered_pre_finalizers_;
HeapBase& heap_; HeapBase& heap_;
bool is_invoking_ = false;
#ifdef DEBUG #ifdef DEBUG
int creation_thread_id_; int creation_thread_id_;
#endif #endif
......
...@@ -226,22 +226,10 @@ TEST_F(PrefinalizerTest, PrefinalizerCanRewireGraphWithLiveObjects) { ...@@ -226,22 +226,10 @@ TEST_F(PrefinalizerTest, PrefinalizerCanRewireGraphWithLiveObjects) {
PreciseGC(); PreciseGC();
} }
TEST_F(PrefinalizerTest, PrefinalizerCanRewireGraphWithDeadObjects) {
Persistent<LinkedNode> root{MakeGarbageCollected<LinkedNode>(
GetAllocationHandle(),
MakeGarbageCollected<LinkedNode>(
GetAllocationHandle(),
MakeGarbageCollected<LinkedNode>(GetAllocationHandle(), nullptr)))};
CHECK(root->next());
MakeGarbageCollected<MutatingPrefinalizer>(GetAllocationHandle(), root.Get());
// All LinkedNode objects will die on the following GC. The pre-finalizer may
// still operate with them but not add them to a live object.
root.Clear();
PreciseGC();
}
namespace { namespace {
class PrefinalizerDeathTest : public testing::TestWithHeap {};
class AllocatingPrefinalizer : public GarbageCollected<AllocatingPrefinalizer> { class AllocatingPrefinalizer : public GarbageCollected<AllocatingPrefinalizer> {
CPPGC_USING_PRE_FINALIZER(AllocatingPrefinalizer, PreFinalizer); CPPGC_USING_PRE_FINALIZER(AllocatingPrefinalizer, PreFinalizer);
...@@ -260,7 +248,7 @@ class AllocatingPrefinalizer : public GarbageCollected<AllocatingPrefinalizer> { ...@@ -260,7 +248,7 @@ class AllocatingPrefinalizer : public GarbageCollected<AllocatingPrefinalizer> {
#ifdef DEBUG #ifdef DEBUG
TEST_F(PrefinalizerTest, PrefinalizerFailsOnAllcoation) { TEST_F(PrefinalizerDeathTest, PrefinalizerFailsOnAllcoation) {
auto* object = MakeGarbageCollected<AllocatingPrefinalizer>( auto* object = MakeGarbageCollected<AllocatingPrefinalizer>(
GetAllocationHandle(), GetHeap()); GetAllocationHandle(), GetHeap());
USE(object); USE(object);
...@@ -269,5 +257,69 @@ TEST_F(PrefinalizerTest, PrefinalizerFailsOnAllcoation) { ...@@ -269,5 +257,69 @@ TEST_F(PrefinalizerTest, PrefinalizerFailsOnAllcoation) {
#endif // DEBUG #endif // DEBUG
namespace {
template <template <typename T> class RefType>
class RessurectingPrefinalizer
: public GarbageCollected<RessurectingPrefinalizer<RefType>> {
CPPGC_USING_PRE_FINALIZER(RessurectingPrefinalizer, PreFinalizer);
public:
explicit RessurectingPrefinalizer(RefType<GCed>& ref, GCed* obj)
: ref_(ref), obj_(obj) {}
void Trace(Visitor*) const {}
void PreFinalizer() { ref_ = obj_; }
private:
RefType<GCed>& ref_;
GCed* obj_;
};
class GCedHolder : public GarbageCollected<GCedHolder> {
public:
void Trace(Visitor* v) const { v->Trace(member_); }
Member<GCed> member_;
};
} // namespace
#if V8_ENABLE_CHECKS
#ifdef CPPGC_CHECK_ASSIGNMENTS_IN_PREFINALIZERS
TEST_F(PrefinalizerDeathTest, PrefinalizerCantRewireGraphWithDeadObjects) {
Persistent<LinkedNode> root{MakeGarbageCollected<LinkedNode>(
GetAllocationHandle(),
MakeGarbageCollected<LinkedNode>(
GetAllocationHandle(),
MakeGarbageCollected<LinkedNode>(GetAllocationHandle(), nullptr)))};
CHECK(root->next());
MakeGarbageCollected<MutatingPrefinalizer>(GetAllocationHandle(), root.Get());
// All LinkedNode objects will die on the following GC. The pre-finalizer may
// still operate with them but not add them to a live object.
root.Clear();
EXPECT_DEATH_IF_SUPPORTED(PreciseGC(), "");
}
TEST_F(PrefinalizerDeathTest, PrefinalizerCantRessurectObjectOnStack) {
Persistent<GCed> persistent;
MakeGarbageCollected<RessurectingPrefinalizer<Persistent>>(
GetAllocationHandle(), persistent,
MakeGarbageCollected<GCed>(GetAllocationHandle()));
EXPECT_DEATH_IF_SUPPORTED(PreciseGC(), "");
}
TEST_F(PrefinalizerDeathTest, PrefinalizerCantRessurectObjectOnHeap) {
Persistent<GCedHolder> persistent(
MakeGarbageCollected<GCedHolder>(GetAllocationHandle()));
MakeGarbageCollected<RessurectingPrefinalizer<Member>>(
GetAllocationHandle(), persistent->member_,
MakeGarbageCollected<GCed>(GetAllocationHandle()));
EXPECT_DEATH_IF_SUPPORTED(PreciseGC(), "");
}
#endif // CPPGC_CHECK_ASSIGNMENTS_IN_PREFINALIZERS
#endif // V8_ENABLE_CHECKS
} // namespace internal } // namespace internal
} // namespace cppgc } // namespace cppgc
...@@ -245,6 +245,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) ...@@ -245,6 +245,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(CPPGC_ENABLE_OBJECT_NAMES "Enable object names in cppgc for debug purposes" OFF) option(CPPGC_ENABLE_OBJECT_NAMES "Enable object names in cppgc for debug purposes" OFF)
option(CPPGC_ENABLE_CAGED_HEAP "Enable heap reservation of size 4GB, only possible for 64bit archs" OFF) option(CPPGC_ENABLE_CAGED_HEAP "Enable heap reservation of size 4GB, only possible for 64bit archs" OFF)
option(CPPGC_ENABLE_VERIFY_LIVE_BYTES " Enable verification of live bytes in the marking verifier" OFF) option(CPPGC_ENABLE_VERIFY_LIVE_BYTES " Enable verification of live bytes in the marking verifier" OFF)
option(CPPGC_CHECK_ASSIGNMENTS_IN_PREFINALIZERS " Enable assignment checks for Members/Persistents during prefinalizer invocations" OFF)
option(CPPGC_ENABLE_YOUNG_GENERATION "Enable young generation in cppgc" OFF) option(CPPGC_ENABLE_YOUNG_GENERATION "Enable young generation in cppgc" OFF)
set(CPPGC_TARGET_ARCH "x64" CACHE STRING "Target architecture, possible options: x64, x86, arm, arm64, ppc64, s390x, mipsel, mips64el") set(CPPGC_TARGET_ARCH "x64" CACHE STRING "Target architecture, possible options: x64, x86, arm, arm64, ppc64, s390x, mipsel, mips64el")
...@@ -437,6 +438,9 @@ endif() ...@@ -437,6 +438,9 @@ endif()
if(CPPGC_ENABLE_VERIFY_LIVE_BYTES) if(CPPGC_ENABLE_VERIFY_LIVE_BYTES)
target_compile_definitions({target.name} PRIVATE "-DCPPGC_VERIFY_LIVE_BYTES") target_compile_definitions({target.name} PRIVATE "-DCPPGC_VERIFY_LIVE_BYTES")
endif() endif()
if(CPPGC_CHECK_ASSIGNMENTS_IN_PREFINALIZERS)
target_compile_definitions({target.name} PRIVATE "-DCPPGC_CHECK_ASSIGNMENTS_IN_PREFINALIZERS")
endif()
if(CPPGC_ENABLE_YOUNG_GENERATION) if(CPPGC_ENABLE_YOUNG_GENERATION)
target_compile_definitions({target.name} PRIVATE "-DCPPGC_YOUNG_GENERATION") target_compile_definitions({target.name} PRIVATE "-DCPPGC_YOUNG_GENERATION")
endif()""" endif()"""
......
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