18#include "llvm/ADT/DenseSet.h"
26class RetainPtrCtorAdoptChecker
27 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
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;
37 RetainPtrCtorAdoptChecker()
38 : Bug(this,
"Correct use of RetainPtr, adoptNS, and adoptCF",
39 "WebKit coding guidelines") {}
41 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42 BugReporter &BRArg)
const {
48 struct LocalVisitor :
public RecursiveASTVisitor<LocalVisitor> {
49 const RetainPtrCtorAdoptChecker *Checker;
50 Decl *DeclWithIssue{
nullptr};
52 using Base = RecursiveASTVisitor<LocalVisitor>;
54 explicit LocalVisitor(
const RetainPtrCtorAdoptChecker *Checker)
59 bool shouldVisitTemplateInstantiations()
const {
return true; }
60 bool shouldVisitImplicitCode()
const {
return false; }
62 bool TraverseDecl(Decl *D) {
63 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
66 return Base::TraverseDecl(D);
69 bool TraverseClassTemplateDecl(ClassTemplateDecl *CTD) {
72 return Base::TraverseClassTemplateDecl(CTD);
75 bool VisitTypedefDecl(TypedefDecl *TD) {
80 bool VisitCallExpr(
const CallExpr *CE) {
81 Checker->visitCallExpr(CE, DeclWithIssue);
85 bool VisitCXXConstructExpr(
const CXXConstructExpr *CE) {
86 Checker->visitConstructExpr(CE, DeclWithIssue);
90 bool VisitObjCMessageExpr(
const ObjCMessageExpr *ObjCMsgExpr) {
91 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
95 bool VisitReturnStmt(
const ReturnStmt *RS) {
96 Checker->visitReturnStmt(RS, DeclWithIssue);
100 bool VisitVarDecl(
const VarDecl *VD) {
101 Checker->visitVarDecl(VD);
105 bool VisitBinaryOperator(
const BinaryOperator *BO) {
106 Checker->visitBinaryOperator(BO);
111 LocalVisitor visitor(
this);
112 Summaries = std::make_unique<RetainSummaryManager>(
115 RTC.visitTranslationUnitDecl(TUD);
116 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
119 bool isAdoptFn(
const Decl *FnDecl)
const {
123 bool isAdoptFnName(
const std::string &Name)
const {
124 return isAdoptNS(Name) || Name ==
"adoptCF" || Name ==
"adoptCFArc" ||
125 Name ==
"adoptOSObject" || Name ==
"adoptOSObjectArc";
128 bool isAdoptNS(
const std::string &Name)
const {
129 return Name ==
"adoptNS" || Name ==
"adoptNSArc";
132 void visitCallExpr(
const CallExpr *CE,
const Decl *DeclWithIssue)
const {
133 assert(BR &&
"expected nonnull BugReporter");
134 if (BR->getSourceManager().isInSystemHeader(CE->
getExprLoc()))
140 if (isAdoptFnName(FnName))
141 checkAdoptCall(CE, FnName, DeclWithIssue);
143 checkCreateOrCopyFunction(CE, DeclWithIssue);
144 checkBridgingRelease(CE, F, DeclWithIssue);
153 if (
auto *UnresolvedExpr = dyn_cast<UnresolvedLookupExpr>(CalleeExpr)) {
154 auto Name = UnresolvedExpr->getName();
155 if (!Name.isIdentifier())
157 FnName = Name.getAsString();
158 if (isAdoptFnName(FnName))
159 checkAdoptCall(CE, FnName, DeclWithIssue);
161 checkCreateOrCopyFunction(CE, DeclWithIssue);
164 void checkAdoptCall(
const CallExpr *CE,
const std::string &FnName,
165 const Decl *DeclWithIssue)
const {
170 auto Result = isOwned(Arg);
171 if (
Result == IsOwnedResult::Unknown)
172 Result = IsOwnedResult::NotOwned;
174 const Expr *Inner =
nullptr;
175 if (isAllocInit(Arg, &Inner) || isCreateOrCopy(Arg)) {
177 CreateOrCopyFnCall.insert(Inner);
178 CreateOrCopyFnCall.insert(Arg);
181 if (
Result == IsOwnedResult::Owned ||
Result == IsOwnedResult::Skip ||
183 CreateOrCopyFnCall.insert(Arg);
187 if (
auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
188 if (CreateOrCopyOutArguments.contains(DRE->getDecl()))
191 if (RTC.isARCEnabled() && isAdoptFnName(FnName))
192 reportUseAfterFree(FnName, CE, DeclWithIssue,
"when ARC is disabled");
194 reportUseAfterFree(FnName, CE, DeclWithIssue);
197 void visitObjCMessageExpr(
const ObjCMessageExpr *ObjCMsgExpr,
198 const Decl *DeclWithIssue)
const {
199 if (BR->getSourceManager().isInSystemHeader(ObjCMsgExpr->
getExprLoc()))
203 if (Selector.getAsString() ==
"autorelease") {
207 ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Receiver);
210 const Expr *Inner =
nullptr;
211 if (!isAllocInit(ObjCMsgExpr, &Inner))
213 CreateOrCopyFnCall.insert(ObjCMsgExpr);
215 CreateOrCopyFnCall.insert(Inner);
219 const Expr *Inner =
nullptr;
220 if (!isAllocInit(ObjCMsgExpr, &Inner))
222 if (RTC.isARCEnabled())
224 if (CreateOrCopyFnCall.contains(ObjCMsgExpr))
227 CreateOrCopyFnCall.insert(Inner);
228 reportLeak(ObjCMsgExpr, DeclWithIssue);
231 void checkCreateOrCopyFunction(
const CallExpr *CE,
232 const Decl *DeclWithIssue)
const {
235 auto *FnDecl = CalleeDecl ? CalleeDecl->
getAsFunction() :
nullptr;
236 for (
unsigned ArgIndex = 0; ArgIndex < ArgCount; ++ArgIndex) {
238 auto *Unary = dyn_cast<UnaryOperator>(Arg);
241 if (Unary->getOpcode() != UO_AddrOf)
243 auto *SubExpr = Unary->getSubExpr();
246 auto *DRE = dyn_cast<DeclRefExpr>(SubExpr->IgnoreParenCasts());
249 auto *
Decl = DRE->getDecl();
252 if (FnDecl && ArgIndex < FnDecl->getNumParams()) {
255 auto *ParamDecl = FnDecl->getParamDecl(ArgIndex);
256 if (ParamDecl->hasAttr<CFReturnsRetainedAttr>())
257 CreateOrCopyOutArguments.insert(Decl);
261 if (RTC.isUnretained(
Decl->getType()))
262 CreateOrCopyOutArguments.insert(Decl);
265 auto Summary = Summaries->getSummary(AnyCall(CE));
266 switch (Summary->getRetEffect().getKind()) {
269 if (!CreateOrCopyFnCall.contains(CE))
270 reportLeak(CE, DeclWithIssue);
277 void checkBridgingRelease(
const CallExpr *CE,
const FunctionDecl *Callee,
278 const Decl *DeclWithIssue)
const {
283 auto *InnerCE = dyn_cast<CallExpr>(Arg);
287 auto *InnerF = InnerCE->getDirectCallee();
288 if (!InnerF || !isCreateOrCopyFunction(InnerF))
291 CreateOrCopyFnCall.insert(InnerCE);
294 void visitConstructExpr(
const CXXConstructExpr *CE,
295 const Decl *DeclWithIssue)
const {
296 assert(BR &&
"expected nonnull BugReporter");
297 if (BR->getSourceManager().isInSystemHeader(CE->
getExprLoc()))
312 if (isAdoptFn(DeclWithIssue) ||
safeGetName(DeclWithIssue) ==
"retainPtr")
315 std::string Name =
"RetainPtr constructor";
317 auto Result = isOwned(Arg);
319 if (isCreateOrCopy(Arg))
320 CreateOrCopyFnCall.insert(Arg);
322 const Expr *Inner =
nullptr;
323 if (isAllocInit(Arg, &Inner)) {
324 CreateOrCopyFnCall.insert(Arg);
326 CreateOrCopyFnCall.insert(Inner);
329 if (
Result == IsOwnedResult::Skip)
332 if (
Result == IsOwnedResult::Unknown)
333 Result = IsOwnedResult::NotOwned;
334 if (
Result == IsOwnedResult::Owned)
335 reportLeak(Name, CE, DeclWithIssue);
336 else if (RTC.isARCEnabled() && isAllocInit(Arg))
337 reportLeak(Name, CE, DeclWithIssue,
"when ARC is disabled");
338 else if (isCreateOrCopy(Arg))
339 reportLeak(Name, CE, DeclWithIssue);
342 void visitVarDecl(
const VarDecl *VD)
const {
344 if (!
Init || !RTC.isARCEnabled())
347 const Expr *Inner =
nullptr;
348 if (isAllocInit(
Init, &Inner)) {
349 CreateOrCopyFnCall.insert(
Init);
351 CreateOrCopyFnCall.insert(Inner);
355 void visitBinaryOperator(
const BinaryOperator *BO)
const {
361 const Expr *Inner =
nullptr;
362 if (isAllocInit(RHS, &Inner)) {
363 CreateOrCopyFnCall.insert(RHS);
365 CreateOrCopyFnCall.insert(Inner);
369 void visitReturnStmt(
const ReturnStmt *RS,
const Decl *DeclWithIssue)
const {
376 std::optional<bool> retainsRet;
377 if (
auto *FnDecl = dyn_cast<FunctionDecl>(DeclWithIssue))
378 retainsRet = retainsReturnValue(FnDecl);
379 else if (
auto *MethodDecl = dyn_cast<ObjCMethodDecl>(DeclWithIssue))
380 retainsRet = retainsReturnValue(MethodDecl);
383 if (!retainsRet || !*retainsRet) {
385 if (RTC.isUnretained(
RetValue->getType()))
388 if (retainsRet && *retainsRet) {
389 CreateOrCopyFnCall.insert(
RetValue);
392 if (
auto *CE = dyn_cast<CallExpr>(
RetValue)) {
393 auto *
Callee = CE->getDirectCallee();
394 if (!Callee || !isCreateOrCopyFunction(Callee))
396 CreateOrCopyFnCall.insert(CE);
399 const Expr *Inner =
nullptr;
400 if (isAllocInit(
RetValue, &Inner)) {
401 CreateOrCopyFnCall.insert(
RetValue);
403 CreateOrCopyFnCall.insert(Inner);
407 template <
typename CallableType>
408 std::optional<bool> retainsReturnValue(
const CallableType *FnDecl)
const {
409 auto Summary = Summaries->getSummary(AnyCall(FnDecl));
410 auto RetEffect = Summary->getRetEffect();
411 switch (RetEffect.getKind()) {
426 bool isAllocInit(
const Expr *E,
const Expr **InnerExpr =
nullptr)
const {
427 auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E);
428 if (
auto *POE = dyn_cast<PseudoObjectExpr>(E)) {
429 if (
unsigned ExprCount = POE->getNumSemanticExprs()) {
430 auto *Expr = POE->getSemanticExpr(ExprCount - 1)->IgnoreParenCasts();
431 ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Expr);
433 *InnerExpr = ObjCMsgExpr;
440 if (NameForFirstSlot ==
"alloc" || NameForFirstSlot.starts_with(
"copy") ||
441 NameForFirstSlot.starts_with(
"mutableCopy"))
443 if (!NameForFirstSlot.starts_with(
"init") &&
444 !NameForFirstSlot.starts_with(
"_init"))
452 if (
auto *Inner = dyn_cast<ObjCMessageExpr>(Receiver)) {
455 auto InnerSelector = Inner->getSelector();
456 return InnerSelector.getNameForSlot(0) ==
"alloc";
457 }
else if (
auto *CE = dyn_cast<CallExpr>(Receiver)) {
460 if (
auto *Callee = CE->getDirectCallee()) {
461 if (
Callee->getDeclName().isIdentifier()) {
462 auto CalleeName =
Callee->getName();
463 return CalleeName.starts_with(
"alloc");
470 bool isCreateOrCopy(
const Expr *E)
const {
471 auto *CE = dyn_cast<CallExpr>(E);
474 auto *
Callee = CE->getDirectCallee();
477 return isCreateOrCopyFunction(Callee);
480 bool isCreateOrCopyFunction(
const FunctionDecl *FnDecl)
const {
482 return CalleeName.find(
"Create") != std::string::npos ||
483 CalleeName.find(
"Copy") != std::string::npos;
486 enum class IsOwnedResult {
Unknown,
Skip, Owned, NotOwned };
487 IsOwnedResult isOwned(
const Expr *E)
const {
489 if (
auto *POE = dyn_cast<PseudoObjectExpr>(E)) {
490 if (
unsigned SemanticExprCount = POE->getNumSemanticExprs()) {
491 E = POE->getSemanticExpr(SemanticExprCount - 1);
496 return IsOwnedResult::NotOwned;
497 if (
auto *DRE = dyn_cast<DeclRefExpr>(E)) {
498 auto QT = DRE->getType();
500 return IsOwnedResult::NotOwned;
501 QT = QT.getCanonicalType();
502 if (RTC.isUnretained(QT,
true ))
503 return IsOwnedResult::NotOwned;
504 auto *PointeeType = QT->getPointeeType().getTypePtrOrNull();
505 if (PointeeType && PointeeType->isVoidType())
506 return IsOwnedResult::NotOwned;
508 if (
auto *TE = dyn_cast<CXXBindTemporaryExpr>(E)) {
509 E = TE->getSubExpr();
512 if (
auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
513 auto Summary = Summaries->getSummary(AnyCall(ObjCMsgExpr));
514 auto RetEffect = Summary->getRetEffect();
515 switch (RetEffect.getKind()) {
517 return IsOwnedResult::Unknown;
519 return IsOwnedResult::Owned;
521 return IsOwnedResult::NotOwned;
527 return IsOwnedResult::Unknown;
529 return IsOwnedResult::Unknown;
532 if (
auto *CXXCE = dyn_cast<CXXMemberCallExpr>(E)) {
533 if (
auto *MD = CXXCE->getMethodDecl()) {
534 auto *Cls = MD->getParent();
535 if (
auto *CD = dyn_cast<CXXConversionDecl>(MD)) {
536 auto QT = CD->getConversionType().getCanonicalType();
537 auto *ResultType = QT.getTypePtrOrNull();
539 (ResultType->isPointerType() || ResultType->isReferenceType() ||
540 ResultType->isObjCObjectPointerType()))
541 return IsOwnedResult::NotOwned;
545 return IsOwnedResult::Owned;
548 if (
auto *CE = dyn_cast<CallExpr>(E)) {
549 if (
auto *Callee = CE->getDirectCallee()) {
550 if (isAdoptFn(Callee))
551 return IsOwnedResult::NotOwned;
553 if (Name ==
"__builtin___CFStringMakeConstantString")
554 return IsOwnedResult::NotOwned;
555 if ((Name ==
"checked_cf_cast" || Name ==
"dynamic_cf_cast" ||
556 Name ==
"checked_objc_cast" || Name ==
"dynamic_objc_cast") &&
561 auto RetType =
Callee->getReturnType();
563 return IsOwnedResult::NotOwned;
564 if (isCreateOrCopyFunction(Callee)) {
565 CreateOrCopyFnCall.insert(CE);
566 return IsOwnedResult::Owned;
568 }
else if (
auto *CalleeExpr = CE->getCallee()) {
570 return IsOwnedResult::Skip;
572 return IsOwnedResult::Skip;
574 auto Summary = Summaries->getSummary(AnyCall(CE));
575 auto RetEffect = Summary->getRetEffect();
576 switch (RetEffect.getKind()) {
578 return IsOwnedResult::Unknown;
580 return IsOwnedResult::Owned;
582 return IsOwnedResult::NotOwned;
584 return IsOwnedResult::Unknown;
586 return IsOwnedResult::Unknown;
591 return IsOwnedResult::Unknown;
594 void reportUseAfterFree(
const std::string &Name,
const CallExpr *CE,
595 const Decl *DeclWithIssue,
596 const char *condition =
nullptr)
const {
597 SmallString<100> Buf;
598 llvm::raw_svector_ostream Os(Buf);
600 Os <<
"Incorrect use of " << Name
601 <<
". The argument is +0 and results in an use-after-free";
603 Os <<
" " << condition;
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 void reportLeak(std::string &Name,
const CXXConstructExpr *CE,
616 const Decl *DeclWithIssue,
617 const char *condition =
nullptr)
const {
618 SmallString<100> Buf;
619 llvm::raw_svector_ostream Os(Buf);
621 Os <<
"Incorrect use of " << Name
622 <<
". The argument is +1 and results in a memory leak";
624 Os <<
" " << condition;
627 assert(BR &&
"expected nonnull BugReporter");
629 BR->getSourceManager());
630 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
632 Report->setDeclWithIssue(DeclWithIssue);
633 BR->emitReport(std::move(
Report));
636 template <
typename ExprType>
637 void reportLeak(
const ExprType *E,
const Decl *DeclWithIssue)
const {
638 SmallString<100> Buf;
639 llvm::raw_svector_ostream Os(Buf);
641 Os <<
"The return value is +1 and results in a memory leak.";
643 PathDiagnosticLocation BSLoc(E->getSourceRange().getBegin(),
644 BR->getSourceManager());
645 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
646 Report->addRange(E->getSourceRange());
647 Report->setDeclWithIssue(DeclWithIssue);
648 BR->emitReport(std::move(
Report));
653void ento::registerRetainPtrCtorAdoptChecker(
CheckerManager &Mgr) {
657bool ento::shouldRegisterRetainPtrCtorAdoptChecker(
const CheckerManager &mgr) {
static bool RetValue(InterpState &S, CodePtr &Pt)
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
bool isInstanceMessage() const
Determine whether this is an instance message to either a computed object or to super.
void visitTypedef(const TypedefDecl *)
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
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.
The JSON file list parser is used to communicate input to InstallAPI.
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)