1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include "RetainCountDiagnostics.h" |
15 | #include "RetainCountChecker.h" |
16 | |
17 | using namespace clang; |
18 | using namespace ento; |
19 | using namespace retaincountchecker; |
20 | |
21 | StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) { |
22 | switch (BT) { |
23 | case UseAfterRelease: |
24 | return "Use-after-release"; |
25 | case ReleaseNotOwned: |
26 | return "Bad release"; |
27 | case DeallocNotOwned: |
28 | return "-dealloc sent to non-exclusively owned object"; |
29 | case FreeNotOwned: |
30 | return "freeing non-exclusively owned object"; |
31 | case OverAutorelease: |
32 | return "Object autoreleased too many times"; |
33 | case ReturnNotOwnedForOwned: |
34 | return "Method should return an owned object"; |
35 | case LeakWithinFunction: |
36 | return "Leak"; |
37 | case LeakAtReturn: |
38 | return "Leak of returned object"; |
39 | } |
40 | llvm_unreachable("Unknown RefCountBugType"); |
41 | } |
42 | |
43 | StringRef RefCountBug::getDescription() const { |
44 | switch (BT) { |
45 | case UseAfterRelease: |
46 | return "Reference-counted object is used after it is released"; |
47 | case ReleaseNotOwned: |
48 | return "Incorrect decrement of the reference count of an object that is " |
49 | "not owned at this point by the caller"; |
50 | case DeallocNotOwned: |
51 | return "-dealloc sent to object that may be referenced elsewhere"; |
52 | case FreeNotOwned: |
53 | return "'free' called on an object that may be referenced elsewhere"; |
54 | case OverAutorelease: |
55 | return "Object autoreleased too many times"; |
56 | case ReturnNotOwnedForOwned: |
57 | return "Object with a +0 retain count returned to caller where a +1 " |
58 | "(owning) retain count is expected"; |
59 | case LeakWithinFunction: |
60 | case LeakAtReturn: |
61 | return ""; |
62 | } |
63 | llvm_unreachable("Unknown RefCountBugType"); |
64 | } |
65 | |
66 | RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT) |
67 | : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, |
68 | BT == LeakWithinFunction || BT == LeakAtReturn), |
69 | BT(BT), Checker(Checker) {} |
70 | |
71 | static bool isNumericLiteralExpression(const Expr *E) { |
72 | |
73 | return isa<IntegerLiteral>(E) || |
74 | isa<CharacterLiteral>(E) || |
75 | isa<FloatingLiteral>(E) || |
76 | isa<ObjCBoolLiteralExpr>(E) || |
77 | isa<CXXBoolLiteralExpr>(E); |
78 | } |
79 | |
80 | |
81 | |
82 | |
83 | static std::string getPrettyTypeName(QualType QT) { |
84 | QualType PT = QT->getPointeeType(); |
85 | if (!PT.isNull() && !QT->getAs<TypedefType>()) |
86 | if (const auto *RD = PT->getAsCXXRecordDecl()) |
87 | return RD->getName(); |
88 | return QT.getAsString(); |
89 | } |
90 | |
91 | |
92 | |
93 | bool shouldGenerateNote(llvm::raw_string_ostream &os, |
94 | const RefVal *PrevT, |
95 | const RefVal &CurrV, |
96 | bool DeallocSent) { |
97 | |
98 | RefVal PrevV = *PrevT; |
99 | |
100 | |
101 | if (DeallocSent) { |
102 | |
103 | (0) . __assert_fail ("!PrevV.hasSameState(CurrV) && \"The state should have changed.\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp", 103, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true">assert(!PrevV.hasSameState(CurrV) && "The state should have changed."); |
104 | |
105 | |
106 | if (CurrV.getKind() == RefVal::Released) { |
107 | assert(CurrV.getCombinedCounts() == 0); |
108 | os << "Object released by directly sending the '-dealloc' message"; |
109 | return true; |
110 | } |
111 | } |
112 | |
113 | |
114 | if (!PrevV.hasSameState(CurrV)) |
115 | switch (CurrV.getKind()) { |
116 | case RefVal::Owned: |
117 | case RefVal::NotOwned: |
118 | if (PrevV.getCount() == CurrV.getCount()) { |
119 | |
120 | if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) |
121 | return false; |
122 | |
123 | assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); |
124 | os << "Object autoreleased"; |
125 | return true; |
126 | } |
127 | |
128 | if (PrevV.getCount() > CurrV.getCount()) |
129 | os << "Reference count decremented."; |
130 | else |
131 | os << "Reference count incremented."; |
132 | |
133 | if (unsigned Count = CurrV.getCount()) |
134 | os << " The object now has a +" << Count << " retain count."; |
135 | |
136 | return true; |
137 | |
138 | case RefVal::Released: |
139 | if (CurrV.getIvarAccessHistory() == |
140 | RefVal::IvarAccessHistory::ReleasedAfterDirectAccess && |
141 | CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) { |
142 | os << "Strong instance variable relinquished. "; |
143 | } |
144 | os << "Object released."; |
145 | return true; |
146 | |
147 | case RefVal::ReturnedOwned: |
148 | |
149 | if (CurrV.getAutoreleaseCount()) |
150 | return false; |
151 | |
152 | os << "Object returned to caller as an owning reference (single " |
153 | "retain count transferred to caller)"; |
154 | return true; |
155 | |
156 | case RefVal::ReturnedNotOwned: |
157 | os << "Object returned to caller with a +0 retain count"; |
158 | return true; |
159 | |
160 | default: |
161 | return false; |
162 | } |
163 | return true; |
164 | } |
165 | |
166 | |
167 | |
168 | |
169 | Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, |
170 | const LocationContext *LCtx, |
171 | SymbolRef &Sym, |
172 | Optional<CallEventRef<>> CE) { |
173 | if (!CE) |
174 | return None; |
175 | |
176 | for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++) |
177 | if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion()) |
178 | if (const auto *TR = dyn_cast<TypedValueRegion>(MR)) |
179 | if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym) |
180 | return Idx; |
181 | |
182 | return None; |
183 | } |
184 | |
185 | static Optional<std::string> findMetaClassAlloc(const Expr *Callee) { |
186 | if (const auto *ME = dyn_cast<MemberExpr>(Callee)) { |
187 | if (ME->getMemberDecl()->getNameAsString() != "alloc") |
188 | return None; |
189 | const Expr *This = ME->getBase()->IgnoreParenImpCasts(); |
190 | if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) { |
191 | const ValueDecl *VD = DRE->getDecl(); |
192 | if (VD->getNameAsString() != "metaClass") |
193 | return None; |
194 | |
195 | if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext())) |
196 | return RD->getNameAsString(); |
197 | |
198 | } |
199 | } |
200 | return None; |
201 | } |
202 | |
203 | static std::string findAllocatedObjectName(const Stmt *S, QualType QT) { |
204 | if (const auto *CE = dyn_cast<CallExpr>(S)) |
205 | if (auto Out = findMetaClassAlloc(CE->getCallee())) |
206 | return *Out; |
207 | return getPrettyTypeName(QT); |
208 | } |
209 | |
210 | static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, |
211 | const LocationContext *LCtx, |
212 | const RefVal &CurrV, SymbolRef &Sym, |
213 | const Stmt *S, |
214 | llvm::raw_string_ostream &os) { |
215 | CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); |
216 | if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { |
217 | |
218 | |
219 | SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); |
220 | const FunctionDecl *FD = X.getAsFunctionDecl(); |
221 | |
222 | |
223 | if (!FD) |
224 | FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); |
225 | |
226 | if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) { |
227 | os << "Call to method '" << MD->getQualifiedNameAsString() << '\''; |
228 | } else if (FD) { |
229 | os << "Call to function '" << FD->getQualifiedNameAsString() << '\''; |
230 | } else { |
231 | os << "function call"; |
232 | } |
233 | } else if (isa<CXXNewExpr>(S)) { |
234 | os << "Operator 'new'"; |
235 | } else { |
236 | (S)", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp", 236, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true">assert(isa<ObjCMessageExpr>(S)); |
237 | CallEventRef<ObjCMethodCall> Call = |
238 | Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); |
239 | |
240 | switch (Call->getMessageKind()) { |
241 | case OCM_Message: |
242 | os << "Method"; |
243 | break; |
244 | case OCM_PropertyAccess: |
245 | os << "Property"; |
246 | break; |
247 | case OCM_Subscript: |
248 | os << "Subscript"; |
249 | break; |
250 | } |
251 | } |
252 | |
253 | Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); |
254 | auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); |
255 | |
256 | |
257 | if (!Idx) { |
258 | os << " returns "; |
259 | } else { |
260 | os << " writes "; |
261 | } |
262 | |
263 | if (CurrV.getObjKind() == ObjKind::CF) { |
264 | os << "a Core Foundation object of type '" |
265 | << Sym->getType().getAsString() << "' with a "; |
266 | } else if (CurrV.getObjKind() == ObjKind::OS) { |
267 | os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType()) |
268 | << "' with a "; |
269 | } else if (CurrV.getObjKind() == ObjKind::Generalized) { |
270 | os << "an object of type '" << Sym->getType().getAsString() |
271 | << "' with a "; |
272 | } else { |
273 | assert(CurrV.getObjKind() == ObjKind::ObjC); |
274 | QualType T = Sym->getType(); |
275 | if (!isa<ObjCObjectPointerType>(T)) { |
276 | os << "an Objective-C object with a "; |
277 | } else { |
278 | const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); |
279 | os << "an instance of " << PT->getPointeeType().getAsString() |
280 | << " with a "; |
281 | } |
282 | } |
283 | |
284 | if (CurrV.isOwned()) { |
285 | os << "+1 retain count"; |
286 | } else { |
287 | assert(CurrV.isNotOwned()); |
288 | os << "+0 retain count"; |
289 | } |
290 | |
291 | if (Idx) { |
292 | os << " into an out parameter '"; |
293 | const ParmVarDecl *PVD = (*CE)->parameters()[*Idx]; |
294 | PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), |
295 | ); |
296 | os << "'"; |
297 | |
298 | QualType RT = (*CE)->getResultType(); |
299 | if (!RT.isNull() && !RT->isVoidType()) { |
300 | SVal RV = (*CE)->getReturnValue(); |
301 | if (CurrSt->isNull(RV).isConstrainedTrue()) { |
302 | os << " (assuming the call returns zero)"; |
303 | } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) { |
304 | os << " (assuming the call returns non-zero)"; |
305 | } |
306 | |
307 | } |
308 | } |
309 | } |
310 | |
311 | namespace clang { |
312 | namespace ento { |
313 | namespace retaincountchecker { |
314 | |
315 | class RefCountReportVisitor : public BugReporterVisitor { |
316 | protected: |
317 | SymbolRef Sym; |
318 | |
319 | public: |
320 | RefCountReportVisitor(SymbolRef sym) : Sym(sym) {} |
321 | |
322 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
323 | static int x = 0; |
324 | ID.AddPointer(&x); |
325 | ID.AddPointer(Sym); |
326 | } |
327 | |
328 | std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, |
329 | BugReporterContext &BRC, |
330 | BugReport &BR) override; |
331 | |
332 | std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, |
333 | const ExplodedNode *N, |
334 | BugReport &BR) override; |
335 | }; |
336 | |
337 | class RefLeakReportVisitor : public RefCountReportVisitor { |
338 | public: |
339 | RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {} |
340 | |
341 | std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, |
342 | const ExplodedNode *N, |
343 | BugReport &BR) override; |
344 | }; |
345 | |
346 | } |
347 | } |
348 | } |
349 | |
350 | |
351 | |
352 | static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) { |
353 | const StackFrameContext *SC = Pred->getStackFrame(); |
354 | if (SC->inTopFrame()) |
355 | return nullptr; |
356 | const StackFrameContext *PC = SC->getParent()->getStackFrame(); |
357 | if (!PC) |
358 | return nullptr; |
359 | |
360 | const ExplodedNode *N = Pred; |
361 | while (N && N->getStackFrame() != PC) { |
362 | N = N->getFirstPred(); |
363 | } |
364 | return N; |
365 | } |
366 | |
367 | |
368 | |
369 | |
370 | |
371 | static std::shared_ptr<PathDiagnosticEventPiece> |
372 | annotateConsumedSummaryMismatch(const ExplodedNode *N, |
373 | CallExitBegin &CallExitLoc, |
374 | const SourceManager &SM, |
375 | CallEventManager &CEMgr) { |
376 | |
377 | const ExplodedNode *CN = getCalleeNode(N); |
378 | if (!CN) |
379 | return nullptr; |
380 | |
381 | CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState()); |
382 | |
383 | std::string sbuf; |
384 | llvm::raw_string_ostream os(sbuf); |
385 | ArrayRef<const ParmVarDecl *> Parameters = Call->parameters(); |
386 | for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) { |
387 | const ParmVarDecl *PVD = Parameters[I]; |
388 | |
389 | if (!PVD->hasAttr<OSConsumedAttr>()) |
390 | continue; |
391 | |
392 | if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) { |
393 | const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR); |
394 | const RefVal *CountAtExit = getRefBinding(N->getState(), SR); |
395 | |
396 | if (!CountBeforeCall || !CountAtExit) |
397 | continue; |
398 | |
399 | unsigned CountBefore = CountBeforeCall->getCount(); |
400 | unsigned CountAfter = CountAtExit->getCount(); |
401 | |
402 | bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1; |
403 | if (!AsExpected) { |
404 | os << "Parameter '"; |
405 | PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), |
406 | ); |
407 | os << "' is marked as consuming, but the function did not consume " |
408 | << "the reference\n"; |
409 | } |
410 | } |
411 | } |
412 | |
413 | if (os.str().empty()) |
414 | return nullptr; |
415 | |
416 | PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM); |
417 | return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); |
418 | } |
419 | |
420 | |
421 | static std::shared_ptr<PathDiagnosticEventPiece> |
422 | annotateStartParameter(const ExplodedNode *N, SymbolRef Sym, |
423 | const SourceManager &SM) { |
424 | auto PP = N->getLocationAs<BlockEdge>(); |
425 | if (!PP) |
426 | return nullptr; |
427 | |
428 | const CFGBlock *Src = PP->getSrc(); |
429 | const RefVal *CurrT = getRefBinding(N->getState(), Sym); |
430 | |
431 | if (&Src->getParent()->getEntry() != Src || !CurrT || |
432 | getRefBinding(N->getFirstPred()->getState(), Sym)) |
433 | return nullptr; |
434 | |
435 | const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion()); |
436 | const auto *PVD = cast<ParmVarDecl>(VR->getDecl()); |
437 | PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM); |
438 | |
439 | std::string s; |
440 | llvm::raw_string_ostream os(s); |
441 | os << "Parameter '" << PVD->getNameAsString() << "' starts at +"; |
442 | if (CurrT->getCount() == 1) { |
443 | os << "1, as it is marked as consuming"; |
444 | } else { |
445 | getCount() == 0", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp", 445, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true">assert(CurrT->getCount() == 0); |
446 | os << "0"; |
447 | } |
448 | return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); |
449 | } |
450 | |
451 | std::shared_ptr<PathDiagnosticPiece> |
452 | RefCountReportVisitor::VisitNode(const ExplodedNode *N, |
453 | BugReporterContext &BRC, BugReport &BR) { |
454 | |
455 | const auto &BT = static_cast<const RefCountBug&>(BR.getBugType()); |
456 | const auto *Checker = |
457 | static_cast<const RetainCountChecker *>(BT.getChecker()); |
458 | |
459 | bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || |
460 | BT.getBugType() == RefCountBug::DeallocNotOwned; |
461 | |
462 | const SourceManager &SM = BRC.getSourceManager(); |
463 | CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); |
464 | if (auto CE = N->getLocationAs<CallExitBegin>()) |
465 | if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr)) |
466 | return PD; |
467 | |
468 | if (auto PD = annotateStartParameter(N, Sym, SM)) |
469 | return PD; |
470 | |
471 | |
472 | |
473 | if (!N->getLocation().getAs<StmtPoint>()) |
474 | return nullptr; |
475 | |
476 | |
477 | const ExplodedNode *PrevNode = N->getFirstPred(); |
478 | ProgramStateRef PrevSt = PrevNode->getState(); |
479 | ProgramStateRef CurrSt = N->getState(); |
480 | const LocationContext *LCtx = N->getLocationContext(); |
481 | |
482 | const RefVal* CurrT = getRefBinding(CurrSt, Sym); |
483 | if (!CurrT) |
484 | return nullptr; |
485 | |
486 | const RefVal &CurrV = *CurrT; |
487 | const RefVal *PrevT = getRefBinding(PrevSt, Sym); |
488 | |
489 | |
490 | |
491 | std::string sbuf; |
492 | llvm::raw_string_ostream os(sbuf); |
493 | |
494 | if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) { |
495 | os << "Object is now not exclusively owned"; |
496 | auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM); |
497 | return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); |
498 | } |
499 | |
500 | |
501 | |
502 | if (!PrevT) { |
503 | const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); |
504 | |
505 | if (isa<ObjCIvarRefExpr>(S) && |
506 | isSynthesizedAccessor(LCtx->getStackFrame())) { |
507 | S = LCtx->getStackFrame()->getCallSite(); |
508 | } |
509 | |
510 | if (isa<ObjCArrayLiteral>(S)) { |
511 | os << "NSArray literal is an object with a +0 retain count"; |
512 | } else if (isa<ObjCDictionaryLiteral>(S)) { |
513 | os << "NSDictionary literal is an object with a +0 retain count"; |
514 | } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { |
515 | if (isNumericLiteralExpression(BL->getSubExpr())) |
516 | os << "NSNumber literal is an object with a +0 retain count"; |
517 | else { |
518 | const ObjCInterfaceDecl *BoxClass = nullptr; |
519 | if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) |
520 | BoxClass = Method->getClassInterface(); |
521 | |
522 | |
523 | |
524 | if (BoxClass) { |
525 | os << *BoxClass << " b"; |
526 | } else { |
527 | os << "B"; |
528 | } |
529 | |
530 | os << "oxed expression produces an object with a +0 retain count"; |
531 | } |
532 | } else if (isa<ObjCIvarRefExpr>(S)) { |
533 | os << "Object loaded from instance variable"; |
534 | } else { |
535 | generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os); |
536 | } |
537 | |
538 | PathDiagnosticLocation Pos(S, SM, N->getLocationContext()); |
539 | return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); |
540 | } |
541 | |
542 | |
543 | |
544 | bool DeallocSent = false; |
545 | |
546 | const ProgramPointTag *Tag = N->getLocation().getTag(); |
547 | |
548 | if (Tag == &Checker->getCastFailTag()) { |
549 | os << "Assuming dynamic cast returns null due to type mismatch"; |
550 | } |
551 | |
552 | if (Tag == &Checker->getDeallocSentTag()) { |
553 | |
554 | |
555 | const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); |
556 | |
557 | if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { |
558 | |
559 | |
560 | unsigned i = 0; |
561 | |
562 | for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) { |
563 | |
564 | |
565 | |
566 | if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) |
567 | continue; |
568 | |
569 | |
570 | DeallocSent = true; |
571 | } |
572 | } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { |
573 | if (const Expr *receiver = ME->getInstanceReceiver()) { |
574 | if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) |
575 | .getAsLocSymbol() == Sym) { |
576 | |
577 | DeallocSent = true; |
578 | } |
579 | } |
580 | } |
581 | } |
582 | |
583 | if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent)) |
584 | return nullptr; |
585 | |
586 | if (os.str().empty()) |
587 | return nullptr; |
588 | |
589 | const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); |
590 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
591 | N->getLocationContext()); |
592 | auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); |
593 | |
594 | |
595 | |
596 | for (const Stmt *Child : S->children()) |
597 | if (const Expr *Exp = dyn_cast_or_null<Expr>(Child)) |
598 | if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { |
599 | P->addRange(Exp->getSourceRange()); |
600 | break; |
601 | } |
602 | |
603 | return std::move(P); |
604 | } |
605 | |
606 | static Optional<std::string> describeRegion(const MemRegion *MR) { |
607 | if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) |
608 | return std::string(VR->getDecl()->getName()); |
609 | |
610 | |
611 | return None; |
612 | } |
613 | |
614 | namespace { |
615 | |
616 | |
617 | |
618 | |
619 | |
620 | struct AllocationInfo { |
621 | const ExplodedNode* N; |
622 | const MemRegion *R; |
623 | const LocationContext *InterestingMethodContext; |
624 | AllocationInfo(const ExplodedNode *InN, |
625 | const MemRegion *InR, |
626 | const LocationContext *InInterestingMethodContext) : |
627 | N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} |
628 | }; |
629 | } |
630 | |
631 | static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, |
632 | const ExplodedNode *N, SymbolRef Sym) { |
633 | const ExplodedNode *AllocationNode = N; |
634 | const ExplodedNode *AllocationNodeInCurrentOrParentContext = N; |
635 | const MemRegion *FirstBinding = nullptr; |
636 | const LocationContext *LeakContext = N->getLocationContext(); |
637 | |
638 | |
639 | |
640 | const LocationContext *InitMethodContext = nullptr; |
641 | |
642 | while (N) { |
643 | ProgramStateRef St = N->getState(); |
644 | const LocationContext *NContext = N->getLocationContext(); |
645 | |
646 | if (!getRefBinding(St, Sym)) |
647 | break; |
648 | |
649 | StoreManager::FindUniqueBinding FB(Sym); |
650 | StateMgr.iterBindings(St, FB); |
651 | |
652 | if (FB) { |
653 | const MemRegion *R = FB.getRegion(); |
654 | |
655 | |
656 | if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace())) |
657 | if (MR->getStackFrame() == LeakContext->getStackFrame()) |
658 | FirstBinding = R; |
659 | } |
660 | |
661 | |
662 | AllocationNode = N; |
663 | |
664 | |
665 | |
666 | |
667 | |
668 | |
669 | |
670 | |
671 | if (NContext == LeakContext || NContext->isParentOf(LeakContext)) |
672 | AllocationNodeInCurrentOrParentContext = N; |
673 | |
674 | |
675 | |
676 | if (!InitMethodContext) |
677 | if (auto CEP = N->getLocation().getAs<CallEnter>()) { |
678 | const Stmt *CE = CEP->getCallExpr(); |
679 | if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) { |
680 | const Stmt *RecExpr = ME->getInstanceReceiver(); |
681 | if (RecExpr) { |
682 | SVal RecV = St->getSVal(RecExpr, NContext); |
683 | if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym) |
684 | InitMethodContext = CEP->getCalleeContext(); |
685 | } |
686 | } |
687 | } |
688 | |
689 | N = N->getFirstPred(); |
690 | } |
691 | |
692 | |
693 | |
694 | const LocationContext *InterestingMethodContext = nullptr; |
695 | if (InitMethodContext) { |
696 | const ProgramPoint AllocPP = AllocationNode->getLocation(); |
697 | if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) |
698 | if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) |
699 | if (ME->getMethodFamily() == OMF_alloc) |
700 | InterestingMethodContext = InitMethodContext; |
701 | } |
702 | |
703 | |
704 | |
705 | (0) . __assert_fail ("N && \"Could not find allocation node\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp", 705, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true">assert(N && "Could not find allocation node"); |
706 | |
707 | if (AllocationNodeInCurrentOrParentContext && |
708 | AllocationNodeInCurrentOrParentContext->getLocationContext() != |
709 | LeakContext) |
710 | FirstBinding = nullptr; |
711 | |
712 | return AllocationInfo(AllocationNodeInCurrentOrParentContext, |
713 | FirstBinding, |
714 | InterestingMethodContext); |
715 | } |
716 | |
717 | std::shared_ptr<PathDiagnosticPiece> |
718 | RefCountReportVisitor::getEndPath(BugReporterContext &BRC, |
719 | const ExplodedNode *EndN, BugReport &BR) { |
720 | BR.markInteresting(Sym); |
721 | return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); |
722 | } |
723 | |
724 | std::shared_ptr<PathDiagnosticPiece> |
725 | RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, |
726 | const ExplodedNode *EndN, BugReport &BR) { |
727 | |
728 | |
729 | |
730 | BR.markInteresting(Sym); |
731 | |
732 | |
733 | |
734 | |
735 | AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); |
736 | |
737 | const MemRegion* FirstBinding = AllocI.R; |
738 | BR.markInteresting(AllocI.InterestingMethodContext); |
739 | |
740 | SourceManager& SM = BRC.getSourceManager(); |
741 | |
742 | |
743 | |
744 | |
745 | const ExplodedNode *LeakN = EndN; |
746 | PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); |
747 | |
748 | std::string sbuf; |
749 | llvm::raw_string_ostream os(sbuf); |
750 | |
751 | os << "Object leaked: "; |
752 | |
753 | Optional<std::string> RegionDescription = describeRegion(FirstBinding); |
754 | if (RegionDescription) { |
755 | os << "object allocated and stored into '" << *RegionDescription << '\''; |
756 | } else { |
757 | os << "allocated object of type '" << getPrettyTypeName(Sym->getType()) |
758 | << "'"; |
759 | } |
760 | |
761 | |
762 | const RefVal* RV = getRefBinding(EndN->getState(), Sym); |
763 | assert(RV); |
764 | |
765 | if (RV->getKind() == RefVal::ErrorLeakReturned) { |
766 | |
767 | |
768 | |
769 | const Decl *D = &EndN->getCodeDecl(); |
770 | |
771 | os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " |
772 | : " is returned from a function "); |
773 | |
774 | if (D->hasAttr<CFReturnsNotRetainedAttr>()) { |
775 | os << "that is annotated as CF_RETURNS_NOT_RETAINED"; |
776 | } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { |
777 | os << "that is annotated as NS_RETURNS_NOT_RETAINED"; |
778 | } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { |
779 | os << "that is annotated as OS_RETURNS_NOT_RETAINED"; |
780 | } else { |
781 | if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { |
782 | if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { |
783 | os << "managed by Automatic Reference Counting"; |
784 | } else { |
785 | os << "whose name ('" << MD->getSelector().getAsString() |
786 | << "') does not start with " |
787 | "'copy', 'mutableCopy', 'alloc' or 'new'." |
788 | " This violates the naming convention rules" |
789 | " given in the Memory Management Guide for Cocoa"; |
790 | } |
791 | } else { |
792 | const FunctionDecl *FD = cast<FunctionDecl>(D); |
793 | ObjKind K = RV->getObjKind(); |
794 | if (K == ObjKind::ObjC || K == ObjKind::CF) { |
795 | os << "whose name ('" << *FD |
796 | << "') does not contain 'Copy' or 'Create'. This violates the " |
797 | "naming" |
798 | " convention rules given in the Memory Management Guide for " |
799 | "Core" |
800 | " Foundation"; |
801 | } else if (RV->getObjKind() == ObjKind::OS) { |
802 | std::string FuncName = FD->getNameAsString(); |
803 | os << "whose name ('" << FuncName |
804 | << "') starts with '" << StringRef(FuncName).substr(0, 3) << "'"; |
805 | } |
806 | } |
807 | } |
808 | } else { |
809 | os << " is not referenced later in this execution path and has a retain " |
810 | "count of +" << RV->getCount(); |
811 | } |
812 | |
813 | return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); |
814 | } |
815 | |
816 | RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, |
817 | ExplodedNode *n, SymbolRef sym, |
818 | bool isLeak) |
819 | : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) { |
820 | if (!isLeak) |
821 | addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); |
822 | } |
823 | |
824 | RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, |
825 | ExplodedNode *n, SymbolRef sym, |
826 | StringRef endText) |
827 | : BugReport(D, D.getDescription(), endText, n) { |
828 | |
829 | addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); |
830 | } |
831 | |
832 | void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) { |
833 | const SourceManager& SMgr = Ctx.getSourceManager(); |
834 | |
835 | if (!sym->getOriginRegion()) |
836 | return; |
837 | |
838 | auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion()); |
839 | if (Region) { |
840 | const Decl *PDecl = Region->getDecl(); |
841 | if (PDecl && isa<ParmVarDecl>(PDecl)) { |
842 | PathDiagnosticLocation ParamLocation = |
843 | PathDiagnosticLocation::create(PDecl, SMgr); |
844 | Location = ParamLocation; |
845 | UniqueingLocation = ParamLocation; |
846 | UniqueingDecl = Ctx.getLocationContext()->getDecl(); |
847 | } |
848 | } |
849 | } |
850 | |
851 | void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx, |
852 | SymbolRef sym) { |
853 | |
854 | |
855 | |
856 | |
857 | |
858 | |
859 | |
860 | |
861 | const ExplodedNode *AllocNode = nullptr; |
862 | |
863 | const SourceManager& SMgr = Ctx.getSourceManager(); |
864 | |
865 | AllocationInfo AllocI = |
866 | GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); |
867 | |
868 | AllocNode = AllocI.N; |
869 | AllocBinding = AllocI.R; |
870 | markInteresting(AllocI.InterestingMethodContext); |
871 | |
872 | |
873 | |
874 | |
875 | |
876 | AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); |
877 | |
878 | if (!AllocStmt) { |
879 | AllocBinding = nullptr; |
880 | return; |
881 | } |
882 | |
883 | PathDiagnosticLocation AllocLocation = |
884 | PathDiagnosticLocation::createBegin(AllocStmt, SMgr, |
885 | AllocNode->getLocationContext()); |
886 | Location = AllocLocation; |
887 | |
888 | |
889 | |
890 | UniqueingLocation = AllocLocation; |
891 | UniqueingDecl = AllocNode->getLocationContext()->getDecl(); |
892 | } |
893 | |
894 | void RefLeakReport::createDescription(CheckerContext &Ctx) { |
895 | assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid()); |
896 | Description.clear(); |
897 | llvm::raw_string_ostream os(Description); |
898 | os << "Potential leak of an object"; |
899 | |
900 | Optional<std::string> RegionDescription = describeRegion(AllocBinding); |
901 | if (RegionDescription) { |
902 | os << " stored into '" << *RegionDescription << '\''; |
903 | } else { |
904 | |
905 | |
906 | os << " of type '" << getPrettyTypeName(Sym->getType()) << "'"; |
907 | } |
908 | } |
909 | |
910 | RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, |
911 | ExplodedNode *n, SymbolRef sym, |
912 | CheckerContext &Ctx) |
913 | : RefCountReport(D, LOpts, n, sym, ) { |
914 | |
915 | deriveAllocLocation(Ctx, sym); |
916 | if (!AllocBinding) |
917 | deriveParamLocation(Ctx, sym); |
918 | |
919 | createDescription(Ctx); |
920 | |
921 | addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym)); |
922 | } |
923 | |