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 return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr()); 159 } 160 161 void checkCalleeLambda(CallExpr *CE) { 162 auto *Callee = CE->getCallee(); 163 if (!Callee) 164 return; 165 auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts()); 166 if (!DRE) 167 return; 168 auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl()); 169 if (!MD || CE->getNumArgs() < 1) 170 return; 171 auto *Arg = CE->getArg(0)->IgnoreParenCasts(); 172 if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) { 173 LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe. 174 return; 175 } 176 auto *ArgRef = dyn_cast<DeclRefExpr>(Arg); 177 if (!ArgRef) 178 return; 179 auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl()); 180 if (!VD) 181 return; 182 auto *Init = VD->getInit(); 183 if (!Init) 184 return; 185 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 186 if (!L) 187 return; 188 DeclRefExprsToIgnore.insert(ArgRef); 189 LambdasToIgnore.insert(L); 190 Checker->visitLambdaExpr(L, shouldCheckThis(), 191 /* ignoreParamVarDecl */ true); 192 } 193 }; 194 195 LocalVisitor visitor(this); 196 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 197 } 198 199 void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis, 200 bool ignoreParamVarDecl = false) const { 201 if (TFA.isTrivial(L->getBody())) 202 return; 203 for (const LambdaCapture &C : L->captures()) { 204 if (C.capturesVariable()) { 205 ValueDecl *CapturedVar = C.getCapturedVar(); 206 if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar)) 207 continue; 208 QualType CapturedVarQualType = CapturedVar->getType(); 209 auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType()); 210 if (IsUncountedPtr && *IsUncountedPtr) 211 reportBug(C, CapturedVar, CapturedVarQualType); 212 } else if (C.capturesThis() && shouldCheckThis) { 213 if (ignoreParamVarDecl) // this is always a parameter to this function. 214 continue; 215 bool hasProtectThis = false; 216 for (const LambdaCapture &OtherCapture : L->captures()) { 217 if (!OtherCapture.capturesVariable()) 218 continue; 219 if (auto *ValueDecl = OtherCapture.getCapturedVar()) { 220 if (protectThis(ValueDecl)) { 221 hasProtectThis = true; 222 break; 223 } 224 } 225 } 226 if (!hasProtectThis) 227 reportBugOnThisPtr(C); 228 } 229 } 230 } 231 232 bool protectThis(const ValueDecl *ValueDecl) const { 233 auto *VD = dyn_cast<VarDecl>(ValueDecl); 234 if (!VD) 235 return false; 236 auto *Init = VD->getInit()->IgnoreParenCasts(); 237 if (!Init) 238 return false; 239 auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Init); 240 if (!BTE) 241 return false; 242 auto *CE = dyn_cast_or_null<CXXConstructExpr>(BTE->getSubExpr()); 243 if (!CE) 244 return false; 245 auto *Ctor = CE->getConstructor(); 246 if (!Ctor) 247 return false; 248 auto clsName = safeGetName(Ctor->getParent()); 249 if (!isRefType(clsName) || !CE->getNumArgs()) 250 return false; 251 auto *Arg = CE->getArg(0)->IgnoreParenCasts(); 252 while (auto *UO = dyn_cast<UnaryOperator>(Arg)) { 253 auto OpCode = UO->getOpcode(); 254 if (OpCode == UO_Deref || OpCode == UO_AddrOf) 255 Arg = UO->getSubExpr(); 256 else 257 break; 258 } 259 return isa<CXXThisExpr>(Arg); 260 } 261 262 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, 263 const QualType T) const { 264 assert(CapturedVar); 265 266 SmallString<100> Buf; 267 llvm::raw_svector_ostream Os(Buf); 268 269 if (Capture.isExplicit()) { 270 Os << "Captured "; 271 } else { 272 Os << "Implicitly captured "; 273 } 274 if (T->isPointerType()) { 275 Os << "raw-pointer "; 276 } else { 277 assert(T->isReferenceType()); 278 Os << "reference "; 279 } 280 281 printQuotedQualifiedName(Os, Capture.getCapturedVar()); 282 Os << " to ref-counted type or CheckedPtr-capable type is unsafe."; 283 284 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); 285 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 286 BR->emitReport(std::move(Report)); 287 } 288 289 void reportBugOnThisPtr(const LambdaCapture &Capture) const { 290 SmallString<100> Buf; 291 llvm::raw_svector_ostream Os(Buf); 292 293 if (Capture.isExplicit()) { 294 Os << "Captured "; 295 } else { 296 Os << "Implicitly captured "; 297 } 298 299 Os << "raw-pointer 'this' to ref-counted type or CheckedPtr-capable type " 300 "is unsafe."; 301 302 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); 303 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 304 BR->emitReport(std::move(Report)); 305 } 306 }; 307 } // namespace 308 309 void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) { 310 Mgr.registerChecker<UncountedLambdaCapturesChecker>(); 311 } 312 313 bool ento::shouldRegisterUncountedLambdaCapturesChecker( 314 const CheckerManager &mgr) { 315 return true; 316 } 317