1 | //=======- RetainPtrCtorAdoptChecker.cpp -------------------------*- C++ -*-==// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "ASTUtils.h" |
10 | #include "PtrTypesSemantics.h" |
11 | #include "clang/AST/RecursiveASTVisitor.h" |
12 | #include "clang/Analysis/DomainSpecific/CocoaConventions.h" |
13 | #include "clang/Analysis/RetainSummaryManager.h" |
14 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
15 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
16 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
17 | #include "clang/StaticAnalyzer/Core/Checker.h" |
18 | #include "llvm/ADT/DenseSet.h" |
19 | #include <optional> |
20 | |
21 | using namespace clang; |
22 | using namespace ento; |
23 | |
24 | namespace { |
25 | |
26 | class RetainPtrCtorAdoptChecker |
27 | : public Checker<check::ASTDecl<TranslationUnitDecl>> { |
28 | private: |
29 | BugType Bug; |
30 | mutable BugReporter *BR = nullptr; |
31 | mutable std::unique_ptr<RetainSummaryManager> Summaries; |
32 | mutable llvm::DenseSet<const ValueDecl *> CreateOrCopyOutArguments; |
33 | mutable llvm::DenseSet<const Expr *> CreateOrCopyFnCall; |
34 | mutable RetainTypeChecker RTC; |
35 | |
36 | public: |
37 | RetainPtrCtorAdoptChecker() |
38 | : Bug(this, "Correct use of RetainPtr, adoptNS, and adoptCF" , |
39 | "WebKit coding guidelines" ) {} |
40 | |
41 | void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, |
42 | BugReporter &BRArg) const { |
43 | BR = &BRArg; |
44 | |
45 | // The calls to checkAST* from AnalysisConsumer don't |
46 | // visit template instantiations or lambda classes. We |
47 | // want to visit those, so we make our own RecursiveASTVisitor. |
48 | struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { |
49 | const RetainPtrCtorAdoptChecker *Checker; |
50 | Decl *DeclWithIssue{nullptr}; |
51 | |
52 | using Base = RecursiveASTVisitor<LocalVisitor>; |
53 | |
54 | explicit LocalVisitor(const RetainPtrCtorAdoptChecker *Checker) |
55 | : Checker(Checker) { |
56 | assert(Checker); |
57 | } |
58 | |
59 | bool shouldVisitTemplateInstantiations() const { return true; } |
60 | bool shouldVisitImplicitCode() const { return false; } |
61 | |
62 | bool TraverseDecl(Decl *D) { |
63 | llvm::SaveAndRestore SavedDecl(DeclWithIssue); |
64 | if (D && (isa<FunctionDecl>(Val: D) || isa<ObjCMethodDecl>(Val: D))) |
65 | DeclWithIssue = D; |
66 | return Base::TraverseDecl(D); |
67 | } |
68 | |
69 | bool TraverseClassTemplateDecl(ClassTemplateDecl *CTD) { |
70 | if (isRetainPtr(Name: safeGetName(ASTNode: CTD))) |
71 | return true; // Skip the contents of RetainPtr. |
72 | return Base::TraverseClassTemplateDecl(D: CTD); |
73 | } |
74 | |
75 | bool VisitTypedefDecl(TypedefDecl *TD) { |
76 | Checker->RTC.visitTypedef(TD); |
77 | return true; |
78 | } |
79 | |
80 | bool VisitCallExpr(const CallExpr *CE) { |
81 | Checker->visitCallExpr(CE, DeclWithIssue); |
82 | return true; |
83 | } |
84 | |
85 | bool VisitCXXConstructExpr(const CXXConstructExpr *CE) { |
86 | Checker->visitConstructExpr(CE, DeclWithIssue); |
87 | return true; |
88 | } |
89 | |
90 | bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) { |
91 | Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue); |
92 | return true; |
93 | } |
94 | |
95 | bool VisitReturnStmt(const ReturnStmt *RS) { |
96 | Checker->visitReturnStmt(RS, DeclWithIssue); |
97 | return true; |
98 | } |
99 | |
100 | bool VisitVarDecl(const VarDecl *VD) { |
101 | Checker->visitVarDecl(VD); |
102 | return true; |
103 | } |
104 | |
105 | bool VisitBinaryOperator(const BinaryOperator *BO) { |
106 | Checker->visitBinaryOperator(BO); |
107 | return true; |
108 | } |
109 | }; |
110 | |
111 | LocalVisitor visitor(this); |
112 | Summaries = std::make_unique<RetainSummaryManager>( |
113 | args&: TUD->getASTContext(), args: true /* trackObjCAndCFObjects */, |
114 | args: false /* trackOSObjects */); |
115 | RTC.visitTranslationUnitDecl(TUD); |
116 | visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD)); |
117 | } |
118 | |
119 | bool isAdoptFn(const Decl *FnDecl) const { |
120 | return isAdoptFnName(Name: safeGetName(ASTNode: FnDecl)); |
121 | } |
122 | |
123 | bool isAdoptFnName(const std::string &Name) const { |
124 | return isAdoptNS(Name) || Name == "adoptCF" || Name == "adoptCFArc" ; |
125 | } |
126 | |
127 | bool isAdoptNS(const std::string &Name) const { |
128 | return Name == "adoptNS" || Name == "adoptNSArc" ; |
129 | } |
130 | |
131 | void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const { |
132 | assert(BR && "expected nonnull BugReporter" ); |
133 | if (BR->getSourceManager().isInSystemHeader(Loc: CE->getExprLoc())) |
134 | return; |
135 | |
136 | std::string FnName; |
137 | if (auto *F = CE->getDirectCallee()) { |
138 | FnName = safeGetName(ASTNode: F); |
139 | if (isAdoptFnName(Name: FnName)) |
140 | checkAdoptCall(CE, FnName, DeclWithIssue); |
141 | else { |
142 | checkCreateOrCopyFunction(CE, DeclWithIssue); |
143 | checkBridgingRelease(CE, Callee: F, DeclWithIssue); |
144 | } |
145 | return; |
146 | } |
147 | |
148 | auto *CalleeExpr = CE->getCallee(); |
149 | if (!CalleeExpr) |
150 | return; |
151 | CalleeExpr = CalleeExpr->IgnoreParenCasts(); |
152 | if (auto *UnresolvedExpr = dyn_cast<UnresolvedLookupExpr>(Val: CalleeExpr)) { |
153 | auto Name = UnresolvedExpr->getName(); |
154 | if (!Name.isIdentifier()) |
155 | return; |
156 | FnName = Name.getAsString(); |
157 | if (isAdoptFnName(Name: FnName)) |
158 | checkAdoptCall(CE, FnName, DeclWithIssue); |
159 | } |
160 | checkCreateOrCopyFunction(CE, DeclWithIssue); |
161 | } |
162 | |
163 | void checkAdoptCall(const CallExpr *CE, const std::string &FnName, |
164 | const Decl *DeclWithIssue) const { |
165 | if (!CE->getNumArgs()) |
166 | return; |
167 | |
168 | auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts(); |
169 | auto Result = isOwned(E: Arg); |
170 | if (Result == IsOwnedResult::Unknown) |
171 | Result = IsOwnedResult::NotOwned; |
172 | |
173 | const Expr *Inner = nullptr; |
174 | if (isAllocInit(E: Arg, InnerExpr: &Inner) || isCreateOrCopy(E: Arg)) { |
175 | if (Inner) |
176 | CreateOrCopyFnCall.insert(V: Inner); |
177 | CreateOrCopyFnCall.insert(V: Arg); // Avoid double reporting. |
178 | return; |
179 | } |
180 | if (Result == IsOwnedResult::Owned || Result == IsOwnedResult::Skip) { |
181 | CreateOrCopyFnCall.insert(V: Arg); |
182 | return; |
183 | } |
184 | |
185 | if (auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg)) { |
186 | if (CreateOrCopyOutArguments.contains(V: DRE->getDecl())) |
187 | return; |
188 | } |
189 | if (RTC.isARCEnabled() && isAdoptFnName(Name: FnName)) |
190 | reportUseAfterFree(Name: FnName, CE, DeclWithIssue, condition: "when ARC is disabled" ); |
191 | else |
192 | reportUseAfterFree(Name: FnName, CE, DeclWithIssue); |
193 | } |
194 | |
195 | void visitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr, |
196 | const Decl *DeclWithIssue) const { |
197 | if (BR->getSourceManager().isInSystemHeader(Loc: ObjCMsgExpr->getExprLoc())) |
198 | return; |
199 | |
200 | auto Selector = ObjCMsgExpr->getSelector(); |
201 | if (Selector.getAsString() == "autorelease" ) { |
202 | auto *Receiver = ObjCMsgExpr->getInstanceReceiver()->IgnoreParenCasts(); |
203 | if (!Receiver) |
204 | return; |
205 | ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: Receiver); |
206 | if (!ObjCMsgExpr) |
207 | return; |
208 | const Expr *Inner = nullptr; |
209 | if (!isAllocInit(E: ObjCMsgExpr, InnerExpr: &Inner)) |
210 | return; |
211 | CreateOrCopyFnCall.insert(V: ObjCMsgExpr); |
212 | if (Inner) |
213 | CreateOrCopyFnCall.insert(V: Inner); |
214 | return; |
215 | } |
216 | |
217 | const Expr *Inner = nullptr; |
218 | if (!isAllocInit(E: ObjCMsgExpr, InnerExpr: &Inner)) |
219 | return; |
220 | if (RTC.isARCEnabled()) |
221 | return; // ARC never leaks. |
222 | if (CreateOrCopyFnCall.contains(V: ObjCMsgExpr)) |
223 | return; |
224 | if (Inner) |
225 | CreateOrCopyFnCall.insert(V: Inner); // Avoid double reporting. |
226 | reportLeak(E: ObjCMsgExpr, DeclWithIssue); |
227 | } |
228 | |
229 | void checkCreateOrCopyFunction(const CallExpr *CE, |
230 | const Decl *DeclWithIssue) const { |
231 | unsigned ArgCount = CE->getNumArgs(); |
232 | auto *CalleeDecl = CE->getCalleeDecl(); |
233 | auto *FnDecl = CalleeDecl ? CalleeDecl->getAsFunction() : nullptr; |
234 | for (unsigned ArgIndex = 0; ArgIndex < ArgCount; ++ArgIndex) { |
235 | auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts(); |
236 | auto *Unary = dyn_cast<UnaryOperator>(Val: Arg); |
237 | if (!Unary) |
238 | continue; |
239 | if (Unary->getOpcode() != UO_AddrOf) |
240 | continue; |
241 | auto *SubExpr = Unary->getSubExpr(); |
242 | if (!SubExpr) |
243 | continue; |
244 | auto *DRE = dyn_cast<DeclRefExpr>(Val: SubExpr->IgnoreParenCasts()); |
245 | if (!DRE) |
246 | continue; |
247 | auto *Decl = DRE->getDecl(); |
248 | if (!Decl) |
249 | continue; |
250 | if (FnDecl && ArgIndex < FnDecl->getNumParams()) { |
251 | // Manually check attributes on argumenet since RetainSummaryManager |
252 | // basically ignores CF_RETRUNS_RETAINED on out arguments. |
253 | auto *ParamDecl = FnDecl->getParamDecl(i: ArgIndex); |
254 | if (ParamDecl->hasAttr<CFReturnsRetainedAttr>()) |
255 | CreateOrCopyOutArguments.insert(V: Decl); |
256 | } else { |
257 | // No callee or a variadic argument. |
258 | // Conservatively assume it's an out argument. |
259 | if (RTC.isUnretained(Decl->getType())) |
260 | CreateOrCopyOutArguments.insert(V: Decl); |
261 | } |
262 | } |
263 | auto Summary = Summaries->getSummary(C: AnyCall(CE)); |
264 | switch (Summary->getRetEffect().getKind()) { |
265 | case RetEffect::OwnedSymbol: |
266 | case RetEffect::OwnedWhenTrackedReceiver: |
267 | if (!CreateOrCopyFnCall.contains(V: CE)) |
268 | reportLeak(E: CE, DeclWithIssue); |
269 | break; |
270 | default: |
271 | break; |
272 | } |
273 | } |
274 | |
275 | void checkBridgingRelease(const CallExpr *CE, const FunctionDecl *Callee, |
276 | const Decl *DeclWithIssue) const { |
277 | if (safeGetName(ASTNode: Callee) != "CFBridgingRelease" || CE->getNumArgs() != 1) |
278 | return; |
279 | |
280 | auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts(); |
281 | auto *InnerCE = dyn_cast<CallExpr>(Val: Arg); |
282 | if (!InnerCE) |
283 | return; |
284 | |
285 | auto *InnerF = InnerCE->getDirectCallee(); |
286 | if (!InnerF || !isCreateOrCopyFunction(FnDecl: InnerF)) |
287 | return; |
288 | |
289 | CreateOrCopyFnCall.insert(V: InnerCE); |
290 | } |
291 | |
292 | void visitConstructExpr(const CXXConstructExpr *CE, |
293 | const Decl *DeclWithIssue) const { |
294 | assert(BR && "expected nonnull BugReporter" ); |
295 | if (BR->getSourceManager().isInSystemHeader(Loc: CE->getExprLoc())) |
296 | return; |
297 | |
298 | auto *Ctor = CE->getConstructor(); |
299 | if (!Ctor) |
300 | return; |
301 | |
302 | auto *Cls = Ctor->getParent(); |
303 | if (!Cls) |
304 | return; |
305 | |
306 | if (!isRetainPtr(Name: safeGetName(ASTNode: Cls)) || !CE->getNumArgs()) |
307 | return; |
308 | |
309 | // Ignore RetainPtr construction inside adoptNS, adoptCF, and retainPtr. |
310 | if (isAdoptFn(FnDecl: DeclWithIssue) || safeGetName(ASTNode: DeclWithIssue) == "retainPtr" ) |
311 | return; |
312 | |
313 | std::string Name = "RetainPtr constructor" ; |
314 | auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts(); |
315 | auto Result = isOwned(E: Arg); |
316 | |
317 | if (isCreateOrCopy(E: Arg)) |
318 | CreateOrCopyFnCall.insert(V: Arg); // Avoid double reporting. |
319 | |
320 | const Expr *Inner = nullptr; |
321 | if (isAllocInit(E: Arg, InnerExpr: &Inner)) { |
322 | CreateOrCopyFnCall.insert(V: Arg); |
323 | if (Inner) |
324 | CreateOrCopyFnCall.insert(V: Inner); |
325 | } |
326 | |
327 | if (Result == IsOwnedResult::Skip) |
328 | return; |
329 | |
330 | if (Result == IsOwnedResult::Unknown) |
331 | Result = IsOwnedResult::NotOwned; |
332 | if (Result == IsOwnedResult::Owned) |
333 | reportLeak(Name, CE, DeclWithIssue); |
334 | else if (RTC.isARCEnabled() && isAllocInit(E: Arg)) |
335 | reportLeak(Name, CE, DeclWithIssue, condition: "when ARC is disabled" ); |
336 | else if (isCreateOrCopy(E: Arg)) |
337 | reportLeak(Name, CE, DeclWithIssue); |
338 | } |
339 | |
340 | void visitVarDecl(const VarDecl *VD) const { |
341 | auto *Init = VD->getInit(); |
342 | if (!Init || !RTC.isARCEnabled()) |
343 | return; |
344 | Init = Init->IgnoreParenCasts(); |
345 | const Expr *Inner = nullptr; |
346 | if (isAllocInit(E: Init, InnerExpr: &Inner)) { |
347 | CreateOrCopyFnCall.insert(V: Init); |
348 | if (Inner) |
349 | CreateOrCopyFnCall.insert(V: Inner); |
350 | } |
351 | } |
352 | |
353 | void visitBinaryOperator(const BinaryOperator *BO) const { |
354 | if (!BO->isAssignmentOp()) |
355 | return; |
356 | if (!isa<ObjCIvarRefExpr>(Val: BO->getLHS())) |
357 | return; |
358 | auto *RHS = BO->getRHS()->IgnoreParenCasts(); |
359 | const Expr *Inner = nullptr; |
360 | if (isAllocInit(E: RHS, InnerExpr: &Inner)) { |
361 | CreateOrCopyFnCall.insert(V: RHS); |
362 | if (Inner) |
363 | CreateOrCopyFnCall.insert(V: Inner); |
364 | } |
365 | } |
366 | |
367 | void visitReturnStmt(const ReturnStmt *RS, const Decl *DeclWithIssue) const { |
368 | if (!DeclWithIssue) |
369 | return; |
370 | auto *RetValue = RS->getRetValue(); |
371 | if (!RetValue) |
372 | return; |
373 | RetValue = RetValue->IgnoreParenCasts(); |
374 | std::optional<bool> retainsRet; |
375 | if (auto *FnDecl = dyn_cast<FunctionDecl>(Val: DeclWithIssue)) |
376 | retainsRet = retainsReturnValue(FnDecl); |
377 | else if (auto *MethodDecl = dyn_cast<ObjCMethodDecl>(Val: DeclWithIssue)) |
378 | retainsRet = retainsReturnValue(FnDecl: MethodDecl); |
379 | else |
380 | return; |
381 | if (!retainsRet || !*retainsRet) { |
382 | // Under ARC, returning [[X alloc] init] doesn't leak X. |
383 | if (RTC.isUnretained(RetValue->getType())) |
384 | return; |
385 | } |
386 | if (auto *CE = dyn_cast<CallExpr>(Val: RetValue)) { |
387 | auto *Callee = CE->getDirectCallee(); |
388 | if (!Callee || !isCreateOrCopyFunction(FnDecl: Callee)) |
389 | return; |
390 | CreateOrCopyFnCall.insert(V: CE); |
391 | return; |
392 | } |
393 | const Expr *Inner = nullptr; |
394 | if (isAllocInit(E: RetValue, InnerExpr: &Inner)) { |
395 | CreateOrCopyFnCall.insert(V: RetValue); |
396 | if (Inner) |
397 | CreateOrCopyFnCall.insert(V: Inner); |
398 | } |
399 | } |
400 | |
401 | template <typename CallableType> |
402 | std::optional<bool> retainsReturnValue(const CallableType *FnDecl) const { |
403 | auto Summary = Summaries->getSummary(C: AnyCall(FnDecl)); |
404 | auto RetEffect = Summary->getRetEffect(); |
405 | switch (RetEffect.getKind()) { |
406 | case RetEffect::NoRet: |
407 | return std::nullopt; |
408 | case RetEffect::OwnedSymbol: |
409 | return true; |
410 | case RetEffect::NotOwnedSymbol: |
411 | return false; |
412 | case RetEffect::OwnedWhenTrackedReceiver: |
413 | return std::nullopt; |
414 | case RetEffect::NoRetHard: |
415 | return std::nullopt; |
416 | } |
417 | return std::nullopt; |
418 | } |
419 | |
420 | bool isAllocInit(const Expr *E, const Expr **InnerExpr = nullptr) const { |
421 | auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: E); |
422 | if (auto *POE = dyn_cast<PseudoObjectExpr>(Val: E)) { |
423 | if (unsigned ExprCount = POE->getNumSemanticExprs()) { |
424 | auto *Expr = POE->getSemanticExpr(index: ExprCount - 1)->IgnoreParenCasts(); |
425 | ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: Expr); |
426 | if (InnerExpr) |
427 | *InnerExpr = ObjCMsgExpr; |
428 | } |
429 | } |
430 | if (!ObjCMsgExpr) |
431 | return false; |
432 | auto Selector = ObjCMsgExpr->getSelector(); |
433 | auto NameForFirstSlot = Selector.getNameForSlot(argIndex: 0); |
434 | if (NameForFirstSlot == "alloc" || NameForFirstSlot.starts_with(Prefix: "copy" ) || |
435 | NameForFirstSlot.starts_with(Prefix: "mutableCopy" )) |
436 | return true; |
437 | if (!NameForFirstSlot.starts_with(Prefix: "init" ) && |
438 | !NameForFirstSlot.starts_with(Prefix: "_init" )) |
439 | return false; |
440 | if (!ObjCMsgExpr->isInstanceMessage()) |
441 | return false; |
442 | auto *Receiver = ObjCMsgExpr->getInstanceReceiver(); |
443 | if (!Receiver) |
444 | return false; |
445 | Receiver = Receiver->IgnoreParenCasts(); |
446 | if (auto *Inner = dyn_cast<ObjCMessageExpr>(Val: Receiver)) { |
447 | if (InnerExpr) |
448 | *InnerExpr = Inner; |
449 | auto InnerSelector = Inner->getSelector(); |
450 | return InnerSelector.getNameForSlot(argIndex: 0) == "alloc" ; |
451 | } else if (auto *CE = dyn_cast<CallExpr>(Val: Receiver)) { |
452 | if (InnerExpr) |
453 | *InnerExpr = CE; |
454 | if (auto *Callee = CE->getDirectCallee()) { |
455 | if (Callee->getDeclName().isIdentifier()) { |
456 | auto CalleeName = Callee->getName(); |
457 | return CalleeName.starts_with(Prefix: "alloc" ); |
458 | } |
459 | } |
460 | } |
461 | return false; |
462 | } |
463 | |
464 | bool isCreateOrCopy(const Expr *E) const { |
465 | auto *CE = dyn_cast<CallExpr>(Val: E); |
466 | if (!CE) |
467 | return false; |
468 | auto *Callee = CE->getDirectCallee(); |
469 | if (!Callee) |
470 | return false; |
471 | return isCreateOrCopyFunction(FnDecl: Callee); |
472 | } |
473 | |
474 | bool isCreateOrCopyFunction(const FunctionDecl *FnDecl) const { |
475 | auto CalleeName = safeGetName(ASTNode: FnDecl); |
476 | return CalleeName.find(s: "Create" ) != std::string::npos || |
477 | CalleeName.find(s: "Copy" ) != std::string::npos; |
478 | } |
479 | |
480 | enum class IsOwnedResult { Unknown, Skip, Owned, NotOwned }; |
481 | IsOwnedResult isOwned(const Expr *E) const { |
482 | while (1) { |
483 | if (auto *POE = dyn_cast<PseudoObjectExpr>(Val: E)) { |
484 | if (unsigned SemanticExprCount = POE->getNumSemanticExprs()) { |
485 | E = POE->getSemanticExpr(index: SemanticExprCount - 1); |
486 | continue; |
487 | } |
488 | } |
489 | if (isa<CXXNullPtrLiteralExpr>(Val: E)) |
490 | return IsOwnedResult::NotOwned; |
491 | if (auto *DRE = dyn_cast<DeclRefExpr>(Val: E)) { |
492 | auto QT = DRE->getType(); |
493 | if (isRetainPtrType(T: QT)) |
494 | return IsOwnedResult::NotOwned; |
495 | QT = QT.getCanonicalType(); |
496 | if (RTC.isUnretained(QT, ignoreARC: true /* ignoreARC */)) |
497 | return IsOwnedResult::NotOwned; |
498 | auto *PointeeType = QT->getPointeeType().getTypePtrOrNull(); |
499 | if (PointeeType && PointeeType->isVoidType()) |
500 | return IsOwnedResult::NotOwned; // Assume reading void* as +0. |
501 | } |
502 | if (auto *TE = dyn_cast<CXXBindTemporaryExpr>(Val: E)) { |
503 | E = TE->getSubExpr(); |
504 | continue; |
505 | } |
506 | if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: E)) { |
507 | auto Summary = Summaries->getSummary(C: AnyCall(ObjCMsgExpr)); |
508 | auto RetEffect = Summary->getRetEffect(); |
509 | switch (RetEffect.getKind()) { |
510 | case RetEffect::NoRet: |
511 | return IsOwnedResult::Unknown; |
512 | case RetEffect::OwnedSymbol: |
513 | return IsOwnedResult::Owned; |
514 | case RetEffect::NotOwnedSymbol: |
515 | return IsOwnedResult::NotOwned; |
516 | case RetEffect::OwnedWhenTrackedReceiver: |
517 | if (auto *Receiver = ObjCMsgExpr->getInstanceReceiver()) { |
518 | E = Receiver->IgnoreParenCasts(); |
519 | continue; |
520 | } |
521 | return IsOwnedResult::Unknown; |
522 | case RetEffect::NoRetHard: |
523 | return IsOwnedResult::Unknown; |
524 | } |
525 | } |
526 | if (auto *CXXCE = dyn_cast<CXXMemberCallExpr>(Val: E)) { |
527 | if (auto *MD = CXXCE->getMethodDecl()) { |
528 | auto *Cls = MD->getParent(); |
529 | if (auto *CD = dyn_cast<CXXConversionDecl>(Val: MD)) { |
530 | auto QT = CD->getConversionType().getCanonicalType(); |
531 | auto *ResultType = QT.getTypePtrOrNull(); |
532 | if (isRetainPtr(Name: safeGetName(ASTNode: Cls)) && ResultType && |
533 | (ResultType->isPointerType() || ResultType->isReferenceType() || |
534 | ResultType->isObjCObjectPointerType())) |
535 | return IsOwnedResult::NotOwned; |
536 | } |
537 | if (safeGetName(ASTNode: MD) == "leakRef" && isRetainPtr(Name: safeGetName(ASTNode: Cls))) |
538 | return IsOwnedResult::Owned; |
539 | } |
540 | } |
541 | if (auto *CE = dyn_cast<CallExpr>(Val: E)) { |
542 | if (auto *Callee = CE->getDirectCallee()) { |
543 | if (isAdoptFn(FnDecl: Callee)) |
544 | return IsOwnedResult::NotOwned; |
545 | auto Name = safeGetName(ASTNode: Callee); |
546 | if (Name == "__builtin___CFStringMakeConstantString" ) |
547 | return IsOwnedResult::NotOwned; |
548 | if ((Name == "checked_cf_cast" || Name == "dynamic_cf_cast" || |
549 | Name == "checked_objc_cast" || Name == "dynamic_objc_cast" ) && |
550 | CE->getNumArgs() == 1) { |
551 | E = CE->getArg(Arg: 0)->IgnoreParenCasts(); |
552 | continue; |
553 | } |
554 | auto RetType = Callee->getReturnType(); |
555 | if (isRetainPtrType(T: RetType)) |
556 | return IsOwnedResult::NotOwned; |
557 | if (isCreateOrCopyFunction(FnDecl: Callee)) { |
558 | CreateOrCopyFnCall.insert(V: CE); |
559 | return IsOwnedResult::Owned; |
560 | } |
561 | } else if (auto *CalleeExpr = CE->getCallee()) { |
562 | if (isa<CXXDependentScopeMemberExpr>(Val: CalleeExpr)) |
563 | return IsOwnedResult::Skip; // Wait for instantiation. |
564 | if (isa<UnresolvedLookupExpr>(Val: CalleeExpr)) |
565 | return IsOwnedResult::Skip; // Wait for instantiation. |
566 | } |
567 | auto Summary = Summaries->getSummary(C: AnyCall(CE)); |
568 | auto RetEffect = Summary->getRetEffect(); |
569 | switch (RetEffect.getKind()) { |
570 | case RetEffect::NoRet: |
571 | return IsOwnedResult::Unknown; |
572 | case RetEffect::OwnedSymbol: |
573 | return IsOwnedResult::Owned; |
574 | case RetEffect::NotOwnedSymbol: |
575 | return IsOwnedResult::NotOwned; |
576 | case RetEffect::OwnedWhenTrackedReceiver: |
577 | return IsOwnedResult::Unknown; |
578 | case RetEffect::NoRetHard: |
579 | return IsOwnedResult::Unknown; |
580 | } |
581 | } |
582 | break; |
583 | } |
584 | return IsOwnedResult::Unknown; |
585 | } |
586 | |
587 | void reportUseAfterFree(const std::string &Name, const CallExpr *CE, |
588 | const Decl *DeclWithIssue, |
589 | const char *condition = nullptr) const { |
590 | SmallString<100> Buf; |
591 | llvm::raw_svector_ostream Os(Buf); |
592 | |
593 | Os << "Incorrect use of " << Name |
594 | << ". The argument is +0 and results in an use-after-free" ; |
595 | if (condition) |
596 | Os << " " << condition; |
597 | Os << "." ; |
598 | |
599 | assert(BR && "expected nonnull BugReporter" ); |
600 | PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(), |
601 | BR->getSourceManager()); |
602 | auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc); |
603 | Report->addRange(R: CE->getSourceRange()); |
604 | Report->setDeclWithIssue(DeclWithIssue); |
605 | BR->emitReport(R: std::move(Report)); |
606 | } |
607 | |
608 | void reportLeak(std::string &Name, const CXXConstructExpr *CE, |
609 | const Decl *DeclWithIssue, |
610 | const char *condition = nullptr) const { |
611 | SmallString<100> Buf; |
612 | llvm::raw_svector_ostream Os(Buf); |
613 | |
614 | Os << "Incorrect use of " << Name |
615 | << ". The argument is +1 and results in a memory leak" ; |
616 | if (condition) |
617 | Os << " " << condition; |
618 | Os << "." ; |
619 | |
620 | assert(BR && "expected nonnull BugReporter" ); |
621 | PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(), |
622 | BR->getSourceManager()); |
623 | auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc); |
624 | Report->addRange(R: CE->getSourceRange()); |
625 | Report->setDeclWithIssue(DeclWithIssue); |
626 | BR->emitReport(R: std::move(Report)); |
627 | } |
628 | |
629 | template <typename ExprType> |
630 | void reportLeak(const ExprType *E, const Decl *DeclWithIssue) const { |
631 | SmallString<100> Buf; |
632 | llvm::raw_svector_ostream Os(Buf); |
633 | |
634 | Os << "The return value is +1 and results in a memory leak." ; |
635 | |
636 | PathDiagnosticLocation BSLoc(E->getSourceRange().getBegin(), |
637 | BR->getSourceManager()); |
638 | auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc); |
639 | Report->addRange(R: E->getSourceRange()); |
640 | Report->setDeclWithIssue(DeclWithIssue); |
641 | BR->emitReport(R: std::move(Report)); |
642 | } |
643 | }; |
644 | } // namespace |
645 | |
646 | void ento::registerRetainPtrCtorAdoptChecker(CheckerManager &Mgr) { |
647 | Mgr.registerChecker<RetainPtrCtorAdoptChecker>(); |
648 | } |
649 | |
650 | bool ento::shouldRegisterRetainPtrCtorAdoptChecker(const CheckerManager &mgr) { |
651 | return true; |
652 | } |
653 | |