Generalize the check for recognizing [[Obj alloc] init] to also recognize [allocObj() init]. We do this by utilizing isAllocInit function in RetainPtrCtorAdoptChecker.
638 lines
21 KiB
C++
638 lines
21 KiB
C++
//=======- RetainPtrCtorAdoptChecker.cpp -------------------------*- C++ -*-==//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ASTUtils.h"
|
|
#include "PtrTypesSemantics.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
|
|
#include "clang/Analysis/RetainSummaryManager.h"
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
#include "llvm/ADT/DenseSet.h"
|
|
#include <optional>
|
|
|
|
using namespace clang;
|
|
using namespace ento;
|
|
|
|
namespace {
|
|
|
|
class RetainPtrCtorAdoptChecker
|
|
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
|
|
private:
|
|
BugType Bug;
|
|
mutable BugReporter *BR = nullptr;
|
|
mutable std::unique_ptr<RetainSummaryManager> Summaries;
|
|
mutable llvm::DenseSet<const ValueDecl *> CreateOrCopyOutArguments;
|
|
mutable llvm::DenseSet<const Expr *> CreateOrCopyFnCall;
|
|
mutable RetainTypeChecker RTC;
|
|
|
|
public:
|
|
RetainPtrCtorAdoptChecker()
|
|
: Bug(this, "Correct use of RetainPtr, adoptNS, and adoptCF",
|
|
"WebKit coding guidelines") {}
|
|
|
|
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
|
|
BugReporter &BRArg) const {
|
|
BR = &BRArg;
|
|
|
|
// The calls to checkAST* from AnalysisConsumer don't
|
|
// visit template instantiations or lambda classes. We
|
|
// want to visit those, so we make our own RecursiveASTVisitor.
|
|
struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
|
|
const RetainPtrCtorAdoptChecker *Checker;
|
|
Decl *DeclWithIssue{nullptr};
|
|
|
|
using Base = RecursiveASTVisitor<LocalVisitor>;
|
|
|
|
explicit LocalVisitor(const RetainPtrCtorAdoptChecker *Checker)
|
|
: Checker(Checker) {
|
|
assert(Checker);
|
|
}
|
|
|
|
bool shouldVisitTemplateInstantiations() const { return true; }
|
|
bool shouldVisitImplicitCode() const { return false; }
|
|
|
|
bool TraverseDecl(Decl *D) {
|
|
llvm::SaveAndRestore SavedDecl(DeclWithIssue);
|
|
if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
|
|
DeclWithIssue = D;
|
|
return Base::TraverseDecl(D);
|
|
}
|
|
|
|
bool TraverseClassTemplateDecl(ClassTemplateDecl *CTD) {
|
|
if (isRetainPtrOrOSPtr(safeGetName(CTD)))
|
|
return true; // Skip the contents of RetainPtr.
|
|
return Base::TraverseClassTemplateDecl(CTD);
|
|
}
|
|
|
|
bool VisitTypedefDecl(TypedefDecl *TD) {
|
|
Checker->RTC.visitTypedef(TD);
|
|
return true;
|
|
}
|
|
|
|
bool VisitCallExpr(const CallExpr *CE) {
|
|
Checker->visitCallExpr(CE, DeclWithIssue);
|
|
return true;
|
|
}
|
|
|
|
bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
|
|
Checker->visitConstructExpr(CE, DeclWithIssue);
|
|
return true;
|
|
}
|
|
|
|
bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) {
|
|
Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
|
|
return true;
|
|
}
|
|
|
|
bool VisitReturnStmt(const ReturnStmt *RS) {
|
|
Checker->visitReturnStmt(RS, DeclWithIssue);
|
|
return true;
|
|
}
|
|
|
|
bool VisitVarDecl(const VarDecl *VD) {
|
|
Checker->visitVarDecl(VD);
|
|
return true;
|
|
}
|
|
|
|
bool VisitBinaryOperator(const BinaryOperator *BO) {
|
|
Checker->visitBinaryOperator(BO);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
LocalVisitor visitor(this);
|
|
Summaries = std::make_unique<RetainSummaryManager>(
|
|
TUD->getASTContext(), true /* trackObjCAndCFObjects */,
|
|
false /* trackOSObjects */);
|
|
RTC.visitTranslationUnitDecl(TUD);
|
|
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
|
|
}
|
|
|
|
bool isAdoptFn(const Decl *FnDecl) const {
|
|
return isAdoptFnName(safeGetName(FnDecl));
|
|
}
|
|
|
|
bool isAdoptFnName(const std::string &Name) const {
|
|
return isAdoptNS(Name) || Name == "adoptCF" || Name == "adoptCFArc" ||
|
|
Name == "adoptOSObject" || Name == "adoptOSObjectArc";
|
|
}
|
|
|
|
bool isAdoptNS(const std::string &Name) const {
|
|
return Name == "adoptNS" || Name == "adoptNSArc";
|
|
}
|
|
|
|
void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
|
|
assert(BR && "expected nonnull BugReporter");
|
|
if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
|
|
return;
|
|
|
|
std::string FnName;
|
|
if (auto *F = CE->getDirectCallee()) {
|
|
FnName = safeGetName(F);
|
|
if (isAdoptFnName(FnName))
|
|
checkAdoptCall(CE, FnName, DeclWithIssue);
|
|
else {
|
|
checkCreateOrCopyFunction(CE, DeclWithIssue);
|
|
checkBridgingRelease(CE, F, DeclWithIssue);
|
|
}
|
|
return;
|
|
}
|
|
|
|
auto *CalleeExpr = CE->getCallee();
|
|
if (!CalleeExpr)
|
|
return;
|
|
CalleeExpr = CalleeExpr->IgnoreParenCasts();
|
|
if (auto *UnresolvedExpr = dyn_cast<UnresolvedLookupExpr>(CalleeExpr)) {
|
|
auto Name = UnresolvedExpr->getName();
|
|
if (!Name.isIdentifier())
|
|
return;
|
|
FnName = Name.getAsString();
|
|
if (isAdoptFnName(FnName))
|
|
checkAdoptCall(CE, FnName, DeclWithIssue);
|
|
}
|
|
checkCreateOrCopyFunction(CE, DeclWithIssue);
|
|
}
|
|
|
|
void checkAdoptCall(const CallExpr *CE, const std::string &FnName,
|
|
const Decl *DeclWithIssue) const {
|
|
if (!CE->getNumArgs())
|
|
return;
|
|
|
|
auto *Arg = CE->getArg(0)->IgnoreParenCasts();
|
|
auto Result = isOwned(Arg);
|
|
if (Result == IsOwnedResult::Unknown)
|
|
Result = IsOwnedResult::NotOwned;
|
|
|
|
const Expr *Inner = nullptr;
|
|
if (isAllocInit(Arg, &Inner) || isCreateOrCopy(Arg)) {
|
|
if (Inner)
|
|
CreateOrCopyFnCall.insert(Inner);
|
|
CreateOrCopyFnCall.insert(Arg); // Avoid double reporting.
|
|
return;
|
|
}
|
|
if (Result == IsOwnedResult::Owned || Result == IsOwnedResult::Skip ||
|
|
isNullPtr(Arg)) {
|
|
CreateOrCopyFnCall.insert(Arg);
|
|
return;
|
|
}
|
|
|
|
if (auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
|
|
if (CreateOrCopyOutArguments.contains(DRE->getDecl()))
|
|
return;
|
|
}
|
|
if (RTC.isARCEnabled() && isAdoptFnName(FnName))
|
|
reportUseAfterFree(FnName, CE, DeclWithIssue, "when ARC is disabled");
|
|
else
|
|
reportUseAfterFree(FnName, CE, DeclWithIssue);
|
|
}
|
|
|
|
void visitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr,
|
|
const Decl *DeclWithIssue) const {
|
|
if (BR->getSourceManager().isInSystemHeader(ObjCMsgExpr->getExprLoc()))
|
|
return;
|
|
|
|
auto Selector = ObjCMsgExpr->getSelector();
|
|
if (Selector.getAsString() == "autorelease") {
|
|
auto *Receiver = ObjCMsgExpr->getInstanceReceiver()->IgnoreParenCasts();
|
|
if (!Receiver)
|
|
return;
|
|
ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Receiver);
|
|
if (!ObjCMsgExpr)
|
|
return;
|
|
const Expr *Inner = nullptr;
|
|
if (!isAllocInit(ObjCMsgExpr, &Inner))
|
|
return;
|
|
CreateOrCopyFnCall.insert(ObjCMsgExpr);
|
|
if (Inner)
|
|
CreateOrCopyFnCall.insert(Inner);
|
|
return;
|
|
}
|
|
|
|
const Expr *Inner = nullptr;
|
|
if (!isAllocInit(ObjCMsgExpr, &Inner))
|
|
return;
|
|
if (RTC.isARCEnabled())
|
|
return; // ARC never leaks.
|
|
if (CreateOrCopyFnCall.contains(ObjCMsgExpr))
|
|
return;
|
|
if (Inner)
|
|
CreateOrCopyFnCall.insert(Inner); // Avoid double reporting.
|
|
reportLeak(ObjCMsgExpr, DeclWithIssue);
|
|
}
|
|
|
|
void checkCreateOrCopyFunction(const CallExpr *CE,
|
|
const Decl *DeclWithIssue) const {
|
|
unsigned ArgCount = CE->getNumArgs();
|
|
auto *CalleeDecl = CE->getCalleeDecl();
|
|
auto *FnDecl = CalleeDecl ? CalleeDecl->getAsFunction() : nullptr;
|
|
for (unsigned ArgIndex = 0; ArgIndex < ArgCount; ++ArgIndex) {
|
|
auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
|
|
auto *Unary = dyn_cast<UnaryOperator>(Arg);
|
|
if (!Unary)
|
|
continue;
|
|
if (Unary->getOpcode() != UO_AddrOf)
|
|
continue;
|
|
auto *SubExpr = Unary->getSubExpr();
|
|
if (!SubExpr)
|
|
continue;
|
|
auto *DRE = dyn_cast<DeclRefExpr>(SubExpr->IgnoreParenCasts());
|
|
if (!DRE)
|
|
continue;
|
|
auto *Decl = DRE->getDecl();
|
|
if (!Decl)
|
|
continue;
|
|
if (FnDecl && ArgIndex < FnDecl->getNumParams()) {
|
|
// Manually check attributes on argumenet since RetainSummaryManager
|
|
// basically ignores CF_RETRUNS_RETAINED on out arguments.
|
|
auto *ParamDecl = FnDecl->getParamDecl(ArgIndex);
|
|
if (ParamDecl->hasAttr<CFReturnsRetainedAttr>())
|
|
CreateOrCopyOutArguments.insert(Decl);
|
|
} else {
|
|
// No callee or a variadic argument.
|
|
// Conservatively assume it's an out argument.
|
|
if (RTC.isUnretained(Decl->getType()))
|
|
CreateOrCopyOutArguments.insert(Decl);
|
|
}
|
|
}
|
|
auto Summary = Summaries->getSummary(AnyCall(CE));
|
|
switch (Summary->getRetEffect().getKind()) {
|
|
case RetEffect::OwnedSymbol:
|
|
case RetEffect::OwnedWhenTrackedReceiver:
|
|
if (!CreateOrCopyFnCall.contains(CE))
|
|
reportLeak(CE, DeclWithIssue);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void checkBridgingRelease(const CallExpr *CE, const FunctionDecl *Callee,
|
|
const Decl *DeclWithIssue) const {
|
|
if (safeGetName(Callee) != "CFBridgingRelease" || CE->getNumArgs() != 1)
|
|
return;
|
|
|
|
auto *Arg = CE->getArg(0)->IgnoreParenCasts();
|
|
auto *InnerCE = dyn_cast<CallExpr>(Arg);
|
|
if (!InnerCE)
|
|
return;
|
|
|
|
auto *InnerF = InnerCE->getDirectCallee();
|
|
if (!InnerF || !isCreateOrCopyFunction(InnerF))
|
|
return;
|
|
|
|
CreateOrCopyFnCall.insert(InnerCE);
|
|
}
|
|
|
|
void visitConstructExpr(const CXXConstructExpr *CE,
|
|
const Decl *DeclWithIssue) const {
|
|
assert(BR && "expected nonnull BugReporter");
|
|
if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
|
|
return;
|
|
|
|
auto *Ctor = CE->getConstructor();
|
|
if (!Ctor)
|
|
return;
|
|
|
|
auto *Cls = Ctor->getParent();
|
|
if (!Cls)
|
|
return;
|
|
|
|
if (!isRetainPtrOrOSPtr(safeGetName(Cls)) || !CE->getNumArgs())
|
|
return;
|
|
|
|
// Ignore RetainPtr construction inside adoptNS, adoptCF, and retainPtr.
|
|
if (isAdoptFn(DeclWithIssue) || safeGetName(DeclWithIssue) == "retainPtr")
|
|
return;
|
|
|
|
std::string Name = "RetainPtr constructor";
|
|
auto *Arg = CE->getArg(0)->IgnoreParenCasts();
|
|
auto Result = isOwned(Arg);
|
|
|
|
if (isCreateOrCopy(Arg))
|
|
CreateOrCopyFnCall.insert(Arg); // Avoid double reporting.
|
|
|
|
const Expr *Inner = nullptr;
|
|
if (isAllocInit(Arg, &Inner)) {
|
|
CreateOrCopyFnCall.insert(Arg);
|
|
if (Inner)
|
|
CreateOrCopyFnCall.insert(Inner);
|
|
}
|
|
|
|
if (Result == IsOwnedResult::Skip)
|
|
return;
|
|
|
|
if (Result == IsOwnedResult::Unknown)
|
|
Result = IsOwnedResult::NotOwned;
|
|
if (Result == IsOwnedResult::Owned)
|
|
reportLeak(Name, CE, DeclWithIssue);
|
|
else if (RTC.isARCEnabled() && isAllocInit(Arg))
|
|
reportLeak(Name, CE, DeclWithIssue, "when ARC is disabled");
|
|
else if (isCreateOrCopy(Arg))
|
|
reportLeak(Name, CE, DeclWithIssue);
|
|
}
|
|
|
|
void visitVarDecl(const VarDecl *VD) const {
|
|
auto *Init = VD->getInit();
|
|
if (!Init || !RTC.isARCEnabled())
|
|
return;
|
|
Init = Init->IgnoreParenCasts();
|
|
const Expr *Inner = nullptr;
|
|
if (isAllocInit(Init, &Inner)) {
|
|
CreateOrCopyFnCall.insert(Init);
|
|
if (Inner)
|
|
CreateOrCopyFnCall.insert(Inner);
|
|
}
|
|
}
|
|
|
|
void visitBinaryOperator(const BinaryOperator *BO) const {
|
|
if (!BO->isAssignmentOp())
|
|
return;
|
|
auto *LHS = BO->getLHS();
|
|
auto *RHS = BO->getRHS()->IgnoreParenCasts();
|
|
if (isa<ObjCIvarRefExpr>(LHS)) {
|
|
const Expr *Inner = nullptr;
|
|
if (isAllocInit(RHS, &Inner)) {
|
|
CreateOrCopyFnCall.insert(RHS);
|
|
if (Inner)
|
|
CreateOrCopyFnCall.insert(Inner);
|
|
}
|
|
return;
|
|
}
|
|
auto *UO = dyn_cast<UnaryOperator>(LHS);
|
|
if (!UO)
|
|
return;
|
|
auto OpCode = UO->getOpcode();
|
|
if (OpCode != UO_Deref)
|
|
return;
|
|
auto *DerefTarget = UO->getSubExpr();
|
|
if (!DerefTarget)
|
|
return;
|
|
DerefTarget = DerefTarget->IgnoreParenCasts();
|
|
auto *DRE = dyn_cast<DeclRefExpr>(DerefTarget);
|
|
if (!DRE)
|
|
return;
|
|
auto *Decl = DRE->getDecl();
|
|
if (!Decl)
|
|
return;
|
|
if (!isa<ParmVarDecl>(Decl) || !isCreateOrCopy(RHS))
|
|
return;
|
|
if (Decl->hasAttr<CFReturnsRetainedAttr>())
|
|
CreateOrCopyFnCall.insert(RHS);
|
|
}
|
|
|
|
void visitReturnStmt(const ReturnStmt *RS, const Decl *DeclWithIssue) const {
|
|
if (!DeclWithIssue)
|
|
return;
|
|
auto *RetValue = RS->getRetValue();
|
|
if (!RetValue)
|
|
return;
|
|
RetValue = RetValue->IgnoreParenCasts();
|
|
std::optional<bool> retainsRet;
|
|
if (auto *FnDecl = dyn_cast<FunctionDecl>(DeclWithIssue))
|
|
retainsRet = retainsReturnValue(FnDecl);
|
|
else if (auto *MethodDecl = dyn_cast<ObjCMethodDecl>(DeclWithIssue))
|
|
retainsRet = retainsReturnValue(MethodDecl);
|
|
else
|
|
return;
|
|
if (!retainsRet || !*retainsRet) {
|
|
// Under ARC, returning [[X alloc] init] doesn't leak X.
|
|
if (RTC.isUnretained(RetValue->getType()))
|
|
return;
|
|
}
|
|
if (retainsRet && *retainsRet) {
|
|
CreateOrCopyFnCall.insert(RetValue);
|
|
return;
|
|
}
|
|
if (auto *CE = dyn_cast<CallExpr>(RetValue)) {
|
|
auto *Callee = CE->getDirectCallee();
|
|
if (!Callee || !isCreateOrCopyFunction(Callee))
|
|
return;
|
|
CreateOrCopyFnCall.insert(CE);
|
|
return;
|
|
}
|
|
const Expr *Inner = nullptr;
|
|
if (isAllocInit(RetValue, &Inner)) {
|
|
CreateOrCopyFnCall.insert(RetValue);
|
|
if (Inner)
|
|
CreateOrCopyFnCall.insert(Inner);
|
|
}
|
|
}
|
|
|
|
template <typename CallableType>
|
|
std::optional<bool> retainsReturnValue(const CallableType *FnDecl) const {
|
|
auto Summary = Summaries->getSummary(AnyCall(FnDecl));
|
|
auto RetEffect = Summary->getRetEffect();
|
|
switch (RetEffect.getKind()) {
|
|
case RetEffect::NoRet:
|
|
return std::nullopt;
|
|
case RetEffect::OwnedSymbol:
|
|
return true;
|
|
case RetEffect::NotOwnedSymbol:
|
|
return false;
|
|
case RetEffect::OwnedWhenTrackedReceiver:
|
|
return std::nullopt;
|
|
case RetEffect::NoRetHard:
|
|
return std::nullopt;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
bool isCreateOrCopy(const Expr *E) const {
|
|
auto *CE = dyn_cast<CallExpr>(E);
|
|
if (!CE)
|
|
return false;
|
|
auto *Callee = CE->getDirectCallee();
|
|
if (!Callee)
|
|
return false;
|
|
return isCreateOrCopyFunction(Callee);
|
|
}
|
|
|
|
bool isCreateOrCopyFunction(const FunctionDecl *FnDecl) const {
|
|
auto CalleeName = safeGetName(FnDecl);
|
|
return CalleeName.find("Create") != std::string::npos ||
|
|
CalleeName.find("Copy") != std::string::npos;
|
|
}
|
|
|
|
enum class IsOwnedResult { Unknown, Skip, Owned, NotOwned };
|
|
IsOwnedResult isOwned(const Expr *E) const {
|
|
while (1) {
|
|
if (auto *POE = dyn_cast<PseudoObjectExpr>(E)) {
|
|
if (unsigned SemanticExprCount = POE->getNumSemanticExprs()) {
|
|
E = POE->getSemanticExpr(SemanticExprCount - 1);
|
|
continue;
|
|
}
|
|
}
|
|
if (isNullPtr(E))
|
|
return IsOwnedResult::NotOwned;
|
|
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
|
|
auto QT = DRE->getType();
|
|
if (isRetainPtrOrOSPtrType(QT))
|
|
return IsOwnedResult::NotOwned;
|
|
QT = QT.getCanonicalType();
|
|
if (RTC.isUnretained(QT, true /* ignoreARC */))
|
|
return IsOwnedResult::NotOwned;
|
|
auto *PointeeType = QT->getPointeeType().getTypePtrOrNull();
|
|
if (PointeeType && PointeeType->isVoidType())
|
|
return IsOwnedResult::NotOwned; // Assume reading void* as +0.
|
|
}
|
|
if (auto *TE = dyn_cast<CXXBindTemporaryExpr>(E)) {
|
|
E = TE->getSubExpr();
|
|
continue;
|
|
}
|
|
if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
|
|
auto Summary = Summaries->getSummary(AnyCall(ObjCMsgExpr));
|
|
auto RetEffect = Summary->getRetEffect();
|
|
switch (RetEffect.getKind()) {
|
|
case RetEffect::NoRet:
|
|
return IsOwnedResult::Unknown;
|
|
case RetEffect::OwnedSymbol:
|
|
return IsOwnedResult::Owned;
|
|
case RetEffect::NotOwnedSymbol:
|
|
return IsOwnedResult::NotOwned;
|
|
case RetEffect::OwnedWhenTrackedReceiver:
|
|
if (auto *Receiver = ObjCMsgExpr->getInstanceReceiver()) {
|
|
E = Receiver->IgnoreParenCasts();
|
|
continue;
|
|
}
|
|
return IsOwnedResult::Unknown;
|
|
case RetEffect::NoRetHard:
|
|
return IsOwnedResult::Unknown;
|
|
}
|
|
}
|
|
if (auto *CXXCE = dyn_cast<CXXMemberCallExpr>(E)) {
|
|
if (auto *MD = CXXCE->getMethodDecl()) {
|
|
auto *Cls = MD->getParent();
|
|
if (auto *CD = dyn_cast<CXXConversionDecl>(MD)) {
|
|
auto QT = CD->getConversionType().getCanonicalType();
|
|
auto *ResultType = QT.getTypePtrOrNull();
|
|
if (isRetainPtrOrOSPtr(safeGetName(Cls)) && ResultType &&
|
|
(ResultType->isPointerType() || ResultType->isReferenceType() ||
|
|
ResultType->isObjCObjectPointerType()))
|
|
return IsOwnedResult::NotOwned;
|
|
}
|
|
if (safeGetName(MD) == "leakRef" &&
|
|
isRetainPtrOrOSPtr(safeGetName(Cls)))
|
|
return IsOwnedResult::Owned;
|
|
}
|
|
}
|
|
if (auto *CE = dyn_cast<CallExpr>(E)) {
|
|
if (auto *Callee = CE->getDirectCallee()) {
|
|
if (isAdoptFn(Callee))
|
|
return IsOwnedResult::NotOwned;
|
|
auto Name = safeGetName(Callee);
|
|
if (Name == "__builtin___CFStringMakeConstantString")
|
|
return IsOwnedResult::NotOwned;
|
|
if ((Name == "checked_cf_cast" || Name == "dynamic_cf_cast" ||
|
|
Name == "checked_objc_cast" || Name == "dynamic_objc_cast") &&
|
|
CE->getNumArgs() == 1) {
|
|
E = CE->getArg(0)->IgnoreParenCasts();
|
|
continue;
|
|
}
|
|
auto RetType = Callee->getReturnType();
|
|
if (isRetainPtrOrOSPtrType(RetType))
|
|
return IsOwnedResult::NotOwned;
|
|
if (isCreateOrCopyFunction(Callee)) {
|
|
CreateOrCopyFnCall.insert(CE);
|
|
return IsOwnedResult::Owned;
|
|
}
|
|
} else if (auto *CalleeExpr = CE->getCallee()) {
|
|
if (isa<CXXDependentScopeMemberExpr>(CalleeExpr))
|
|
return IsOwnedResult::Skip; // Wait for instantiation.
|
|
if (isa<UnresolvedLookupExpr>(CalleeExpr))
|
|
return IsOwnedResult::Skip; // Wait for instantiation.
|
|
}
|
|
auto Summary = Summaries->getSummary(AnyCall(CE));
|
|
auto RetEffect = Summary->getRetEffect();
|
|
switch (RetEffect.getKind()) {
|
|
case RetEffect::NoRet:
|
|
return IsOwnedResult::Unknown;
|
|
case RetEffect::OwnedSymbol:
|
|
return IsOwnedResult::Owned;
|
|
case RetEffect::NotOwnedSymbol:
|
|
return IsOwnedResult::NotOwned;
|
|
case RetEffect::OwnedWhenTrackedReceiver:
|
|
return IsOwnedResult::Unknown;
|
|
case RetEffect::NoRetHard:
|
|
return IsOwnedResult::Unknown;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return IsOwnedResult::Unknown;
|
|
}
|
|
|
|
void reportUseAfterFree(const std::string &Name, const CallExpr *CE,
|
|
const Decl *DeclWithIssue,
|
|
const char *condition = nullptr) const {
|
|
SmallString<100> Buf;
|
|
llvm::raw_svector_ostream Os(Buf);
|
|
|
|
Os << "Incorrect use of " << Name
|
|
<< ". The argument is +0 and results in an use-after-free";
|
|
if (condition)
|
|
Os << " " << condition;
|
|
Os << ".";
|
|
|
|
assert(BR && "expected nonnull BugReporter");
|
|
PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
|
|
BR->getSourceManager());
|
|
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
|
|
Report->addRange(CE->getSourceRange());
|
|
Report->setDeclWithIssue(DeclWithIssue);
|
|
BR->emitReport(std::move(Report));
|
|
}
|
|
|
|
void reportLeak(std::string &Name, const CXXConstructExpr *CE,
|
|
const Decl *DeclWithIssue,
|
|
const char *condition = nullptr) const {
|
|
SmallString<100> Buf;
|
|
llvm::raw_svector_ostream Os(Buf);
|
|
|
|
Os << "Incorrect use of " << Name
|
|
<< ". The argument is +1 and results in a memory leak";
|
|
if (condition)
|
|
Os << " " << condition;
|
|
Os << ".";
|
|
|
|
assert(BR && "expected nonnull BugReporter");
|
|
PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
|
|
BR->getSourceManager());
|
|
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
|
|
Report->addRange(CE->getSourceRange());
|
|
Report->setDeclWithIssue(DeclWithIssue);
|
|
BR->emitReport(std::move(Report));
|
|
}
|
|
|
|
template <typename ExprType>
|
|
void reportLeak(const ExprType *E, const Decl *DeclWithIssue) const {
|
|
SmallString<100> Buf;
|
|
llvm::raw_svector_ostream Os(Buf);
|
|
|
|
Os << "The return value is +1 and results in a memory leak.";
|
|
|
|
PathDiagnosticLocation BSLoc(E->getSourceRange().getBegin(),
|
|
BR->getSourceManager());
|
|
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
|
|
Report->addRange(E->getSourceRange());
|
|
Report->setDeclWithIssue(DeclWithIssue);
|
|
BR->emitReport(std::move(Report));
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void ento::registerRetainPtrCtorAdoptChecker(CheckerManager &Mgr) {
|
|
Mgr.registerChecker<RetainPtrCtorAdoptChecker>();
|
|
}
|
|
|
|
bool ento::shouldRegisterRetainPtrCtorAdoptChecker(const CheckerManager &mgr) {
|
|
return true;
|
|
}
|