1 //=======- UncountedCallArgsChecker.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 "DiagOutputUtils.h" 11 #include "PtrTypesSemantics.h" 12 #include "clang/AST/CXXInheritance.h" 13 #include "clang/AST/Decl.h" 14 #include "clang/AST/DeclCXX.h" 15 #include "clang/AST/RecursiveASTVisitor.h" 16 #include "clang/Basic/SourceLocation.h" 17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20 #include "clang/StaticAnalyzer/Core/Checker.h" 21 #include "llvm/ADT/DenseSet.h" 22 23 using namespace clang; 24 using namespace ento; 25 26 namespace { 27 28 class UncountedCallArgsChecker 29 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 30 BugType Bug{this, 31 "Uncounted call argument for a raw pointer/reference parameter", 32 "WebKit coding guidelines"}; 33 mutable BugReporter *BR; 34 35 public: 36 37 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 38 BugReporter &BRArg) const { 39 BR = &BRArg; 40 41 // The calls to checkAST* from AnalysisConsumer don't 42 // visit template instantiations or lambda classes. We 43 // want to visit those, so we make our own RecursiveASTVisitor. 44 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { 45 const UncountedCallArgsChecker *Checker; 46 explicit LocalVisitor(const UncountedCallArgsChecker *Checker) 47 : Checker(Checker) { 48 assert(Checker); 49 } 50 51 bool shouldVisitTemplateInstantiations() const { return true; } 52 bool shouldVisitImplicitCode() const { return false; } 53 54 bool VisitCallExpr(const CallExpr *CE) { 55 Checker->visitCallExpr(CE); 56 return true; 57 } 58 }; 59 60 LocalVisitor visitor(this); 61 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 62 } 63 64 void visitCallExpr(const CallExpr *CE) const { 65 if (shouldSkipCall(CE)) 66 return; 67 68 if (auto *F = CE->getDirectCallee()) { 69 // Skip the first argument for overloaded member operators (e. g. lambda 70 // or std::function call operator). 71 unsigned ArgIdx = 72 isa<CXXOperatorCallExpr>(CE) && dyn_cast_or_null<CXXMethodDecl>(F); 73 74 for (auto P = F->param_begin(); 75 // FIXME: Also check variadic function parameters. 76 // FIXME: Also check default function arguments. Probably a different 77 // checker. In case there are default arguments the call can have 78 // fewer arguments than the callee has parameters. 79 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) { 80 // TODO: attributes. 81 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>()) 82 // continue; 83 84 const auto *ArgType = (*P)->getType().getTypePtrOrNull(); 85 if (!ArgType) 86 continue; // FIXME? Should we bail? 87 88 // FIXME: more complex types (arrays, references to raw pointers, etc) 89 if (!isUncountedPtr(ArgType)) 90 continue; 91 92 const auto *Arg = CE->getArg(ArgIdx); 93 94 std::pair<const clang::Expr *, bool> ArgOrigin = 95 tryToFindPtrOrigin(Arg, true); 96 97 // Temporary ref-counted object created as part of the call argument 98 // would outlive the call. 99 if (ArgOrigin.second) 100 continue; 101 102 if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) { 103 // foo(nullptr) 104 continue; 105 } 106 if (isa<IntegerLiteral>(ArgOrigin.first)) { 107 // FIXME: Check the value. 108 // foo(NULL) 109 continue; 110 } 111 112 if (isASafeCallArg(ArgOrigin.first)) 113 continue; 114 115 reportBug(Arg, *P); 116 } 117 } 118 } 119 120 bool shouldSkipCall(const CallExpr *CE) const { 121 if (CE->getNumArgs() == 0) 122 return false; 123 124 // If an assignment is problematic we should warn about the sole existence 125 // of object on LHS. 126 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) { 127 // Note: assignemnt to built-in type isn't derived from CallExpr. 128 if (MemberOp->isAssignmentOp()) 129 return false; 130 } 131 132 const auto *Callee = CE->getDirectCallee(); 133 if (!Callee) 134 return false; 135 136 auto overloadedOperatorType = Callee->getOverloadedOperator(); 137 if (overloadedOperatorType == OO_EqualEqual || 138 overloadedOperatorType == OO_ExclaimEqual || 139 overloadedOperatorType == OO_LessEqual || 140 overloadedOperatorType == OO_GreaterEqual || 141 overloadedOperatorType == OO_Spaceship || 142 overloadedOperatorType == OO_AmpAmp || 143 overloadedOperatorType == OO_PipePipe) 144 return true; 145 146 if (isCtorOfRefCounted(Callee)) 147 return true; 148 149 auto name = safeGetName(Callee); 150 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || 151 name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" || 152 name == "is" || name == "equal" || name == "hash" || 153 name == "isType" 154 // FIXME: Most/all of these should be implemented via attributes. 155 || name == "equalIgnoringASCIICase" || 156 name == "equalIgnoringASCIICaseCommon" || 157 name == "equalIgnoringNullity") 158 return true; 159 160 return false; 161 } 162 163 void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const { 164 assert(CallArg); 165 166 SmallString<100> Buf; 167 llvm::raw_svector_ostream Os(Buf); 168 169 const std::string paramName = safeGetName(Param); 170 Os << "Call argument"; 171 if (!paramName.empty()) { 172 Os << " for parameter "; 173 printQuotedQualifiedName(Os, Param); 174 } 175 Os << " is uncounted and unsafe."; 176 177 const SourceLocation SrcLocToReport = 178 isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc() 179 : CallArg->getSourceRange().getBegin(); 180 181 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); 182 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 183 Report->addRange(CallArg->getSourceRange()); 184 BR->emitReport(std::move(Report)); 185 } 186 }; 187 } // namespace 188 189 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) { 190 Mgr.registerChecker<UncountedCallArgsChecker>(); 191 } 192 193 bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) { 194 return true; 195 } 196