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 <optional> 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 TrivialFunctionAnalysis TFA; 36 37 public: 38 39 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 40 BugReporter &BRArg) const { 41 BR = &BRArg; 42 43 // The calls to checkAST* from AnalysisConsumer don't 44 // visit template instantiations or lambda classes. We 45 // want to visit those, so we make our own RecursiveASTVisitor. 46 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { 47 const UncountedCallArgsChecker *Checker; 48 explicit LocalVisitor(const UncountedCallArgsChecker *Checker) 49 : Checker(Checker) { 50 assert(Checker); 51 } 52 53 bool shouldVisitTemplateInstantiations() const { return true; } 54 bool shouldVisitImplicitCode() const { return false; } 55 56 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) { 57 if (isRefType(safeGetName(Decl))) 58 return true; 59 return RecursiveASTVisitor<LocalVisitor>::TraverseClassTemplateDecl( 60 Decl); 61 } 62 63 bool VisitCallExpr(const CallExpr *CE) { 64 Checker->visitCallExpr(CE); 65 return true; 66 } 67 }; 68 69 LocalVisitor visitor(this); 70 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 71 } 72 73 void visitCallExpr(const CallExpr *CE) const { 74 if (shouldSkipCall(CE)) 75 return; 76 77 if (auto *F = CE->getDirectCallee()) { 78 // Skip the first argument for overloaded member operators (e. g. lambda 79 // or std::function call operator). 80 unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F); 81 82 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) { 83 if (auto *MD = MemberCallExpr->getMethodDecl()) { 84 auto name = safeGetName(MD); 85 if (name == "ref" || name == "deref") 86 return; 87 } 88 auto *E = MemberCallExpr->getImplicitObjectArgument(); 89 QualType ArgType = MemberCallExpr->getObjectType(); 90 std::optional<bool> IsUncounted = 91 isUncounted(ArgType->getAsCXXRecordDecl()); 92 if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E)) 93 reportBugOnThis(E); 94 } 95 96 for (auto P = F->param_begin(); 97 // FIXME: Also check variadic function parameters. 98 // FIXME: Also check default function arguments. Probably a different 99 // checker. In case there are default arguments the call can have 100 // fewer arguments than the callee has parameters. 101 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) { 102 // TODO: attributes. 103 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>()) 104 // continue; 105 106 const auto *ArgType = (*P)->getType().getTypePtrOrNull(); 107 if (!ArgType) 108 continue; // FIXME? Should we bail? 109 110 // FIXME: more complex types (arrays, references to raw pointers, etc) 111 std::optional<bool> IsUncounted = isUncountedPtr(ArgType); 112 if (!IsUncounted || !(*IsUncounted)) 113 continue; 114 115 const auto *Arg = CE->getArg(ArgIdx); 116 117 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg)) 118 Arg = defaultArg->getExpr(); 119 120 if (isPtrOriginSafe(Arg)) 121 continue; 122 123 reportBug(Arg, *P); 124 } 125 } 126 } 127 128 bool isPtrOriginSafe(const Expr *Arg) const { 129 return tryToFindPtrOrigin(Arg, /*StopAtFirstRefCountedObj=*/true, 130 [](const clang::Expr *ArgOrigin, bool IsSafe) { 131 if (IsSafe) 132 return true; 133 if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) { 134 // foo(nullptr) 135 return true; 136 } 137 if (isa<IntegerLiteral>(ArgOrigin)) { 138 // FIXME: Check the value. 139 // foo(NULL) 140 return true; 141 } 142 if (isASafeCallArg(ArgOrigin)) 143 return true; 144 return false; 145 }); 146 } 147 148 bool shouldSkipCall(const CallExpr *CE) const { 149 const auto *Callee = CE->getDirectCallee(); 150 151 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc())) 152 return true; 153 154 if (Callee && TFA.isTrivial(Callee)) 155 return true; 156 157 if (CE->getNumArgs() == 0) 158 return false; 159 160 // If an assignment is problematic we should warn about the sole existence 161 // of object on LHS. 162 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) { 163 // Note: assignemnt to built-in type isn't derived from CallExpr. 164 if (MemberOp->getOperator() == 165 OO_Equal) { // Ignore assignment to Ref/RefPtr. 166 auto *callee = MemberOp->getDirectCallee(); 167 if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) { 168 if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) { 169 if (isRefCounted(classDecl)) 170 return true; 171 } 172 } 173 } 174 if (MemberOp->isAssignmentOp()) 175 return false; 176 } 177 178 if (!Callee) 179 return false; 180 181 if (isMethodOnWTFContainerType(Callee)) 182 return true; 183 184 auto overloadedOperatorType = Callee->getOverloadedOperator(); 185 if (overloadedOperatorType == OO_EqualEqual || 186 overloadedOperatorType == OO_ExclaimEqual || 187 overloadedOperatorType == OO_LessEqual || 188 overloadedOperatorType == OO_GreaterEqual || 189 overloadedOperatorType == OO_Spaceship || 190 overloadedOperatorType == OO_AmpAmp || 191 overloadedOperatorType == OO_PipePipe) 192 return true; 193 194 if (isCtorOfRefCounted(Callee)) 195 return true; 196 197 auto name = safeGetName(Callee); 198 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || 199 name == "dynamicDowncast" || name == "downcast" || 200 name == "checkedDowncast" || name == "uncheckedDowncast" || 201 name == "bitwise_cast" || name == "is" || name == "equal" || 202 name == "hash" || name == "isType" || 203 // FIXME: Most/all of these should be implemented via attributes. 204 name == "equalIgnoringASCIICase" || 205 name == "equalIgnoringASCIICaseCommon" || 206 name == "equalIgnoringNullity" || name == "toString") 207 return true; 208 209 return false; 210 } 211 212 bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const { 213 if (!isa<CXXMethodDecl>(Decl)) 214 return false; 215 auto *ClassDecl = Decl->getParent(); 216 if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl)) 217 return false; 218 219 auto *NsDecl = ClassDecl->getParent(); 220 if (!NsDecl || !isa<NamespaceDecl>(NsDecl)) 221 return false; 222 223 auto MethodName = safeGetName(Decl); 224 auto ClsNameStr = safeGetName(ClassDecl); 225 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef. 226 auto NamespaceName = safeGetName(NsDecl); 227 // FIXME: These should be implemented via attributes. 228 return NamespaceName == "WTF" && 229 (MethodName == "find" || MethodName == "findIf" || 230 MethodName == "reverseFind" || MethodName == "reverseFindIf" || 231 MethodName == "findIgnoringASCIICase" || MethodName == "get" || 232 MethodName == "inlineGet" || MethodName == "contains" || 233 MethodName == "containsIf" || 234 MethodName == "containsIgnoringASCIICase" || 235 MethodName == "startsWith" || MethodName == "endsWith" || 236 MethodName == "startsWithIgnoringASCIICase" || 237 MethodName == "endsWithIgnoringASCIICase" || 238 MethodName == "substring") && 239 (ClsName.ends_with("Vector") || ClsName.ends_with("Set") || 240 ClsName.ends_with("Map") || ClsName == "StringImpl" || 241 ClsName.ends_with("String")); 242 } 243 244 void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const { 245 assert(CallArg); 246 247 SmallString<100> Buf; 248 llvm::raw_svector_ostream Os(Buf); 249 250 const std::string paramName = safeGetName(Param); 251 Os << "Call argument"; 252 if (!paramName.empty()) { 253 Os << " for parameter "; 254 printQuotedQualifiedName(Os, Param); 255 } 256 Os << " is uncounted and unsafe."; 257 258 const SourceLocation SrcLocToReport = 259 isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc() 260 : CallArg->getSourceRange().getBegin(); 261 262 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); 263 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 264 Report->addRange(CallArg->getSourceRange()); 265 BR->emitReport(std::move(Report)); 266 } 267 268 void reportBugOnThis(const Expr *CallArg) const { 269 assert(CallArg); 270 271 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); 272 273 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); 274 auto Report = std::make_unique<BasicBugReport>( 275 Bug, "Call argument for 'this' parameter is uncounted and unsafe.", 276 BSLoc); 277 Report->addRange(CallArg->getSourceRange()); 278 BR->emitReport(std::move(Report)); 279 } 280 }; 281 } // namespace 282 283 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) { 284 Mgr.registerChecker<UncountedCallArgsChecker>(); 285 } 286 287 bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) { 288 return true; 289 } 290