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