1 //=======- UncountedLambdaCapturesChecker.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/DynamicRecursiveASTVisitor.h" 14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include <optional> 19 20 using namespace clang; 21 using namespace ento; 22 23 namespace { 24 class UncountedLambdaCapturesChecker 25 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 26 private: 27 BugType Bug{this, "Lambda capture of uncounted variable", 28 "WebKit coding guidelines"}; 29 mutable BugReporter *BR = nullptr; 30 TrivialFunctionAnalysis TFA; 31 32 public: 33 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 34 BugReporter &BRArg) const { 35 BR = &BRArg; 36 37 // The calls to checkAST* from AnalysisConsumer don't 38 // visit template instantiations or lambda classes. We 39 // want to visit those, so we make our own RecursiveASTVisitor. 40 struct LocalVisitor : DynamicRecursiveASTVisitor { 41 const UncountedLambdaCapturesChecker *Checker; 42 llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore; 43 QualType ClsType; 44 45 explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker) 46 : Checker(Checker) { 47 assert(Checker); 48 ShouldVisitTemplateInstantiations = true; 49 ShouldVisitImplicitCode = false; 50 } 51 52 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override { 53 llvm::SaveAndRestore SavedDecl(ClsType); 54 ClsType = CXXMD->getThisType(); 55 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD); 56 } 57 58 bool shouldCheckThis() { 59 auto result = !ClsType.isNull() ? isUnsafePtr(ClsType) : std::nullopt; 60 return result && *result; 61 } 62 63 bool VisitDeclRefExpr(DeclRefExpr *DRE) override { 64 if (DeclRefExprsToIgnore.contains(DRE)) 65 return true; 66 auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); 67 if (!VD) 68 return true; 69 auto *Init = VD->getInit(); 70 if (!Init) 71 return true; 72 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 73 if (!L) 74 return true; 75 Checker->visitLambdaExpr(L, shouldCheckThis()); 76 return true; 77 } 78 79 // WTF::switchOn(T, F... f) is a variadic template function and couldn't 80 // be annotated with NOESCAPE. We hard code it here to workaround that. 81 bool shouldTreatAllArgAsNoEscape(FunctionDecl *Decl) { 82 auto *NsDecl = Decl->getParent(); 83 if (!NsDecl || !isa<NamespaceDecl>(NsDecl)) 84 return false; 85 return safeGetName(NsDecl) == "WTF" && safeGetName(Decl) == "switchOn"; 86 } 87 88 bool VisitCallExpr(CallExpr *CE) override { 89 checkCalleeLambda(CE); 90 if (auto *Callee = CE->getDirectCallee()) { 91 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee); 92 unsigned ArgIndex = 0; 93 for (auto *Param : Callee->parameters()) { 94 if (ArgIndex >= CE->getNumArgs()) 95 return true; 96 auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); 97 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) { 98 if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) { 99 Checker->visitLambdaExpr(L, shouldCheckThis()); 100 } 101 } 102 ++ArgIndex; 103 } 104 } 105 return true; 106 } 107 108 void checkCalleeLambda(CallExpr *CE) { 109 auto *Callee = CE->getCallee(); 110 if (!Callee) 111 return; 112 auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts()); 113 if (!DRE) 114 return; 115 auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl()); 116 if (!MD || CE->getNumArgs() != 1) 117 return; 118 auto *Arg = CE->getArg(0)->IgnoreParenCasts(); 119 auto *ArgRef = dyn_cast<DeclRefExpr>(Arg); 120 if (!ArgRef) 121 return; 122 auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl()); 123 if (!VD) 124 return; 125 auto *Init = VD->getInit(); 126 if (!Init) 127 return; 128 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 129 if (!L) 130 return; 131 DeclRefExprsToIgnore.insert(ArgRef); 132 Checker->visitLambdaExpr(L, shouldCheckThis(), 133 /* ignoreParamVarDecl */ true); 134 } 135 }; 136 137 LocalVisitor visitor(this); 138 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 139 } 140 141 void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis, 142 bool ignoreParamVarDecl = false) const { 143 if (TFA.isTrivial(L->getBody())) 144 return; 145 for (const LambdaCapture &C : L->captures()) { 146 if (C.capturesVariable()) { 147 ValueDecl *CapturedVar = C.getCapturedVar(); 148 if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar)) 149 continue; 150 QualType CapturedVarQualType = CapturedVar->getType(); 151 auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType()); 152 if (IsUncountedPtr && *IsUncountedPtr) 153 reportBug(C, CapturedVar, CapturedVarQualType); 154 } else if (C.capturesThis() && shouldCheckThis) { 155 if (ignoreParamVarDecl) // this is always a parameter to this function. 156 continue; 157 reportBugOnThisPtr(C); 158 } 159 } 160 } 161 162 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, 163 const QualType T) const { 164 assert(CapturedVar); 165 166 SmallString<100> Buf; 167 llvm::raw_svector_ostream Os(Buf); 168 169 if (Capture.isExplicit()) { 170 Os << "Captured "; 171 } else { 172 Os << "Implicitly captured "; 173 } 174 if (T->isPointerType()) { 175 Os << "raw-pointer "; 176 } else { 177 assert(T->isReferenceType()); 178 Os << "reference "; 179 } 180 181 printQuotedQualifiedName(Os, Capture.getCapturedVar()); 182 Os << " to ref-counted type or CheckedPtr-capable type is unsafe."; 183 184 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); 185 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 186 BR->emitReport(std::move(Report)); 187 } 188 189 void reportBugOnThisPtr(const LambdaCapture &Capture) const { 190 SmallString<100> Buf; 191 llvm::raw_svector_ostream Os(Buf); 192 193 if (Capture.isExplicit()) { 194 Os << "Captured "; 195 } else { 196 Os << "Implicitly captured "; 197 } 198 199 Os << "raw-pointer 'this' to ref-counted type or CheckedPtr-capable type " 200 "is unsafe."; 201 202 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); 203 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 204 BR->emitReport(std::move(Report)); 205 } 206 }; 207 } // namespace 208 209 void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) { 210 Mgr.registerChecker<UncountedLambdaCapturesChecker>(); 211 } 212 213 bool ento::shouldRegisterUncountedLambdaCapturesChecker( 214 const CheckerManager &mgr) { 215 return true; 216 } 217