• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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;
109     size_t failCount = 0;
110     rootManager.SetPandaVM(heap_->GetPandaVM());
111     rootManager.VisitNonHeapRoots([this, &failCount](const GCRoot &root) {
112         if (root.GetType() == RootType::ROOT_FRAME || root.GetType() == RootType::ROOT_THREAD) {
113             auto *baseCls = root.GetObjectHeader()->ClassAddr<BaseClass>();
114             if (baseCls == nullptr) {
115                 LOG_HEAP_VERIFIER << "Heap corruption found! Class address for root " << std::hex
116                                   << root.GetObjectHeader() << " is null";
117                 ++failCount;
118             } else if (!(!baseCls->IsDynamicClass() && static_cast<Class *>(baseCls)->IsClassClass())) {
119                 HeapReferenceVerifier<LanguageConfig::LANG_TYPE>(heap_, &failCount)(root);
120             }
121         }
122     });
123 
124     return failCount;
125 }
126 
127 template <class LanguageConfig>
GetClassName(const ObjectHeader * obj)128 static PandaString GetClassName(const ObjectHeader *obj)
129 {
130     if constexpr (LanguageConfig::LANG_TYPE == LANG_TYPE_STATIC) {
131         auto *cls = obj->template ClassAddr<Class>();
132         if (IsAddressInObjectsHeap(cls)) {
133             return cls->GetName().c_str();  // NOLINT(readability-redundant-string-cstr)
134         }
135     }
136     return "unknown class";
137 }
138 
139 template <class LanguageConfig>
CheckHeap(const PandaUnorderedSet<const ObjectHeader * > & heapObjects,const PandaVector<ObjectCache> & referentObjects) const140 size_t FastHeapVerifier<LanguageConfig>::CheckHeap(const PandaUnorderedSet<const ObjectHeader *> &heapObjects,
141                                                    const PandaVector<ObjectCache> &referentObjects) const
142 {
143     size_t failsCount = 0;
144     for (auto objectCache : referentObjects) {
145         if (heapObjects.find(objectCache.referent) == heapObjects.end()) {
146             LOG_HEAP_VERIFIER << "Heap object " << std::hex << objectCache.heapObject << " ("
147                               << GetClassName<LanguageConfig>(objectCache.heapObject)
148                               << ") references a dead object at " << objectCache.referent << " ("
149                               << GetClassName<LanguageConfig>(objectCache.referent) << ")";
150             ++failsCount;
151         }
152     }
153     return failsCount;
154 }
155 
156 template <class LanguageConfig>
VerifyAll() const157 size_t FastHeapVerifier<LanguageConfig>::VerifyAll() const
158 {
159     PandaUnorderedSet<const ObjectHeader *> heapObjects;
160     PandaVector<ObjectCache> referentObjects;
161     size_t failsCount = 0;
162 
163     auto lazyVerify = [&heapObjects, &referentObjects, &failsCount](const ObjectHeader *objectHeader,
164                                                                     const ObjectHeader *referent) {
165         // Lazy verify during heap objects collection
166         if (heapObjects.find(referent) == heapObjects.end()) {
167             referentObjects.push_back(ObjectCache({objectHeader, referent}));
168         }
169         if (objectHeader->IsForwarded()) {
170             LOG_HEAP_VERIFIER << "Heap object " << std::hex << objectHeader << " is forwarded object";
171             ++failsCount;
172         }
173         auto *classAddr = objectHeader->ClassAddr<BaseClass>();
174         if (!IsAddressInObjectsHeap(classAddr)) {
175             LOG_HEAP_VERIFIER << "Heap object " << std::hex << objectHeader
176                               << " has non-heap class address: " << classAddr;
177             ++failsCount;
178         }
179     };
180     const std::function<void(ObjectHeader *, ObjectHeader *)> lazyVerifyFunctor(lazyVerify);
181     auto collectObjects = [&heapObjects, &lazyVerifyFunctor](ObjectHeader *object) {
182         heapObjects.insert(object);
183         ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjects(object, lazyVerifyFunctor);
184     };
185 
186     // Heap objects verifier
187 
188     // Add strings from string table because these objects are like a phoenix.
189     // A string object may exist but there are no live references to it (no bit set in the live bitmap).
190     // But later code may reuse it by calling StringTable::GetOrInternString so this string
191     // get alive. That is why we mark all strings as alive by visiting the string table.
192     Thread::GetCurrent()->GetVM()->VisitStrings(collectObjects);
193     heap_->IterateOverObjects(collectObjects);
194     failsCount += this->CheckHeap(heapObjects, referentObjects);
195     // Stack verifier
196     RootManager<LanguageConfig> rootManager;
197     rootManager.SetPandaVM(heap_->GetPandaVM());
198     auto rootVerifier = [&heapObjects, &failsCount](const GCRoot &root) {
199         const auto *rootObjHeader = root.GetObjectHeader();
200         auto *baseCls = rootObjHeader->ClassAddr<BaseClass>();
201         if (!IsAddressInObjectsHeap(ToUintPtr(baseCls))) {
202             LOG_HEAP_VERIFIER << "Class address for root " << std::hex << rootObjHeader
203                               << " is not in objects heap: " << baseCls;
204             ++failsCount;
205         } else if (baseCls->IsDynamicClass() || !static_cast<Class *>(baseCls)->IsClassClass()) {
206             if (heapObjects.find(rootObjHeader) == heapObjects.end()) {
207                 LOG_HEAP_VERIFIER << "Root references a dead object at " << std::hex << rootObjHeader;
208                 ++failsCount;
209             }
210         }
211     };
212     rootManager.VisitLocalRoots(rootVerifier);
213 
214     return failsCount;
215 }
216 
ObjectVerificationInfo(ObjectHeader * referent)217 ObjectVerificationInfo::ObjectVerificationInfo(ObjectHeader *referent)
218     : classAddress_(referent->ClassAddr<void *>()), oldAddress_(referent)
219 {
220 }
221 
VerifyUpdatedRef(ObjectHeader * objectHeader,ObjectHeader * updatedRef,bool inAliveSpace) const222 bool ObjectVerificationInfo::VerifyUpdatedRef(ObjectHeader *objectHeader, ObjectHeader *updatedRef,
223                                               bool inAliveSpace) const
224 {
225     ObjectHeader *correctAddress = oldAddress_;
226     if (!inAliveSpace) {
227         if (!oldAddress_->IsForwarded()) {
228             LOG_HEAP_VERIFIER << "Object " << std::hex << objectHeader << " had reference " << oldAddress_
229                               << ", which is not forwarded, new reference address: " << updatedRef;
230             return false;
231         }
232         correctAddress = GetForwardAddress(oldAddress_);
233     }
234     if (correctAddress != updatedRef) {
235         LOG_HEAP_VERIFIER << "Object " << std::hex << objectHeader << " has incorrect updated reference " << updatedRef
236                           << ", correct address: " << correctAddress;
237         return false;
238     }
239     void *newClassAddr = updatedRef->ClassAddr<void *>();
240     if (newClassAddr != classAddress_) {
241         LOG_HEAP_VERIFIER << "Object " << std::hex << objectHeader << " has incorrect class address (" << newClassAddr
242                           << ") in updated reference " << updatedRef
243                           << ", class address before collection: " << classAddress_;
244         return false;
245     }
246 
247     return true;
248 }
249 
250 template <class LanguageConfig>
InCollectableSpace(const ObjectHeader * object) const251 bool HeapVerifierIntoGC<LanguageConfig>::InCollectableSpace(const ObjectHeader *object) const
252 {
253     for (const auto &memRange : this->collectableMemRanges_) {
254         if (memRange.Contains(ToUintPtr(object))) {
255             return true;
256         }
257     }
258     return false;
259 }
260 
261 template <class LanguageConfig>
InAliveSpace(const ObjectHeader * object) const262 bool HeapVerifierIntoGC<LanguageConfig>::InAliveSpace(const ObjectHeader *object) const
263 {
264     for (const auto &memRange : this->aliveMemRanges_) {
265         if (memRange.Contains(ToUintPtr(object))) {
266             return true;
267         }
268     }
269     return false;
270 }
271 
272 template <class LanguageConfig>
AddToVerificationInfo(RefsVerificationInfo & verificationInfo,size_t refNumber,ObjectHeader * objectHeader,ObjectHeader * referent)273 void HeapVerifierIntoGC<LanguageConfig>::AddToVerificationInfo(RefsVerificationInfo &verificationInfo, size_t refNumber,
274                                                                ObjectHeader *objectHeader, ObjectHeader *referent)
275 {
276     if (this->InCollectableSpace(referent)) {
277         ObjectVerificationInfo objInfo(referent);
278         auto it = verificationInfo.find(objectHeader);
279         if (it != verificationInfo.end()) {
280             it->second.insert({refNumber, objInfo});
281         } else {
282             verificationInfo.insert({objectHeader, VerifyingRefs({{refNumber, objInfo}})});
283         }
284     }
285 }
286 
287 template <class LanguageConfig>
CollectVerificationInfo(PandaVector<MemRange> && collectableMemRanges)288 void HeapVerifierIntoGC<LanguageConfig>::CollectVerificationInfo(PandaVector<MemRange> &&collectableMemRanges)
289 {
290     size_t refNumber = 0;
291     collectableMemRanges_ = std::move(collectableMemRanges);
292     const std::function<void(ObjectHeader *, ObjectHeader *)> refsCollector =
293         [this, &refNumber](ObjectHeader *objectHeader, ObjectHeader *referent) {
294             if (this->InCollectableSpace(objectHeader)) {
295                 this->AddToVerificationInfo(this->collectableVerificationInfo_, refNumber, objectHeader, referent);
296             } else {
297                 this->AddToVerificationInfo(this->permanentVerificationInfo_, refNumber, objectHeader, referent);
298             }
299             ++refNumber;
300         };
301 
302     auto collectFunctor = [&refNumber, &refsCollector](ObjectHeader *object) {
303         if (object->IsMarkedForGC<false>()) {
304             refNumber = 0;
305             ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjects(object, refsCollector);
306         }
307     };
308     heap_->IterateOverObjects(collectFunctor);
309 }
310 
311 template <class LanguageConfig>
VerifyAll(PandaVector<MemRange> && aliveMemRanges)312 size_t HeapVerifierIntoGC<LanguageConfig>::VerifyAll(PandaVector<MemRange> &&aliveMemRanges)
313 {
314     size_t failsCount = 0U;
315     size_t refNumber = 0U;
316     aliveMemRanges_ = std::move(aliveMemRanges);
317     auto it = permanentVerificationInfo_.begin();
318     for (auto &info : collectableVerificationInfo_) {
319         ObjectHeader *obj = info.first;
320         if (obj->IsForwarded()) {
321             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
322             permanentVerificationInfo_[GetForwardAddress(obj)] = std::move(info.second);
323         } else if (this->InAliveSpace(obj)) {
324             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
325             permanentVerificationInfo_[obj] = std::move(info.second);
326         }
327     }
328     collectableVerificationInfo_.clear();
329     const std::function<void(ObjectHeader *, ObjectHeader *)> nonYoungChecker =
330         [this, &failsCount](const ObjectHeader *objectHeader, const ObjectHeader *referent) {
331             if (this->InCollectableSpace(referent)) {
332                 LOG_HEAP_VERIFIER << "Object " << std::hex << objectHeader << " references a dead object " << referent
333                                   << " after collection";
334                 ++failsCount;
335             }
336         };
337     const std::function<void(ObjectHeader *, ObjectHeader *)> sameObjChecker =
338         [this, &nonYoungChecker, &refNumber, &failsCount, &it](ObjectHeader *objectHeader, ObjectHeader *referent) {
339             auto refIt = it->second.find(refNumber);
340             if (refIt != it->second.end()) {
341                 if (!refIt->second.VerifyUpdatedRef(objectHeader, referent, this->InAliveSpace(referent))) {
342                     ++failsCount;
343                 }
344             } else {
345                 nonYoungChecker(objectHeader, referent);
346             }
347             ++refNumber;
348         };
349     // Check references in alive objects
350     ObjectVisitor traverseAliveObj = [&nonYoungChecker, &sameObjChecker, &refNumber, this, &it](ObjectHeader *object) {
351         if (!object->IsMarkedForGC<false>() || (this->InCollectableSpace(object) && !this->InAliveSpace(object))) {
352             return;
353         }
354         it = this->permanentVerificationInfo_.find(object);
355         if (it == this->permanentVerificationInfo_.end()) {
356             ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjects(object, nonYoungChecker);
357         } else {
358             refNumber = 0U;
359             ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjects(object, sameObjChecker);
360         }
361     };
362     heap_->IterateOverObjects(traverseAliveObj);
363     return failsCount;
364 }
365 
366 TEMPLATE_CLASS_LANGUAGE_CONFIG(HeapVerifier);
367 TEMPLATE_CLASS_LANGUAGE_CONFIG(FastHeapVerifier);
368 TEMPLATE_CLASS_LANGUAGE_CONFIG(HeapVerifierIntoGC);
369 }  // namespace ark::mem
370