Commit 54c69fc5 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[heap] Verify the shared heap before tearing down a client heap

In the case of bugs creating shared->local edges, this lets us catch
dangling pointers via CHECKs before they happen.

Also removed some redundant checks in the shared struct verifier.
Existing heap verification already checks that all of a Heap's pointers
are contained within it.

Bug: v8:12547
Change-Id: Ic7a007b3b6559e3dfd0286fbf869586023c6f801
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3704911Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81335}
parent 358ff9bc
...@@ -327,6 +327,8 @@ void HeapObject::HeapObjectVerify(Isolate* isolate) { ...@@ -327,6 +327,8 @@ void HeapObject::HeapObjectVerify(Isolate* isolate) {
// static // static
void HeapObject::VerifyHeapPointer(Isolate* isolate, Object p) { void HeapObject::VerifyHeapPointer(Isolate* isolate, Object p) {
CHECK(p.IsHeapObject()); CHECK(p.IsHeapObject());
// If you crashed here and {isolate->is_shared()}, there is a bug causing the
// host of {p} to point to a non-shared object.
CHECK(IsValidHeapObject(isolate->heap(), HeapObject::cast(p))); CHECK(IsValidHeapObject(isolate->heap(), HeapObject::cast(p)));
CHECK_IMPLIES(V8_EXTERNAL_CODE_SPACE_BOOL, !p.IsCode()); CHECK_IMPLIES(V8_EXTERNAL_CODE_SPACE_BOOL, !p.IsCode());
} }
...@@ -1238,6 +1240,7 @@ USE_TORQUE_VERIFIER(JSWrappedFunction) ...@@ -1238,6 +1240,7 @@ USE_TORQUE_VERIFIER(JSWrappedFunction)
void JSSharedStruct::JSSharedStructVerify(Isolate* isolate) { void JSSharedStruct::JSSharedStructVerify(Isolate* isolate) {
CHECK(IsJSSharedStruct()); CHECK(IsJSSharedStruct());
CHECK(InSharedWritableHeap());
JSObjectVerify(isolate); JSObjectVerify(isolate);
CHECK(HasFastProperties()); CHECK(HasFastProperties());
// Shared structs can only point to primitives or other shared HeapObjects, // Shared structs can only point to primitives or other shared HeapObjects,
...@@ -1258,10 +1261,9 @@ void JSSharedStruct::JSSharedStructVerify(Isolate* isolate) { ...@@ -1258,10 +1261,9 @@ void JSSharedStruct::JSSharedStructVerify(Isolate* isolate) {
void JSAtomicsMutex::JSAtomicsMutexVerify(Isolate* isolate) { void JSAtomicsMutex::JSAtomicsMutexVerify(Isolate* isolate) {
CHECK(IsJSAtomicsMutex()); CHECK(IsJSAtomicsMutex());
CHECK(InSharedHeap()); CHECK(InSharedWritableHeap());
JSObjectVerify(isolate); JSObjectVerify(isolate);
Map mutex_map = map(); Map mutex_map = map();
CHECK(mutex_map.InSharedHeap());
CHECK(mutex_map.GetBackPointer().IsUndefined(isolate)); CHECK(mutex_map.GetBackPointer().IsUndefined(isolate));
CHECK(!mutex_map.is_extensible()); CHECK(!mutex_map.is_extensible());
CHECK(!mutex_map.is_prototype_map()); CHECK(!mutex_map.is_prototype_map());
......
...@@ -4609,6 +4609,26 @@ void Heap::VerifyReadOnlyHeap() { ...@@ -4609,6 +4609,26 @@ void Heap::VerifyReadOnlyHeap() {
read_only_space_->Verify(isolate()); read_only_space_->Verify(isolate());
} }
void Heap::VerifySharedHeap(Isolate* initiator) {
DCHECK(IsShared());
// Stop all client isolates attached to this isolate.
GlobalSafepointScope global_safepoint(initiator);
// Migrate shared isolate to the main thread of the initiator isolate.
v8::Locker locker(reinterpret_cast<v8::Isolate*>(isolate()));
v8::Isolate::Scope isolate_scope(reinterpret_cast<v8::Isolate*>(isolate()));
DCHECK_NOT_NULL(isolate()->global_safepoint());
// Free all shared LABs to make the shared heap iterable.
isolate()->global_safepoint()->IterateClientIsolates([](Isolate* client) {
client->heap()->FreeSharedLinearAllocationAreas();
});
Verify();
}
class SlotVerifyingVisitor : public ObjectVisitorWithCageBases { class SlotVerifyingVisitor : public ObjectVisitorWithCageBases {
public: public:
SlotVerifyingVisitor(Isolate* isolate, std::set<Address>* untyped, SlotVerifyingVisitor(Isolate* isolate, std::set<Address>* untyped,
...@@ -6037,9 +6057,14 @@ void Heap::StartTearDown() { ...@@ -6037,9 +6057,14 @@ void Heap::StartTearDown() {
// tear down parts of the Isolate. // tear down parts of the Isolate.
if (FLAG_verify_heap) { if (FLAG_verify_heap) {
AllowGarbageCollection allow_gc; AllowGarbageCollection allow_gc;
IgnoreLocalGCRequests ignore_gc_requests(this);
SafepointScope scope(this);
Verify(); Verify();
// If this is a client Isolate of a shared Isolate, verify that there are no
// shared-to-local pointers before tearing down the client Isolate and
// creating dangling pointers.
if (Isolate* shared_isolate = isolate()->shared_isolate()) {
shared_isolate->heap()->VerifySharedHeap(isolate());
}
} }
#endif #endif
} }
......
...@@ -1617,9 +1617,15 @@ class Heap { ...@@ -1617,9 +1617,15 @@ class Heap {
#ifdef VERIFY_HEAP #ifdef VERIFY_HEAP
// Verify the heap is in its normal state before or after a GC. // Verify the heap is in its normal state before or after a GC.
V8_EXPORT_PRIVATE void Verify(); V8_EXPORT_PRIVATE void Verify();
// Verify the read-only heap after all read-only heap objects have been // Verify the read-only heap after all read-only heap objects have been
// created. // created.
void VerifyReadOnlyHeap(); void VerifyReadOnlyHeap();
// Verify the shared heap, initiating from a client heap. This performs a
// global safepoint, then the normal heap verification.
void VerifySharedHeap(Isolate* initiator);
void VerifyRememberedSetFor(HeapObject object); void VerifyRememberedSetFor(HeapObject object);
// Verify that cached size of invalidated object is up-to-date. // Verify that cached size of invalidated object is up-to-date.
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "src/common/assert-scope.h" #include "src/common/assert-scope.h"
#include "src/debug/debug.h" #include "src/debug/debug.h"
#include "src/heap/heap-inl.h" #include "src/heap/heap-inl.h"
#include "src/heap/parked-scope.h"
#include "src/heap/read-only-heap.h" #include "src/heap/read-only-heap.h"
#include "src/heap/safepoint.h" #include "src/heap/safepoint.h"
#include "src/heap/spaces.h" #include "src/heap/spaces.h"
...@@ -5040,7 +5041,13 @@ UNINITIALIZED_TEST(SharedStrings) { ...@@ -5040,7 +5041,13 @@ UNINITIALIZED_TEST(SharedStrings) {
CheckObjectsAreInSharedHeap(i_isolate1); CheckObjectsAreInSharedHeap(i_isolate1);
CheckObjectsAreInSharedHeap(i_isolate2); CheckObjectsAreInSharedHeap(i_isolate2);
isolate1->Dispose(); {
// Because both isolate1 and isolate2 are considered running on the main
// thread, one must be parked to avoid deadlock in the shared heap
// verification that may happen on client heap disposal.
ParkedScope parked(i_isolate2->main_thread_local_isolate());
isolate1->Dispose();
}
isolate2->Dispose(); isolate2->Dispose();
Isolate::Delete(reinterpret_cast<Isolate*>(shared_isolate)); Isolate::Delete(reinterpret_cast<Isolate*>(shared_isolate));
......
...@@ -21,6 +21,27 @@ struct V8_NODISCARD IsolateWrapper { ...@@ -21,6 +21,27 @@ struct V8_NODISCARD IsolateWrapper {
v8::Isolate* const isolate; v8::Isolate* const isolate;
}; };
// Some tests in this file allocate two Isolates in the same thread to directly
// test shared string behavior. Because both are considered running, when
// disposing these Isolates, one must be parked to not cause a deadlock in the
// shared heap verification that happens on client Isolate disposal.
struct V8_NODISCARD IsolatePairWrapper {
IsolatePairWrapper(v8::Isolate* isolate1, v8::Isolate* isolate2)
: isolate1(isolate1), isolate2(isolate2) {}
~IsolatePairWrapper() {
{
i::ParkedScope parked(
reinterpret_cast<Isolate*>(isolate1)->main_thread_local_isolate());
isolate2->Dispose();
}
isolate1->Dispose();
}
v8::Isolate* const isolate1;
v8::Isolate* const isolate2;
};
class MultiClientIsolateTest { class MultiClientIsolateTest {
public: public:
MultiClientIsolateTest() { MultiClientIsolateTest() {
...@@ -111,10 +132,10 @@ UNINITIALIZED_TEST(InPlaceInternalization) { ...@@ -111,10 +132,10 @@ UNINITIALIZED_TEST(InPlaceInternalization) {
FLAG_shared_string_table = true; FLAG_shared_string_table = true;
MultiClientIsolateTest test; MultiClientIsolateTest test;
IsolateWrapper isolate1_wrapper(test.NewClientIsolate()); IsolatePairWrapper isolates_wrapper(test.NewClientIsolate(),
IsolateWrapper isolate2_wrapper(test.NewClientIsolate()); test.NewClientIsolate());
v8::Isolate* isolate1 = isolate1_wrapper.isolate; v8::Isolate* isolate1 = isolates_wrapper.isolate1;
v8::Isolate* isolate2 = isolate2_wrapper.isolate; v8::Isolate* isolate2 = isolates_wrapper.isolate2;
Isolate* i_isolate1 = reinterpret_cast<Isolate*>(isolate1); Isolate* i_isolate1 = reinterpret_cast<Isolate*>(isolate1);
Factory* factory1 = i_isolate1->factory(); Factory* factory1 = i_isolate1->factory();
Isolate* i_isolate2 = reinterpret_cast<Isolate*>(isolate2); Isolate* i_isolate2 = reinterpret_cast<Isolate*>(isolate2);
...@@ -179,10 +200,10 @@ UNINITIALIZED_TEST(YoungInternalization) { ...@@ -179,10 +200,10 @@ UNINITIALIZED_TEST(YoungInternalization) {
FLAG_shared_string_table = true; FLAG_shared_string_table = true;
MultiClientIsolateTest test; MultiClientIsolateTest test;
IsolateWrapper isolate1_wrapper(test.NewClientIsolate()); IsolatePairWrapper isolates_wrapper(test.NewClientIsolate(),
IsolateWrapper isolate2_wrapper(test.NewClientIsolate()); test.NewClientIsolate());
v8::Isolate* isolate1 = isolate1_wrapper.isolate; v8::Isolate* isolate1 = isolates_wrapper.isolate1;
v8::Isolate* isolate2 = isolate2_wrapper.isolate; v8::Isolate* isolate2 = isolates_wrapper.isolate2;
Isolate* i_isolate1 = reinterpret_cast<Isolate*>(isolate1); Isolate* i_isolate1 = reinterpret_cast<Isolate*>(isolate1);
Factory* factory1 = i_isolate1->factory(); Factory* factory1 = i_isolate1->factory();
Isolate* i_isolate2 = reinterpret_cast<Isolate*>(isolate2); Isolate* i_isolate2 = reinterpret_cast<Isolate*>(isolate2);
......
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