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 llvm::DenseSet<const LambdaExpr *> LambdasToIgnore; 44 QualType ClsType; 45 46 explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker) 47 : Checker(Checker) { 48 assert(Checker); 49 ShouldVisitTemplateInstantiations = true; 50 ShouldVisitImplicitCode = false; 51 } 52 53 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override { 54 llvm::SaveAndRestore SavedDecl(ClsType); 55 if (CXXMD->isInstance()) 56 ClsType = CXXMD->getThisType(); 57 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD); 58 } 59 60 bool shouldCheckThis() { 61 auto result = !ClsType.isNull() ? isUnsafePtr(ClsType) : std::nullopt; 62 return result && *result; 63 } 64 65 bool VisitLambdaExpr(LambdaExpr *L) override { 66 if (LambdasToIgnore.contains(L)) 67 return true; 68 Checker->visitLambdaExpr(L, shouldCheckThis()); 69 return true; 70 } 71 72 bool VisitVarDecl(VarDecl *VD) override { 73 auto *Init = VD->getInit(); 74 if (!Init) 75 return true; 76 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 77 if (!L) 78 return true; 79 LambdasToIgnore.insert(L); // Evaluate lambdas in VisitDeclRefExpr. 80 return true; 81 } 82 83 bool VisitDeclRefExpr(DeclRefExpr *DRE) override { 84 if (DeclRefExprsToIgnore.contains(DRE)) 85 return true; 86 auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); 87 if (!VD) 88 return true; 89 auto *Init = VD->getInit(); 90 if (!Init) 91 return true; 92 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 93 if (!L) 94 return true; 95 LambdasToIgnore.insert(L); 96 Checker->visitLambdaExpr(L, shouldCheckThis()); 97 return true; 98 } 99 100 // WTF::switchOn(T, F... f) is a variadic template function and couldn't 101 // be annotated with NOESCAPE. We hard code it here to workaround that. 102 bool shouldTreatAllArgAsNoEscape(FunctionDecl *Decl) { 103 auto *NsDecl = Decl->getParent(); 104 if (!NsDecl || !isa<NamespaceDecl>(NsDecl)) 105 return false; 106 return safeGetName(NsDecl) == "WTF" && safeGetName(Decl) == "switchOn"; 107 } 108 109 bool VisitCallExpr(CallExpr *CE) override { 110 checkCalleeLambda(CE); 111 if (auto *Callee = CE->getDirectCallee()) { 112 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee); 113 unsigned ArgIndex = 0; 114 for (auto *Param : Callee->parameters()) { 115 if (ArgIndex >= CE->getNumArgs()) 116 return true; 117 auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); 118 if (auto *L = findLambdaInArg(Arg)) { 119 LambdasToIgnore.insert(L); 120 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) 121 Checker->visitLambdaExpr(L, shouldCheckThis()); 122 } 123 ++ArgIndex; 124 } 125 } 126 return true; 127 } 128 129 LambdaExpr *findLambdaInArg(Expr *E) { 130 if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(E)) 131 return Lambda; 132 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E); 133 if (!TempExpr) 134 return nullptr; 135 E = TempExpr->getSubExpr()->IgnoreParenCasts(); 136 if (!E) 137 return nullptr; 138 if (auto *Lambda = dyn_cast<LambdaExpr>(E)) 139 return Lambda; 140 auto *CE = dyn_cast_or_null<CXXConstructExpr>(E); 141 if (!CE || !CE->getNumArgs()) 142 return nullptr; 143 auto *CtorArg = CE->getArg(0)->IgnoreParenCasts(); 144 if (!CtorArg) 145 return nullptr; 146 if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) 147 return Lambda; 148 auto *DRE = dyn_cast<DeclRefExpr>(CtorArg); 149 if (!DRE) 150 return nullptr; 151 auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); 152 if (!VD) 153 return nullptr; 154 auto *Init = VD->getInit(); 155 if (!Init) 156 return nullptr; 157 TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts()); 158 if (!TempExpr) 159 return nullptr; 160 return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr()); 161 } 162 163 void checkCalleeLambda(CallExpr *CE) { 164 auto *Callee = CE->getCallee(); 165 if (!Callee) 166 return; 167 auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts()); 168 if (!DRE) 169 return; 170 auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl()); 171 if (!MD || CE->getNumArgs() < 1) 172 return; 173 auto *Arg = CE->getArg(0)->IgnoreParenCasts(); 174 if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) { 175 LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe. 176 return; 177 } 178 auto *ArgRef = dyn_cast<DeclRefExpr>(Arg); 179 if (!ArgRef) 180 return; 181 auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl()); 182 if (!VD) 183 return; 184 auto *Init = VD->getInit(); 185 if (!Init) 186 return; 187 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 188 if (!L) 189 return; 190 DeclRefExprsToIgnore.insert(ArgRef); 191 LambdasToIgnore.insert(L); 192 Checker->visitLambdaExpr(L, shouldCheckThis(), 193 /* ignoreParamVarDecl */ true); 194 } 195 }; 196 197 LocalVisitor visitor(this); 198 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 199 } 200 201 void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis, 202 bool ignoreParamVarDecl = false) const { 203 if (TFA.isTrivial(L->getBody())) 204 return; 205 for (const LambdaCapture &C : L->captures()) { 206 if (C.capturesVariable()) { 207 ValueDecl *CapturedVar = C.getCapturedVar(); 208 if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar)) 209 continue; 210 QualType CapturedVarQualType = CapturedVar->getType(); 211 auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType()); 212 if (IsUncountedPtr && *IsUncountedPtr) 213 reportBug(C, CapturedVar, CapturedVarQualType); 214 } else if (C.capturesThis() && shouldCheckThis) { 215 if (ignoreParamVarDecl) // this is always a parameter to this function. 216 continue; 217 bool hasProtectThis = false; 218 for (const LambdaCapture &OtherCapture : L->captures()) { 219 if (!OtherCapture.capturesVariable()) 220 continue; 221 if (auto *ValueDecl = OtherCapture.getCapturedVar()) { 222 if (protectThis(ValueDecl)) { 223 hasProtectThis = true; 224 break; 225 } 226 } 227 } 228 if (!hasProtectThis) 229 reportBugOnThisPtr(C); 230 } 231 } 232 } 233 234 bool protectThis(const ValueDecl *ValueDecl) const { 235 auto *VD = dyn_cast<VarDecl>(ValueDecl); 236 if (!VD) 237 return false; 238 auto *Init = VD->getInit()->IgnoreParenCasts(); 239 if (!Init) 240 return false; 241 auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Init); 242 if (!BTE) 243 return false; 244 auto *CE = dyn_cast_or_null<CXXConstructExpr>(BTE->getSubExpr()); 245 if (!CE) 246 return false; 247 auto *Ctor = CE->getConstructor(); 248 if (!Ctor) 249 return false; 250 auto clsName = safeGetName(Ctor->getParent()); 251 if (!isRefType(clsName) || !CE->getNumArgs()) 252 return false; 253 auto *Arg = CE->getArg(0)->IgnoreParenCasts(); 254 while (auto *UO = dyn_cast<UnaryOperator>(Arg)) { 255 auto OpCode = UO->getOpcode(); 256 if (OpCode == UO_Deref || OpCode == UO_AddrOf) 257 Arg = UO->getSubExpr(); 258 else 259 break; 260 } 261 return isa<CXXThisExpr>(Arg); 262 } 263 264 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, 265 const QualType T) const { 266 assert(CapturedVar); 267 268 SmallString<100> Buf; 269 llvm::raw_svector_ostream Os(Buf); 270 271 if (Capture.isExplicit()) { 272 Os << "Captured "; 273 } else { 274 Os << "Implicitly captured "; 275 } 276 if (T->isPointerType()) { 277 Os << "raw-pointer "; 278 } else { 279 assert(T->isReferenceType()); 280 Os << "reference "; 281 } 282 283 printQuotedQualifiedName(Os, Capture.getCapturedVar()); 284 Os << " to ref-counted type or CheckedPtr-capable type is unsafe."; 285 286 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); 287 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 288 BR->emitReport(std::move(Report)); 289 } 290 291 void reportBugOnThisPtr(const LambdaCapture &Capture) const { 292 SmallString<100> Buf; 293 llvm::raw_svector_ostream Os(Buf); 294 295 if (Capture.isExplicit()) { 296 Os << "Captured "; 297 } else { 298 Os << "Implicitly captured "; 299 } 300 301 Os << "raw-pointer 'this' to ref-counted type or CheckedPtr-capable type " 302 "is unsafe."; 303 304 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); 305 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 306 BR->emitReport(std::move(Report)); 307 } 308 }; 309 } // namespace 310 311 void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) { 312 Mgr.registerChecker<UncountedLambdaCapturesChecker>(); 313 } 314 315 bool ento::shouldRegisterUncountedLambdaCapturesChecker( 316 const CheckerManager &mgr) { 317 return true; 318 } 319