19#include "llvm/ADT/DenseSet.h"
27class RetainPtrCtorAdoptChecker
28 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
31 mutable BugReporter *BR =
nullptr;
32 mutable std::unique_ptr<RetainSummaryManager> Summaries;
33 mutable llvm::DenseSet<const ValueDecl *> CreateOrCopyOutArguments;
34 mutable llvm::DenseSet<const Expr *> CreateOrCopyFnCall;
35 mutable RetainTypeChecker RTC;
38 RetainPtrCtorAdoptChecker()
39 : Bug(this,
"Correct use of RetainPtr, adoptNS, and adoptCF",
40 "WebKit coding guidelines") {}
42 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
43 BugReporter &BRArg)
const {
49 struct LocalVisitor :
public RecursiveASTVisitor<LocalVisitor> {
50 const RetainPtrCtorAdoptChecker *Checker;
51 Decl *DeclWithIssue{
nullptr};
53 using Base = RecursiveASTVisitor<LocalVisitor>;
55 explicit LocalVisitor(
const RetainPtrCtorAdoptChecker *Checker)
60 bool shouldVisitTemplateInstantiations()
const {
return true; }
61 bool shouldVisitImplicitCode()
const {
return false; }
63 bool TraverseDecl(Decl *D) {
64 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
67 return Base::TraverseDecl(D);
70 bool TraverseClassTemplateDecl(ClassTemplateDecl *CTD) {
73 return Base::TraverseClassTemplateDecl(CTD);
76 bool VisitTypedefDecl(TypedefDecl *TD) {
81 bool VisitCallExpr(
const CallExpr *CE) {
82 Checker->visitCallExpr(CE, DeclWithIssue);
86 bool VisitCXXConstructExpr(
const CXXConstructExpr *CE) {
87 Checker->visitConstructExpr(CE, DeclWithIssue);
91 bool VisitObjCMessageExpr(
const ObjCMessageExpr *ObjCMsgExpr) {
92 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
96 bool VisitReturnStmt(
const ReturnStmt *RS) {
97 Checker->visitReturnStmt(RS, DeclWithIssue);
101 bool VisitVarDecl(
const VarDecl *VD) {
102 Checker->visitVarDecl(VD);
106 bool VisitBinaryOperator(
const BinaryOperator *BO) {
107 Checker->visitBinaryOperator(BO);
112 LocalVisitor visitor(
this);
113 Summaries = std::make_unique<RetainSummaryManager>(
116 RTC.visitTranslationUnitDecl(TUD);
117 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
120 bool isAdoptFn(
const Decl *FnDecl)
const {
124 bool isAdoptFnName(
const std::string &Name)
const {
125 return isAdoptNS(Name) || Name ==
"adoptCF" || Name ==
"adoptCFArc" ||
126 Name ==
"adoptCFNullable" || Name ==
"adoptCFNullableArc" ||
127 Name ==
"adoptOSObject" || Name ==
"adoptOSObjectArc";
130 bool isAdoptNS(
const std::string &Name)
const {
131 return Name ==
"adoptNS" || Name ==
"adoptNSArc" ||
132 Name ==
"adoptNSNullable" || Name ==
"adoptNSNullableArc";
135 void visitCallExpr(
const CallExpr *CE,
const Decl *DeclWithIssue)
const {
136 assert(BR &&
"expected nonnull BugReporter");
137 if (BR->getSourceManager().isInSystemHeader(CE->
getExprLoc()))
143 if (isAdoptFnName(FnName))
144 checkAdoptCall(CE, FnName, DeclWithIssue);
146 checkCreateOrCopyFunction(CE, DeclWithIssue);
147 checkBridgingRelease(CE, F, DeclWithIssue);
156 if (
auto *UnresolvedExpr = dyn_cast<UnresolvedLookupExpr>(CalleeExpr)) {
157 auto Name = UnresolvedExpr->getName();
158 if (!Name.isIdentifier())
160 FnName = Name.getAsString();
161 if (isAdoptFnName(FnName))
162 checkAdoptCall(CE, FnName, DeclWithIssue);
164 checkCreateOrCopyFunction(CE, DeclWithIssue);
167 void checkAdoptCall(
const CallExpr *CE,
const std::string &FnName,
168 const Decl *DeclWithIssue)
const {
173 auto Result = isOwned(Arg);
174 if (
Result == IsOwnedResult::Unknown)
175 Result = IsOwnedResult::NotOwned;
177 const Expr *Inner =
nullptr;
178 if (
isAllocInit(Arg, &Inner) || isCreateOrCopy(Arg)) {
180 CreateOrCopyFnCall.insert(Inner);
181 CreateOrCopyFnCall.insert(Arg);
184 if (
Result == IsOwnedResult::Owned ||
Result == IsOwnedResult::Skip ||
186 CreateOrCopyFnCall.insert(Arg);
190 if (
auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
191 if (CreateOrCopyOutArguments.contains(DRE->getDecl()))
194 if (RTC.isARCEnabled() && isAdoptFnName(FnName))
195 reportUseAfterFree(FnName, CE, DeclWithIssue,
"when ARC is disabled");
197 reportUseAfterFree(FnName, CE, DeclWithIssue);
200 void visitObjCMessageExpr(
const ObjCMessageExpr *ObjCMsgExpr,
201 const Decl *DeclWithIssue)
const {
202 if (BR->getSourceManager().isInSystemHeader(ObjCMsgExpr->
getExprLoc()))
206 if (Selector.getAsString() ==
"autorelease") {
210 ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Receiver);
213 const Expr *Inner =
nullptr;
216 CreateOrCopyFnCall.insert(ObjCMsgExpr);
218 CreateOrCopyFnCall.insert(Inner);
222 const Expr *Inner =
nullptr;
225 if (RTC.isARCEnabled())
227 if (CreateOrCopyFnCall.contains(ObjCMsgExpr))
230 CreateOrCopyFnCall.insert(Inner);
231 reportLeak(ObjCMsgExpr, DeclWithIssue);
234 void checkCreateOrCopyFunction(
const CallExpr *CE,
235 const Decl *DeclWithIssue)
const {
238 auto *FnDecl = CalleeDecl ? CalleeDecl->
getAsFunction() :
nullptr;
239 for (
unsigned ArgIndex = 0; ArgIndex < ArgCount; ++ArgIndex) {
241 auto *Unary = dyn_cast<UnaryOperator>(Arg);
244 if (Unary->getOpcode() != UO_AddrOf)
246 auto *SubExpr = Unary->getSubExpr();
249 auto *DRE = dyn_cast<DeclRefExpr>(SubExpr->IgnoreParenCasts());
252 auto *
Decl = DRE->getDecl();
255 if (FnDecl && ArgIndex < FnDecl->getNumParams()) {
258 auto *ParamDecl = FnDecl->getParamDecl(ArgIndex);
259 if (ParamDecl->hasAttr<CFReturnsRetainedAttr>())
260 CreateOrCopyOutArguments.insert(Decl);
264 if (RTC.isUnretained(
Decl->getType()))
265 CreateOrCopyOutArguments.insert(Decl);
268 auto Summary = Summaries->getSummary(AnyCall(CE));
269 switch (Summary->getRetEffect().getKind()) {
272 if (!CreateOrCopyFnCall.contains(CE))
273 reportLeak(CE, DeclWithIssue);
280 void checkBridgingRelease(
const CallExpr *CE,
const FunctionDecl *Callee,
281 const Decl *DeclWithIssue)
const {
286 auto *InnerCE = dyn_cast<CallExpr>(Arg);
290 auto *InnerF = InnerCE->getDirectCallee();
291 if (!InnerF || !isCreateOrCopyFunction(InnerF))
294 CreateOrCopyFnCall.insert(InnerCE);
297 void visitConstructExpr(
const CXXConstructExpr *CE,
298 const Decl *DeclWithIssue)
const {
299 assert(BR &&
"expected nonnull BugReporter");
300 if (BR->getSourceManager().isInSystemHeader(CE->
getExprLoc()))
315 if (isAdoptFn(DeclWithIssue) ||
safeGetName(DeclWithIssue) ==
"retainPtr")
318 std::string Name =
"RetainPtr constructor";
320 auto Result = isOwned(Arg);
322 if (isCreateOrCopy(Arg))
323 CreateOrCopyFnCall.insert(Arg);
325 const Expr *Inner =
nullptr;
327 CreateOrCopyFnCall.insert(Arg);
329 CreateOrCopyFnCall.insert(Inner);
332 if (
Result == IsOwnedResult::Skip)
335 if (
Result == IsOwnedResult::Unknown)
336 Result = IsOwnedResult::NotOwned;
337 if (
Result == IsOwnedResult::Owned)
338 reportLeak(Name, CE, DeclWithIssue);
340 reportLeak(Name, CE, DeclWithIssue,
"when ARC is disabled");
341 else if (isCreateOrCopy(Arg))
342 reportLeak(Name, CE, DeclWithIssue);
345 void visitVarDecl(
const VarDecl *VD)
const {
347 if (!
Init || !RTC.isARCEnabled())
350 const Expr *Inner =
nullptr;
352 CreateOrCopyFnCall.insert(
Init);
354 CreateOrCopyFnCall.insert(Inner);
358 void visitBinaryOperator(
const BinaryOperator *BO)
const {
364 const Expr *Inner =
nullptr;
366 CreateOrCopyFnCall.insert(RHS);
368 CreateOrCopyFnCall.insert(Inner);
372 auto *UO = dyn_cast<UnaryOperator>(LHS);
375 auto OpCode = UO->getOpcode();
376 if (OpCode != UO_Deref)
378 auto *DerefTarget = UO->getSubExpr();
381 DerefTarget = DerefTarget->IgnoreParenCasts();
382 auto *DRE = dyn_cast<DeclRefExpr>(DerefTarget);
385 auto *
Decl = DRE->getDecl();
390 if (
Decl->hasAttr<CFReturnsRetainedAttr>())
391 CreateOrCopyFnCall.insert(RHS);
394 void visitReturnStmt(
const ReturnStmt *RS,
const Decl *DeclWithIssue)
const {
401 std::optional<bool> retainsRet;
402 if (
auto *FnDecl = dyn_cast<FunctionDecl>(DeclWithIssue))
403 retainsRet = retainsReturnValue(FnDecl);
404 else if (
auto *MethodDecl = dyn_cast<ObjCMethodDecl>(DeclWithIssue))
405 retainsRet = retainsReturnValue(MethodDecl);
408 if (!retainsRet || !*retainsRet) {
410 if (RTC.isUnretained(
RetValue->getType()))
413 if (retainsRet && *retainsRet) {
414 CreateOrCopyFnCall.insert(
RetValue);
417 if (
auto *CE = dyn_cast<CallExpr>(
RetValue)) {
418 auto *
Callee = CE->getDirectCallee();
419 if (!Callee || !isCreateOrCopyFunction(Callee))
421 CreateOrCopyFnCall.insert(CE);
424 const Expr *Inner =
nullptr;
426 CreateOrCopyFnCall.insert(
RetValue);
428 CreateOrCopyFnCall.insert(Inner);
432 template <
typename CallableType>
433 std::optional<bool> retainsReturnValue(
const CallableType *FnDecl)
const {
434 auto Summary = Summaries->getSummary(AnyCall(FnDecl));
435 auto RetEffect = Summary->getRetEffect();
436 switch (RetEffect.getKind()) {
451 bool isCreateOrCopy(
const Expr *E)
const {
452 auto *CE = dyn_cast<CallExpr>(E);
455 auto *
Callee = CE->getDirectCallee();
458 return isCreateOrCopyFunction(Callee);
461 bool isCreateOrCopyFunction(
const FunctionDecl *FnDecl)
const {
463 return CalleeName.find(
"Create") != std::string::npos ||
464 CalleeName.find(
"Copy") != std::string::npos;
467 enum class IsOwnedResult {
Unknown,
Skip, Owned, NotOwned };
468 IsOwnedResult isOwned(
const Expr *E)
const {
470 if (
auto *POE = dyn_cast<PseudoObjectExpr>(E)) {
471 if (
unsigned SemanticExprCount = POE->getNumSemanticExprs()) {
472 E = POE->getSemanticExpr(SemanticExprCount - 1);
477 return IsOwnedResult::NotOwned;
478 if (
auto *DRE = dyn_cast<DeclRefExpr>(E)) {
479 auto QT = DRE->getType();
481 return IsOwnedResult::NotOwned;
482 QT = QT.getCanonicalType();
483 if (RTC.isUnretained(QT,
true ))
484 return IsOwnedResult::NotOwned;
485 auto *PointeeType = QT->getPointeeType().getTypePtrOrNull();
486 if (PointeeType && PointeeType->isVoidType())
487 return IsOwnedResult::NotOwned;
489 if (
auto *TE = dyn_cast<CXXBindTemporaryExpr>(E)) {
490 E = TE->getSubExpr();
493 if (
auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
494 auto Summary = Summaries->getSummary(AnyCall(ObjCMsgExpr));
495 auto RetEffect = Summary->getRetEffect();
496 switch (RetEffect.getKind()) {
498 return IsOwnedResult::Unknown;
500 return IsOwnedResult::Owned;
502 return IsOwnedResult::NotOwned;
508 return IsOwnedResult::Unknown;
510 return IsOwnedResult::Unknown;
513 if (
auto *CXXCE = dyn_cast<CXXMemberCallExpr>(E)) {
514 if (
auto *MD = CXXCE->getMethodDecl()) {
515 auto *Cls = MD->getParent();
516 if (
auto *CD = dyn_cast<CXXConversionDecl>(MD)) {
517 auto QT = CD->getConversionType().getCanonicalType();
518 auto *ResultType = QT.getTypePtrOrNull();
520 (ResultType->isPointerType() || ResultType->isReferenceType() ||
521 ResultType->isObjCObjectPointerType()))
522 return IsOwnedResult::NotOwned;
526 return IsOwnedResult::Owned;
529 if (
auto *CE = dyn_cast<CallExpr>(E)) {
530 if (
auto *Callee = CE->getDirectCallee()) {
531 if (isAdoptFn(Callee))
532 return IsOwnedResult::NotOwned;
534 if (Name ==
"__builtin___CFStringMakeConstantString")
535 return IsOwnedResult::NotOwned;
536 if ((Name ==
"checked_cf_cast" || Name ==
"dynamic_cf_cast" ||
537 Name ==
"checked_objc_cast" || Name ==
"dynamic_objc_cast") &&
542 auto RetType =
Callee->getReturnType();
544 return IsOwnedResult::NotOwned;
545 if (isCreateOrCopyFunction(Callee)) {
546 CreateOrCopyFnCall.insert(CE);
547 return IsOwnedResult::Owned;
549 }
else if (
auto *CalleeExpr = CE->getCallee()) {
551 return IsOwnedResult::Skip;
553 return IsOwnedResult::Skip;
555 auto Summary = Summaries->getSummary(AnyCall(CE));
556 auto RetEffect = Summary->getRetEffect();
557 switch (RetEffect.getKind()) {
559 return IsOwnedResult::Unknown;
561 return IsOwnedResult::Owned;
563 return IsOwnedResult::NotOwned;
565 return IsOwnedResult::Unknown;
567 return IsOwnedResult::Unknown;
572 return IsOwnedResult::Unknown;
575 void reportUseAfterFree(
const std::string &Name,
const CallExpr *CE,
576 const Decl *DeclWithIssue,
578 SmallString<100> Buf;
579 llvm::raw_svector_ostream Os(Buf);
581 Os <<
"Incorrect use of " << Name
582 <<
"; the argument is +0 and results in an use-after-free";
586 assert(BR &&
"expected nonnull BugReporter");
588 BR->getSourceManager());
589 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
591 Report->setDeclWithIssue(DeclWithIssue);
592 BR->emitReport(std::move(
Report));
595 void reportLeak(std::string &Name,
const CXXConstructExpr *CE,
596 const Decl *DeclWithIssue,
598 SmallString<100> Buf;
599 llvm::raw_svector_ostream Os(Buf);
601 Os <<
"Incorrect use of " << Name
602 <<
"; the argument is +1 and results in a memory leak";
606 assert(BR &&
"expected nonnull BugReporter");
608 BR->getSourceManager());
609 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
611 Report->setDeclWithIssue(DeclWithIssue);
612 BR->emitReport(std::move(
Report));
615 template <
typename ExprType>
616 void reportLeak(
const ExprType *E,
const Decl *DeclWithIssue)
const {
617 SmallString<100> Buf;
618 llvm::raw_svector_ostream Os(Buf);
620 Os <<
"The return value is +1 and results in a memory leak.";
622 PathDiagnosticLocation BSLoc(E->getSourceRange().getBegin(),
623 BR->getSourceManager());
624 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
625 Report->addRange(E->getSourceRange());
626 Report->setDeclWithIssue(DeclWithIssue);
627 BR->emitReport(std::move(
Report));
632void ento::registerRetainPtrCtorAdoptChecker(
CheckerManager &Mgr) {
636bool ento::shouldRegisterRetainPtrCtorAdoptChecker(
const CheckerManager &mgr) {
static PRESERVE_NONE bool RetValue(InterpState &S)
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a an optional score condition
Defines the SourceManager interface.
static bool isAssignmentOp(Opcode Opc)
Expr * getArg(unsigned Arg)
Return the specified argument.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
FunctionDecl * getAsFunction() LLVM_READONLY
Returns the function itself, or the templated function if this is a function template.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Selector getSelector() const
void visitTypedef(const TypedefDecl *)
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
ASTContext & getASTContext() const
const Expr * getInit() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
@ OwnedSymbol
Indicates that the returned value is an owned (+1) symbol.
@ OwnedWhenTrackedReceiver
Indicates that the return value is an owned object when the receiver is also a tracked object.
@ NoRet
Indicates that no retain count information is tracked for the return value.
@ NotOwnedSymbol
Indicates that the returned value is an object with retain count semantics but that it is not owned (...
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
bool isRetainPtrOrOSPtrType(const clang::QualType T)
@ Result
The result type of a method or function.
bool isRetainPtrOrOSPtr(const std::string &Name)
std::string safeGetName(const T *ASTNode)
bool isNullPtr(const clang::Expr *E)
bool isAllocInit(const Expr *E, const Expr **InnerExpr)