1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "runtime/include/runtime.h"
17 #include "runtime/include/panda_vm.h"
18 #include "runtime/mem/gc/gc_root.h"
19 #include "runtime/mem/heap_verifier.h"
20
21 namespace ark::mem {
22
23 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
24 #define LOG_HEAP_VERIFIER LOG(ERROR, GC) << "HEAP_VERIFIER: "
25
26 // Should be called only with MutatorLock held
27 template <class LanguageConfig>
VerifyAllPaused() const28 size_t HeapVerifier<LanguageConfig>::VerifyAllPaused() const
29 {
30 Rendezvous *rendezvous = Thread::GetCurrent()->GetVM()->GetRendezvous();
31 rendezvous->SafepointBegin();
32 size_t failCount = VerifyAll();
33 rendezvous->SafepointEnd();
34 return failCount;
35 }
36
37 template <LangTypeT LANG_TYPE>
operator ()(ObjectHeader * obj)38 void HeapObjectVerifier<LANG_TYPE>::operator()(ObjectHeader *obj)
39 {
40 HeapReferenceVerifier<LANG_TYPE> refVerifier(heap_, failCount_);
41 ObjectHelpers<LANG_TYPE>::TraverseAllObjects(obj, refVerifier);
42 }
43
44 template <LangTypeT LANG_TYPE>
operator ()(ObjectHeader * objectHeader,ObjectHeader * referent)45 void HeapReferenceVerifier<LANG_TYPE>::operator()([[maybe_unused]] ObjectHeader *objectHeader, ObjectHeader *referent)
46 {
47 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
48 if constexpr (LANG_TYPE == LANG_TYPE_DYNAMIC) {
49 // Weak reference can be passed here, need to resolve the referent
50 coretypes::TaggedValue value(referent);
51 if (value.IsWeak()) {
52 referent = value.GetWeakReferent();
53 }
54 }
55 if (!heap_->IsLiveObject(referent)) {
56 LOG_HEAP_VERIFIER << "Heap corruption found! Heap object " << std::hex << objectHeader
57 << " references a dead object at " << referent;
58 ++(*failCount_);
59 } else if (referent->IsForwarded()) {
60 LOG_HEAP_VERIFIER << "Heap corruption found! Heap object " << std::hex << objectHeader
61 << " references a forwarded object at " << referent;
62 ++(*failCount_);
63 }
64 }
65
66 template <LangTypeT LANG_TYPE>
operator ()(const GCRoot & root)67 void HeapReferenceVerifier<LANG_TYPE>::operator()(const GCRoot &root)
68 {
69 auto referent = root.GetObjectHeader();
70 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
71 if constexpr (LANG_TYPE == LANG_TYPE_DYNAMIC) {
72 // Weak reference can be passed here, need to resolve the referent
73 coretypes::TaggedValue value(referent);
74 if (value.IsWeak()) {
75 referent = value.GetWeakReferent();
76 }
77 }
78 if (!heap_->IsLiveObject(referent)) {
79 LOG_HEAP_VERIFIER << "Heap corruption found! Root references a dead object at " << std::hex << referent;
80 ++(*failCount_);
81 } else if (referent->IsForwarded()) {
82 LOG_HEAP_VERIFIER << "Heap corruption found! Root references a forwarded object at " << std::hex << referent;
83 ++(*failCount_);
84 }
85 }
86
87 template <class LanguageConfig>
IsValidObjectAddress(void * addr) const88 bool HeapVerifier<LanguageConfig>::IsValidObjectAddress(void *addr) const
89 {
90 return IsAligned<DEFAULT_ALIGNMENT_IN_BYTES>(ToUintPtr(addr)) && IsHeapAddress(addr);
91 }
92
93 template <class LanguageConfig>
IsHeapAddress(void * addr) const94 bool HeapVerifier<LanguageConfig>::IsHeapAddress(void *addr) const
95 {
96 return heap_->ContainObject(reinterpret_cast<ObjectHeader *>(addr));
97 }
98
99 template <class LanguageConfig>
VerifyHeap() const100 size_t HeapVerifier<LanguageConfig>::VerifyHeap() const
101 {
102 return heap_->VerifyHeapReferences();
103 }
104
105 template <class LanguageConfig>
VerifyRoot() const106 size_t HeapVerifier<LanguageConfig>::VerifyRoot() const
107 {
108 RootManager<LanguageConfig> rootManager(heap_->GetPandaVM());
109 size_t failCount = 0;
110 rootManager.VisitNonHeapRoots([this, &failCount](const GCRoot &root) {
111 if (root.GetType() == RootType::ROOT_FRAME || root.GetType() == RootType::ROOT_THREAD) {
112 auto *baseCls = root.GetObjectHeader()->ClassAddr<BaseClass>();
113 if (baseCls == nullptr) {
114 LOG_HEAP_VERIFIER << "Heap corruption found! Class address for root " << std::hex
115 << root.GetObjectHeader() << " is null";
116 ++failCount;
117 } else if (!(!baseCls->IsDynamicClass() && static_cast<Class *>(baseCls)->IsClassClass())) {
118 HeapReferenceVerifier<LanguageConfig::LANG_TYPE>(heap_, &failCount)(root);
119 }
120 }
121 });
122
123 return failCount;
124 }
125
126 template <class LanguageConfig>
GetClassName(const ObjectHeader * obj)127 static PandaString GetClassName(const ObjectHeader *obj)
128 {
129 if constexpr (LanguageConfig::LANG_TYPE == LANG_TYPE_STATIC) {
130 auto *cls = obj->template ClassAddr<Class>();
131 if (IsAddressInObjectsHeap(cls)) {
132 return cls->GetName().c_str(); // NOLINT(readability-redundant-string-cstr)
133 }
134 }
135 return "unknown class";
136 }
137
138 template <class LanguageConfig>
CheckHeap(const PandaUnorderedSet<const ObjectHeader * > & heapObjects,const PandaVector<ObjectCache> & referentObjects) const139 size_t FastHeapVerifier<LanguageConfig>::CheckHeap(const PandaUnorderedSet<const ObjectHeader *> &heapObjects,
140 const PandaVector<ObjectCache> &referentObjects) const
141 {
142 size_t failsCount = 0;
143 for (auto objectCache : referentObjects) {
144 if (heapObjects.find(objectCache.referent) == heapObjects.end()) {
145 LOG_HEAP_VERIFIER << "Heap object " << std::hex << objectCache.heapObject << " ("
146 << GetClassName<LanguageConfig>(objectCache.heapObject)
147 << ") references a dead object at " << objectCache.referent << " ("
148 << GetClassName<LanguageConfig>(objectCache.referent) << ")";
149 ++failsCount;
150 }
151 }
152 return failsCount;
153 }
154
155 template <class LanguageConfig>
VerifyAll() const156 size_t FastHeapVerifier<LanguageConfig>::VerifyAll() const
157 {
158 PandaUnorderedSet<const ObjectHeader *> heapObjects;
159 PandaVector<ObjectCache> referentObjects;
160 size_t failsCount = 0;
161
162 auto lazyVerify = [&heapObjects, &referentObjects, &failsCount](const ObjectHeader *objectHeader,
163 const ObjectHeader *referent) {
164 // Lazy verify during heap objects collection
165 if (heapObjects.find(referent) == heapObjects.end()) {
166 referentObjects.push_back(ObjectCache({objectHeader, referent}));
167 }
168 if (objectHeader->IsForwarded()) {
169 LOG_HEAP_VERIFIER << "Heap object " << std::hex << objectHeader << " is forwarded object";
170 ++failsCount;
171 }
172 auto *classAddr = objectHeader->ClassAddr<BaseClass>();
173 if (!IsAddressInObjectsHeap(classAddr)) {
174 LOG_HEAP_VERIFIER << "Heap object " << std::hex << objectHeader
175 << " has non-heap class address: " << classAddr;
176 ++failsCount;
177 }
178 };
179 const std::function<void(ObjectHeader *, ObjectHeader *)> lazyVerifyFunctor(lazyVerify);
180 auto collectObjects = [&heapObjects, &lazyVerifyFunctor](ObjectHeader *object) {
181 heapObjects.insert(object);
182 ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjects(object, lazyVerifyFunctor);
183 };
184
185 // Heap objects verifier
186
187 // Add strings from string table because these objects are like a phoenix.
188 // A string object may exist but there are no live references to it (no bit set in the live bitmap).
189 // But later code may reuse it by calling StringTable::GetOrInternString so this string
190 // get alive. That is why we mark all strings as alive by visiting the string table.
191 Thread::GetCurrent()->GetVM()->VisitStrings(collectObjects);
192 heap_->IterateOverObjects(collectObjects);
193 failsCount += this->CheckHeap(heapObjects, referentObjects);
194 // Stack verifier
195 RootManager<LanguageConfig> rootManager(heap_->GetPandaVM());
196 auto rootVerifier = [&heapObjects, &failsCount](const GCRoot &root) {
197 const auto *rootObjHeader = root.GetObjectHeader();
198 auto *baseCls = rootObjHeader->ClassAddr<BaseClass>();
199 if (!IsAddressInObjectsHeap(ToUintPtr(baseCls))) {
200 LOG_HEAP_VERIFIER << "Class address for root " << std::hex << rootObjHeader
201 << " is not in objects heap: " << baseCls;
202 ++failsCount;
203 } else if (baseCls->IsDynamicClass() || !static_cast<Class *>(baseCls)->IsClassClass()) {
204 if (heapObjects.find(rootObjHeader) == heapObjects.end()) {
205 LOG_HEAP_VERIFIER << "Root references a dead object at " << std::hex << rootObjHeader;
206 ++failsCount;
207 }
208 }
209 };
210 rootManager.VisitLocalRoots(rootVerifier);
211
212 return failsCount;
213 }
214
ObjectVerificationInfo(ObjectHeader * referent)215 ObjectVerificationInfo::ObjectVerificationInfo(ObjectHeader *referent)
216 : classAddress_(referent->ClassAddr<void *>()), oldAddress_(referent)
217 {
218 }
219
VerifyUpdatedRef(ObjectHeader * objectHeader,ObjectHeader * updatedRef,bool inAliveSpace) const220 bool ObjectVerificationInfo::VerifyUpdatedRef(ObjectHeader *objectHeader, ObjectHeader *updatedRef,
221 bool inAliveSpace) const
222 {
223 ObjectHeader *correctAddress = oldAddress_;
224 if (!inAliveSpace) {
225 if (!oldAddress_->IsForwarded()) {
226 LOG_HEAP_VERIFIER << "Object " << std::hex << objectHeader << " had reference " << oldAddress_
227 << ", which is not forwarded, new reference address: " << updatedRef;
228 return false;
229 }
230 correctAddress = GetForwardAddress(oldAddress_);
231 }
232 if (correctAddress != updatedRef) {
233 LOG_HEAP_VERIFIER << "Object " << std::hex << objectHeader << " has incorrect updated reference " << updatedRef
234 << ", correct address: " << correctAddress;
235 return false;
236 }
237 void *newClassAddr = updatedRef->ClassAddr<void *>();
238 if (newClassAddr != classAddress_) {
239 LOG_HEAP_VERIFIER << "Object " << std::hex << objectHeader << " has incorrect class address (" << newClassAddr
240 << ") in updated reference " << updatedRef
241 << ", class address before collection: " << classAddress_;
242 return false;
243 }
244
245 return true;
246 }
247
248 template <class LanguageConfig>
InCollectableSpace(const ObjectHeader * object) const249 bool HeapVerifierIntoGC<LanguageConfig>::InCollectableSpace(const ObjectHeader *object) const
250 {
251 for (const auto &memRange : this->collectableMemRanges_) {
252 if (memRange.Contains(ToUintPtr(object))) {
253 return true;
254 }
255 }
256 return false;
257 }
258
259 template <class LanguageConfig>
InAliveSpace(const ObjectHeader * object) const260 bool HeapVerifierIntoGC<LanguageConfig>::InAliveSpace(const ObjectHeader *object) const
261 {
262 for (const auto &memRange : this->aliveMemRanges_) {
263 if (memRange.Contains(ToUintPtr(object))) {
264 return true;
265 }
266 }
267 return false;
268 }
269
270 template <class LanguageConfig>
AddToVerificationInfo(RefsVerificationInfo & verificationInfo,size_t refNumber,ObjectHeader * objectHeader,ObjectHeader * referent)271 void HeapVerifierIntoGC<LanguageConfig>::AddToVerificationInfo(RefsVerificationInfo &verificationInfo, size_t refNumber,
272 ObjectHeader *objectHeader, ObjectHeader *referent)
273 {
274 if (this->InCollectableSpace(referent)) {
275 ObjectVerificationInfo objInfo(referent);
276 auto it = verificationInfo.find(objectHeader);
277 if (it != verificationInfo.end()) {
278 it->second.insert({refNumber, objInfo});
279 } else {
280 verificationInfo.insert({objectHeader, VerifyingRefs({{refNumber, objInfo}})});
281 }
282 }
283 }
284
285 template <class LanguageConfig>
CollectVerificationInfo(PandaVector<MemRange> && collectableMemRanges)286 void HeapVerifierIntoGC<LanguageConfig>::CollectVerificationInfo(PandaVector<MemRange> &&collectableMemRanges)
287 {
288 size_t refNumber = 0;
289 collectableMemRanges_ = std::move(collectableMemRanges);
290 const std::function<void(ObjectHeader *, ObjectHeader *)> refsCollector =
291 [this, &refNumber](ObjectHeader *objectHeader, ObjectHeader *referent) {
292 if (this->InCollectableSpace(objectHeader)) {
293 this->AddToVerificationInfo(this->collectableVerificationInfo_, refNumber, objectHeader, referent);
294 } else {
295 this->AddToVerificationInfo(this->permanentVerificationInfo_, refNumber, objectHeader, referent);
296 }
297 ++refNumber;
298 };
299
300 auto collectFunctor = [&refNumber, &refsCollector](ObjectHeader *object) {
301 if (object->IsMarkedForGC<false>()) {
302 refNumber = 0;
303 ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjects(object, refsCollector);
304 }
305 };
306 heap_->IterateOverObjects(collectFunctor);
307 }
308
309 template <class LanguageConfig>
VerifyAll(PandaVector<MemRange> && aliveMemRanges)310 size_t HeapVerifierIntoGC<LanguageConfig>::VerifyAll(PandaVector<MemRange> &&aliveMemRanges)
311 {
312 size_t failsCount = 0U;
313 size_t refNumber = 0U;
314 aliveMemRanges_ = std::move(aliveMemRanges);
315 auto it = permanentVerificationInfo_.begin();
316 for (auto &info : collectableVerificationInfo_) {
317 ObjectHeader *obj = info.first;
318 if (obj->IsForwarded()) {
319 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
320 permanentVerificationInfo_[GetForwardAddress(obj)] = std::move(info.second);
321 } else if (this->InAliveSpace(obj)) {
322 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
323 permanentVerificationInfo_[obj] = std::move(info.second);
324 }
325 }
326 collectableVerificationInfo_.clear();
327 const std::function<void(ObjectHeader *, ObjectHeader *)> nonYoungChecker =
328 [this, &failsCount](const ObjectHeader *objectHeader, const ObjectHeader *referent) {
329 if (this->InCollectableSpace(referent)) {
330 LOG_HEAP_VERIFIER << "Object " << std::hex << objectHeader << " references a dead object " << referent
331 << " after collection";
332 ++failsCount;
333 }
334 };
335 const std::function<void(ObjectHeader *, ObjectHeader *)> sameObjChecker =
336 [this, &nonYoungChecker, &refNumber, &failsCount, &it](ObjectHeader *objectHeader, ObjectHeader *referent) {
337 auto refIt = it->second.find(refNumber);
338 if (refIt != it->second.end()) {
339 if (!refIt->second.VerifyUpdatedRef(objectHeader, referent, this->InAliveSpace(referent))) {
340 ++failsCount;
341 }
342 } else {
343 nonYoungChecker(objectHeader, referent);
344 }
345 ++refNumber;
346 };
347 // Check references in alive objects
348 ObjectVisitor traverseAliveObj = [&nonYoungChecker, &sameObjChecker, &refNumber, this, &it](ObjectHeader *object) {
349 if (!object->IsMarkedForGC<false>() || (this->InCollectableSpace(object) && !this->InAliveSpace(object))) {
350 return;
351 }
352 it = this->permanentVerificationInfo_.find(object);
353 if (it == this->permanentVerificationInfo_.end()) {
354 ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjects(object, nonYoungChecker);
355 } else {
356 refNumber = 0U;
357 ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjects(object, sameObjChecker);
358 }
359 };
360 heap_->IterateOverObjects(traverseAliveObj);
361 return failsCount;
362 }
363
364 TEMPLATE_CLASS_LANGUAGE_CONFIG(HeapVerifier);
365 TEMPLATE_CLASS_LANGUAGE_CONFIG(FastHeapVerifier);
366 TEMPLATE_CLASS_LANGUAGE_CONFIG(HeapVerifierIntoGC);
367 } // namespace ark::mem
368