15ffd83dbSDimitry Andric //=======- UncountedCallArgsChecker.cpp --------------------------*- C++ -*-==// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric 95ffd83dbSDimitry Andric #include "ASTUtils.h" 105ffd83dbSDimitry Andric #include "DiagOutputUtils.h" 115ffd83dbSDimitry Andric #include "PtrTypesSemantics.h" 125ffd83dbSDimitry Andric #include "clang/AST/CXXInheritance.h" 135ffd83dbSDimitry Andric #include "clang/AST/Decl.h" 145ffd83dbSDimitry Andric #include "clang/AST/DeclCXX.h" 155ffd83dbSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 165ffd83dbSDimitry Andric #include "clang/Basic/SourceLocation.h" 175ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 185ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 195ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 205ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 21bdd1243dSDimitry Andric #include <optional> 225ffd83dbSDimitry Andric 235ffd83dbSDimitry Andric using namespace clang; 245ffd83dbSDimitry Andric using namespace ento; 255ffd83dbSDimitry Andric 265ffd83dbSDimitry Andric namespace { 275ffd83dbSDimitry Andric 285ffd83dbSDimitry Andric class UncountedCallArgsChecker 295ffd83dbSDimitry Andric : public Checker<check::ASTDecl<TranslationUnitDecl>> { 305ffd83dbSDimitry Andric BugType Bug{this, 315ffd83dbSDimitry Andric "Uncounted call argument for a raw pointer/reference parameter", 325ffd83dbSDimitry Andric "WebKit coding guidelines"}; 335ffd83dbSDimitry Andric mutable BugReporter *BR; 345ffd83dbSDimitry Andric 35*0fca6ea1SDimitry Andric TrivialFunctionAnalysis TFA; 36*0fca6ea1SDimitry Andric 375ffd83dbSDimitry Andric public: 385ffd83dbSDimitry Andric 395ffd83dbSDimitry Andric void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 405ffd83dbSDimitry Andric BugReporter &BRArg) const { 415ffd83dbSDimitry Andric BR = &BRArg; 425ffd83dbSDimitry Andric 435ffd83dbSDimitry Andric // The calls to checkAST* from AnalysisConsumer don't 445ffd83dbSDimitry Andric // visit template instantiations or lambda classes. We 455ffd83dbSDimitry Andric // want to visit those, so we make our own RecursiveASTVisitor. 465ffd83dbSDimitry Andric struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { 475ffd83dbSDimitry Andric const UncountedCallArgsChecker *Checker; 485ffd83dbSDimitry Andric explicit LocalVisitor(const UncountedCallArgsChecker *Checker) 495ffd83dbSDimitry Andric : Checker(Checker) { 505ffd83dbSDimitry Andric assert(Checker); 515ffd83dbSDimitry Andric } 525ffd83dbSDimitry Andric 535ffd83dbSDimitry Andric bool shouldVisitTemplateInstantiations() const { return true; } 545ffd83dbSDimitry Andric bool shouldVisitImplicitCode() const { return false; } 555ffd83dbSDimitry Andric 56*0fca6ea1SDimitry Andric bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) { 57*0fca6ea1SDimitry Andric if (isRefType(safeGetName(Decl))) 58*0fca6ea1SDimitry Andric return true; 59*0fca6ea1SDimitry Andric return RecursiveASTVisitor<LocalVisitor>::TraverseClassTemplateDecl( 60*0fca6ea1SDimitry Andric Decl); 61*0fca6ea1SDimitry Andric } 62*0fca6ea1SDimitry Andric 635ffd83dbSDimitry Andric bool VisitCallExpr(const CallExpr *CE) { 645ffd83dbSDimitry Andric Checker->visitCallExpr(CE); 655ffd83dbSDimitry Andric return true; 665ffd83dbSDimitry Andric } 675ffd83dbSDimitry Andric }; 685ffd83dbSDimitry Andric 695ffd83dbSDimitry Andric LocalVisitor visitor(this); 705ffd83dbSDimitry Andric visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 715ffd83dbSDimitry Andric } 725ffd83dbSDimitry Andric 735ffd83dbSDimitry Andric void visitCallExpr(const CallExpr *CE) const { 745ffd83dbSDimitry Andric if (shouldSkipCall(CE)) 755ffd83dbSDimitry Andric return; 765ffd83dbSDimitry Andric 775ffd83dbSDimitry Andric if (auto *F = CE->getDirectCallee()) { 785ffd83dbSDimitry Andric // Skip the first argument for overloaded member operators (e. g. lambda 795ffd83dbSDimitry Andric // or std::function call operator). 80349cc55cSDimitry Andric unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F); 815ffd83dbSDimitry Andric 82*0fca6ea1SDimitry Andric if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) { 83*0fca6ea1SDimitry Andric if (auto *MD = MemberCallExpr->getMethodDecl()) { 84*0fca6ea1SDimitry Andric auto name = safeGetName(MD); 85*0fca6ea1SDimitry Andric if (name == "ref" || name == "deref") 86*0fca6ea1SDimitry Andric return; 87*0fca6ea1SDimitry Andric } 88*0fca6ea1SDimitry Andric auto *E = MemberCallExpr->getImplicitObjectArgument(); 89*0fca6ea1SDimitry Andric QualType ArgType = MemberCallExpr->getObjectType(); 90*0fca6ea1SDimitry Andric std::optional<bool> IsUncounted = 91*0fca6ea1SDimitry Andric isUncounted(ArgType->getAsCXXRecordDecl()); 92*0fca6ea1SDimitry Andric if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) 93*0fca6ea1SDimitry Andric reportBugOnThis(E); 94*0fca6ea1SDimitry Andric } 95*0fca6ea1SDimitry Andric 965ffd83dbSDimitry Andric for (auto P = F->param_begin(); 975ffd83dbSDimitry Andric // FIXME: Also check variadic function parameters. 985ffd83dbSDimitry Andric // FIXME: Also check default function arguments. Probably a different 995ffd83dbSDimitry Andric // checker. In case there are default arguments the call can have 1005ffd83dbSDimitry Andric // fewer arguments than the callee has parameters. 1015ffd83dbSDimitry Andric P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) { 1025ffd83dbSDimitry Andric // TODO: attributes. 1035ffd83dbSDimitry Andric // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>()) 1045ffd83dbSDimitry Andric // continue; 1055ffd83dbSDimitry Andric 1065ffd83dbSDimitry Andric const auto *ArgType = (*P)->getType().getTypePtrOrNull(); 1075ffd83dbSDimitry Andric if (!ArgType) 1085ffd83dbSDimitry Andric continue; // FIXME? Should we bail? 1095ffd83dbSDimitry Andric 1105ffd83dbSDimitry Andric // FIXME: more complex types (arrays, references to raw pointers, etc) 111bdd1243dSDimitry Andric std::optional<bool> IsUncounted = isUncountedPtr(ArgType); 112e8d8bef9SDimitry Andric if (!IsUncounted || !(*IsUncounted)) 1135ffd83dbSDimitry Andric continue; 1145ffd83dbSDimitry Andric 1155ffd83dbSDimitry Andric const auto *Arg = CE->getArg(ArgIdx); 1165ffd83dbSDimitry Andric 117*0fca6ea1SDimitry Andric if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg)) 118*0fca6ea1SDimitry Andric Arg = defaultArg->getExpr(); 1195ffd83dbSDimitry Andric 120*0fca6ea1SDimitry Andric if (isPtrOriginSafe(Arg)) 1215ffd83dbSDimitry Andric continue; 1225ffd83dbSDimitry Andric 1235ffd83dbSDimitry Andric reportBug(Arg, *P); 1245ffd83dbSDimitry Andric } 1255ffd83dbSDimitry Andric } 1265ffd83dbSDimitry Andric } 1275ffd83dbSDimitry Andric 128*0fca6ea1SDimitry Andric bool isPtrOriginSafe(const Expr *Arg) const { 129*0fca6ea1SDimitry Andric return tryToFindPtrOrigin(Arg, /*StopAtFirstRefCountedObj=*/true, 130*0fca6ea1SDimitry Andric [](const clang::Expr *ArgOrigin, bool IsSafe) { 131*0fca6ea1SDimitry Andric if (IsSafe) 132*0fca6ea1SDimitry Andric return true; 133*0fca6ea1SDimitry Andric if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) { 134*0fca6ea1SDimitry Andric // foo(nullptr) 135*0fca6ea1SDimitry Andric return true; 136*0fca6ea1SDimitry Andric } 137*0fca6ea1SDimitry Andric if (isa<IntegerLiteral>(ArgOrigin)) { 138*0fca6ea1SDimitry Andric // FIXME: Check the value. 139*0fca6ea1SDimitry Andric // foo(NULL) 140*0fca6ea1SDimitry Andric return true; 141*0fca6ea1SDimitry Andric } 142*0fca6ea1SDimitry Andric if (isASafeCallArg(ArgOrigin)) 143*0fca6ea1SDimitry Andric return true; 144*0fca6ea1SDimitry Andric return false; 145*0fca6ea1SDimitry Andric }); 146*0fca6ea1SDimitry Andric } 147*0fca6ea1SDimitry Andric 1485ffd83dbSDimitry Andric bool shouldSkipCall(const CallExpr *CE) const { 149*0fca6ea1SDimitry Andric const auto *Callee = CE->getDirectCallee(); 150*0fca6ea1SDimitry Andric 151*0fca6ea1SDimitry Andric if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc())) 152*0fca6ea1SDimitry Andric return true; 153*0fca6ea1SDimitry Andric 154*0fca6ea1SDimitry Andric if (Callee && TFA.isTrivial(Callee)) 155*0fca6ea1SDimitry Andric return true; 156*0fca6ea1SDimitry Andric 1575ffd83dbSDimitry Andric if (CE->getNumArgs() == 0) 1585ffd83dbSDimitry Andric return false; 1595ffd83dbSDimitry Andric 1605ffd83dbSDimitry Andric // If an assignment is problematic we should warn about the sole existence 1615ffd83dbSDimitry Andric // of object on LHS. 1625ffd83dbSDimitry Andric if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) { 1635ffd83dbSDimitry Andric // Note: assignemnt to built-in type isn't derived from CallExpr. 164*0fca6ea1SDimitry Andric if (MemberOp->getOperator() == 165*0fca6ea1SDimitry Andric OO_Equal) { // Ignore assignment to Ref/RefPtr. 166*0fca6ea1SDimitry Andric auto *callee = MemberOp->getDirectCallee(); 167*0fca6ea1SDimitry Andric if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) { 168*0fca6ea1SDimitry Andric if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) { 169*0fca6ea1SDimitry Andric if (isRefCounted(classDecl)) 170*0fca6ea1SDimitry Andric return true; 171*0fca6ea1SDimitry Andric } 172*0fca6ea1SDimitry Andric } 173*0fca6ea1SDimitry Andric } 1745ffd83dbSDimitry Andric if (MemberOp->isAssignmentOp()) 1755ffd83dbSDimitry Andric return false; 1765ffd83dbSDimitry Andric } 1775ffd83dbSDimitry Andric 1785ffd83dbSDimitry Andric if (!Callee) 1795ffd83dbSDimitry Andric return false; 1805ffd83dbSDimitry Andric 181*0fca6ea1SDimitry Andric if (isMethodOnWTFContainerType(Callee)) 182*0fca6ea1SDimitry Andric return true; 183*0fca6ea1SDimitry Andric 1845ffd83dbSDimitry Andric auto overloadedOperatorType = Callee->getOverloadedOperator(); 1855ffd83dbSDimitry Andric if (overloadedOperatorType == OO_EqualEqual || 1865ffd83dbSDimitry Andric overloadedOperatorType == OO_ExclaimEqual || 1875ffd83dbSDimitry Andric overloadedOperatorType == OO_LessEqual || 1885ffd83dbSDimitry Andric overloadedOperatorType == OO_GreaterEqual || 1895ffd83dbSDimitry Andric overloadedOperatorType == OO_Spaceship || 1905ffd83dbSDimitry Andric overloadedOperatorType == OO_AmpAmp || 1915ffd83dbSDimitry Andric overloadedOperatorType == OO_PipePipe) 1925ffd83dbSDimitry Andric return true; 1935ffd83dbSDimitry Andric 1945ffd83dbSDimitry Andric if (isCtorOfRefCounted(Callee)) 1955ffd83dbSDimitry Andric return true; 1965ffd83dbSDimitry Andric 1975ffd83dbSDimitry Andric auto name = safeGetName(Callee); 1985ffd83dbSDimitry Andric if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || 199*0fca6ea1SDimitry Andric name == "dynamicDowncast" || name == "downcast" || 200*0fca6ea1SDimitry Andric name == "checkedDowncast" || name == "uncheckedDowncast" || 201*0fca6ea1SDimitry Andric name == "bitwise_cast" || name == "is" || name == "equal" || 202*0fca6ea1SDimitry Andric name == "hash" || name == "isType" || 2035ffd83dbSDimitry Andric // FIXME: Most/all of these should be implemented via attributes. 204*0fca6ea1SDimitry Andric name == "equalIgnoringASCIICase" || 2055ffd83dbSDimitry Andric name == "equalIgnoringASCIICaseCommon" || 206*0fca6ea1SDimitry Andric name == "equalIgnoringNullity" || name == "toString") 2075ffd83dbSDimitry Andric return true; 2085ffd83dbSDimitry Andric 2095ffd83dbSDimitry Andric return false; 2105ffd83dbSDimitry Andric } 2115ffd83dbSDimitry Andric 212*0fca6ea1SDimitry Andric bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const { 213*0fca6ea1SDimitry Andric if (!isa<CXXMethodDecl>(Decl)) 214*0fca6ea1SDimitry Andric return false; 215*0fca6ea1SDimitry Andric auto *ClassDecl = Decl->getParent(); 216*0fca6ea1SDimitry Andric if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl)) 217*0fca6ea1SDimitry Andric return false; 218*0fca6ea1SDimitry Andric 219*0fca6ea1SDimitry Andric auto *NsDecl = ClassDecl->getParent(); 220*0fca6ea1SDimitry Andric if (!NsDecl || !isa<NamespaceDecl>(NsDecl)) 221*0fca6ea1SDimitry Andric return false; 222*0fca6ea1SDimitry Andric 223*0fca6ea1SDimitry Andric auto MethodName = safeGetName(Decl); 224*0fca6ea1SDimitry Andric auto ClsNameStr = safeGetName(ClassDecl); 225*0fca6ea1SDimitry Andric StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef. 226*0fca6ea1SDimitry Andric auto NamespaceName = safeGetName(NsDecl); 227*0fca6ea1SDimitry Andric // FIXME: These should be implemented via attributes. 228*0fca6ea1SDimitry Andric return NamespaceName == "WTF" && 229*0fca6ea1SDimitry Andric (MethodName == "find" || MethodName == "findIf" || 230*0fca6ea1SDimitry Andric MethodName == "reverseFind" || MethodName == "reverseFindIf" || 231*0fca6ea1SDimitry Andric MethodName == "findIgnoringASCIICase" || MethodName == "get" || 232*0fca6ea1SDimitry Andric MethodName == "inlineGet" || MethodName == "contains" || 233*0fca6ea1SDimitry Andric MethodName == "containsIf" || 234*0fca6ea1SDimitry Andric MethodName == "containsIgnoringASCIICase" || 235*0fca6ea1SDimitry Andric MethodName == "startsWith" || MethodName == "endsWith" || 236*0fca6ea1SDimitry Andric MethodName == "startsWithIgnoringASCIICase" || 237*0fca6ea1SDimitry Andric MethodName == "endsWithIgnoringASCIICase" || 238*0fca6ea1SDimitry Andric MethodName == "substring") && 239*0fca6ea1SDimitry Andric (ClsName.ends_with("Vector") || ClsName.ends_with("Set") || 240*0fca6ea1SDimitry Andric ClsName.ends_with("Map") || ClsName == "StringImpl" || 241*0fca6ea1SDimitry Andric ClsName.ends_with("String")); 242*0fca6ea1SDimitry Andric } 243*0fca6ea1SDimitry Andric 2445ffd83dbSDimitry Andric void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const { 2455ffd83dbSDimitry Andric assert(CallArg); 2465ffd83dbSDimitry Andric 2475ffd83dbSDimitry Andric SmallString<100> Buf; 2485ffd83dbSDimitry Andric llvm::raw_svector_ostream Os(Buf); 2495ffd83dbSDimitry Andric 2505ffd83dbSDimitry Andric const std::string paramName = safeGetName(Param); 2515ffd83dbSDimitry Andric Os << "Call argument"; 2525ffd83dbSDimitry Andric if (!paramName.empty()) { 2535ffd83dbSDimitry Andric Os << " for parameter "; 2545ffd83dbSDimitry Andric printQuotedQualifiedName(Os, Param); 2555ffd83dbSDimitry Andric } 2565ffd83dbSDimitry Andric Os << " is uncounted and unsafe."; 2575ffd83dbSDimitry Andric 2585ffd83dbSDimitry Andric const SourceLocation SrcLocToReport = 2595ffd83dbSDimitry Andric isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc() 2605ffd83dbSDimitry Andric : CallArg->getSourceRange().getBegin(); 2615ffd83dbSDimitry Andric 2625ffd83dbSDimitry Andric PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); 2635ffd83dbSDimitry Andric auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 2645ffd83dbSDimitry Andric Report->addRange(CallArg->getSourceRange()); 2655ffd83dbSDimitry Andric BR->emitReport(std::move(Report)); 2665ffd83dbSDimitry Andric } 267*0fca6ea1SDimitry Andric 268*0fca6ea1SDimitry Andric void reportBugOnThis(const Expr *CallArg) const { 269*0fca6ea1SDimitry Andric assert(CallArg); 270*0fca6ea1SDimitry Andric 271*0fca6ea1SDimitry Andric const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); 272*0fca6ea1SDimitry Andric 273*0fca6ea1SDimitry Andric PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); 274*0fca6ea1SDimitry Andric auto Report = std::make_unique<BasicBugReport>( 275*0fca6ea1SDimitry Andric Bug, "Call argument for 'this' parameter is uncounted and unsafe.", 276*0fca6ea1SDimitry Andric BSLoc); 277*0fca6ea1SDimitry Andric Report->addRange(CallArg->getSourceRange()); 278*0fca6ea1SDimitry Andric BR->emitReport(std::move(Report)); 279*0fca6ea1SDimitry Andric } 2805ffd83dbSDimitry Andric }; 2815ffd83dbSDimitry Andric } // namespace 2825ffd83dbSDimitry Andric 2835ffd83dbSDimitry Andric void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) { 2845ffd83dbSDimitry Andric Mgr.registerChecker<UncountedCallArgsChecker>(); 2855ffd83dbSDimitry Andric } 2865ffd83dbSDimitry Andric 2875ffd83dbSDimitry Andric bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) { 2885ffd83dbSDimitry Andric return true; 2895ffd83dbSDimitry Andric } 290