Add basic heap corruption detection ConcurrentCopying::Copy
Detect objects that have a null class. This also detects objects that
are not in the from-space allocated area since this area is zero
initialized.
Test: test-art-host
Bug: 37683299
Bug: 12687968
Bug: 37187694
Change-Id: Ib7b9a1913a582692ce05791104c181b75e0f7bcb
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index dd449f9..d5c36bf 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -130,7 +130,7 @@
mirror::Object* to_ref = GetFwdPtr(from_ref);
if (to_ref == nullptr) {
// It isn't marked yet. Mark it by copying it to the to-space.
- to_ref = Copy(from_ref);
+ to_ref = Copy(from_ref, holder, offset);
}
DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
<< "from_ref=" << from_ref << " to_ref=" << to_ref;
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index e193d2a..015e6ba 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2152,8 +2152,16 @@
return reinterpret_cast<mirror::Object*>(addr);
}
-mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref) {
+mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,
+ mirror::Object* holder,
+ MemberOffset offset) {
DCHECK(region_space_->IsInFromSpace(from_ref));
+ // If the class pointer is null, the object is invalid. This could occur for a dangling pointer
+ // from a previous GC that is either inside or outside the allocated region.
+ mirror::Class* klass = from_ref->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (UNLIKELY(klass == nullptr)) {
+ heap_->GetVerification()->LogHeapCorruption(holder, offset, from_ref, /* fatal */ true);
+ }
// There must not be a read barrier to avoid nested RB that might violate the to-space invariant.
// Note that from_ref is a from space ref so the SizeOf() call will access the from-space meta
// objects, but it's ok and necessary.
@@ -2208,7 +2216,7 @@
DCHECK(to_ref != nullptr);
// Copy the object excluding the lock word since that is handled in the loop.
- to_ref->SetClass(from_ref->GetClass<kVerifyNone, kWithoutReadBarrier>());
+ to_ref->SetClass(klass);
const size_t kObjectHeaderSize = sizeof(mirror::Object);
DCHECK_GE(obj_size, kObjectHeaderSize);
static_assert(kObjectHeaderSize == sizeof(mirror::HeapReference<mirror::Class>) +
@@ -2416,7 +2424,7 @@
if (is_los && !IsAligned<kPageSize>(ref)) {
// Ref is a large object that is not aligned, it must be heap corruption. Dump data before
// AtomicSetReadBarrierState since it will fault if the address is not valid.
- heap_->GetVerification()->LogHeapCorruption(ref, offset, holder, /* fatal */ true);
+ heap_->GetVerification()->LogHeapCorruption(holder, offset, ref, /* fatal */ true);
}
// Not marked or on the allocation stack. Try to mark it.
// This may or may not succeed, which is ok.
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index f877314..37b6a2c 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -133,7 +133,10 @@
private:
void PushOntoMarkStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
- mirror::Object* Copy(mirror::Object* from_ref) REQUIRES_SHARED(Locks::mutator_lock_)
+ mirror::Object* Copy(mirror::Object* from_ref,
+ mirror::Object* holder,
+ MemberOffset offset)
+ REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
void Scan(mirror::Object* to_ref) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);