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/DynamicRecursiveASTVisitor.h" 13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 15 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 16 #include "clang/StaticAnalyzer/Core/Checker.h" 17 #include <optional> 18 19 using namespace clang; 20 using namespace ento; 21 22 namespace { 23 class RawPtrRefLambdaCapturesChecker 24 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 25 private: 26 BugType Bug; 27 mutable BugReporter *BR = nullptr; 28 TrivialFunctionAnalysis TFA; 29 30 protected: 31 mutable std::optional<RetainTypeChecker> RTC; 32 33 public: 34 RawPtrRefLambdaCapturesChecker(const char *description) 35 : Bug(this, description, "WebKit coding guidelines") {} 36 37 virtual std::optional<bool> isUnsafePtr(QualType) const = 0; 38 virtual bool isPtrType(const std::string &) const = 0; 39 virtual const char *ptrKind(QualType QT) const = 0; 40 41 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 42 BugReporter &BRArg) const { 43 BR = &BRArg; 44 45 // The calls to checkAST* from AnalysisConsumer don't 46 // visit template instantiations or lambda classes. We 47 // want to visit those, so we make our own RecursiveASTVisitor. 48 struct LocalVisitor : DynamicRecursiveASTVisitor { 49 const RawPtrRefLambdaCapturesChecker *Checker; 50 llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore; 51 llvm::DenseSet<const LambdaExpr *> LambdasToIgnore; 52 llvm::DenseSet<const ValueDecl *> ProtectedThisDecls; 53 llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore; 54 55 QualType ClsType; 56 57 explicit LocalVisitor(const RawPtrRefLambdaCapturesChecker *Checker) 58 : Checker(Checker) { 59 assert(Checker); 60 ShouldVisitTemplateInstantiations = true; 61 ShouldVisitImplicitCode = false; 62 } 63 64 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override { 65 llvm::SaveAndRestore SavedDecl(ClsType); 66 if (CXXMD->isInstance()) 67 ClsType = CXXMD->getThisType(); 68 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD); 69 } 70 71 bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD) override { 72 llvm::SaveAndRestore SavedDecl(ClsType); 73 if (OCMD && OCMD->isInstanceMethod()) { 74 if (auto *ImplParamDecl = OCMD->getSelfDecl()) 75 ClsType = ImplParamDecl->getType(); 76 } 77 return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(OCMD); 78 } 79 80 bool VisitTypedefDecl(TypedefDecl *TD) override { 81 if (Checker->RTC) 82 Checker->RTC->visitTypedef(TD); 83 return true; 84 } 85 86 bool shouldCheckThis() { 87 auto result = 88 !ClsType.isNull() ? Checker->isUnsafePtr(ClsType) : std::nullopt; 89 return result && *result; 90 } 91 92 bool VisitLambdaExpr(LambdaExpr *L) override { 93 if (LambdasToIgnore.contains(L)) 94 return true; 95 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L), 96 ClsType); 97 return true; 98 } 99 100 bool VisitVarDecl(VarDecl *VD) override { 101 auto *Init = VD->getInit(); 102 if (!Init) 103 return true; 104 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 105 if (!L) 106 return true; 107 LambdasToIgnore.insert(L); // Evaluate lambdas in VisitDeclRefExpr. 108 return true; 109 } 110 111 bool VisitDeclRefExpr(DeclRefExpr *DRE) override { 112 if (DeclRefExprsToIgnore.contains(DRE)) 113 return true; 114 auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); 115 if (!VD) 116 return true; 117 auto *Init = VD->getInit(); 118 if (!Init) 119 return true; 120 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 121 if (!L) 122 return true; 123 LambdasToIgnore.insert(L); 124 Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L), 125 ClsType); 126 return true; 127 } 128 129 bool shouldTreatAllArgAsNoEscape(FunctionDecl *Decl) { 130 auto *NsDecl = Decl->getParent(); 131 if (!NsDecl || !isa<NamespaceDecl>(NsDecl)) 132 return false; 133 // WTF::switchOn(T, F... f) is a variadic template function and couldn't 134 // be annotated with NOESCAPE. We hard code it here to workaround that. 135 if (safeGetName(NsDecl) == "WTF" && safeGetName(Decl) == "switchOn") 136 return true; 137 // Treat every argument of functions in std::ranges as noescape. 138 if (safeGetName(NsDecl) == "ranges") { 139 if (auto *OuterDecl = NsDecl->getParent(); 140 OuterDecl && isa<NamespaceDecl>(OuterDecl) && 141 safeGetName(OuterDecl) == "std") 142 return true; 143 } 144 return false; 145 } 146 147 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override { 148 if (ConstructToIgnore.contains(CE)) 149 return true; 150 if (auto *Callee = CE->getConstructor()) { 151 unsigned ArgIndex = 0; 152 for (auto *Param : Callee->parameters()) { 153 if (ArgIndex >= CE->getNumArgs()) 154 return true; 155 auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); 156 if (auto *L = findLambdaInArg(Arg)) { 157 LambdasToIgnore.insert(L); 158 if (!Param->hasAttr<NoEscapeAttr>()) 159 Checker->visitLambdaExpr( 160 L, shouldCheckThis() && !hasProtectedThis(L), ClsType); 161 } 162 ++ArgIndex; 163 } 164 } 165 return true; 166 } 167 168 bool VisitCallExpr(CallExpr *CE) override { 169 checkCalleeLambda(CE); 170 if (auto *Callee = CE->getDirectCallee()) { 171 unsigned ArgIndex = isa<CXXOperatorCallExpr>(CE); 172 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee); 173 for (auto *Param : Callee->parameters()) { 174 if (ArgIndex >= CE->getNumArgs()) 175 return true; 176 auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); 177 if (auto *L = findLambdaInArg(Arg)) { 178 LambdasToIgnore.insert(L); 179 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) 180 Checker->visitLambdaExpr( 181 L, shouldCheckThis() && !hasProtectedThis(L), ClsType); 182 } 183 ++ArgIndex; 184 } 185 } 186 return true; 187 } 188 189 LambdaExpr *findLambdaInArg(Expr *E) { 190 if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(E)) 191 return Lambda; 192 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E); 193 if (!TempExpr) 194 return nullptr; 195 E = TempExpr->getSubExpr()->IgnoreParenCasts(); 196 if (!E) 197 return nullptr; 198 if (auto *Lambda = dyn_cast<LambdaExpr>(E)) 199 return Lambda; 200 auto *CE = dyn_cast_or_null<CXXConstructExpr>(E); 201 if (!CE || !CE->getNumArgs()) 202 return nullptr; 203 auto *CtorArg = CE->getArg(0)->IgnoreParenCasts(); 204 if (!CtorArg) 205 return nullptr; 206 auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg); 207 if (InnerCE && InnerCE->getNumArgs()) 208 CtorArg = InnerCE->getArg(0)->IgnoreParenCasts(); 209 auto updateIgnoreList = [&] { 210 ConstructToIgnore.insert(CE); 211 if (InnerCE) 212 ConstructToIgnore.insert(InnerCE); 213 }; 214 if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) { 215 updateIgnoreList(); 216 return Lambda; 217 } 218 if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) { 219 E = TempExpr->getSubExpr()->IgnoreParenCasts(); 220 if (auto *Lambda = dyn_cast<LambdaExpr>(E)) { 221 updateIgnoreList(); 222 return Lambda; 223 } 224 } 225 auto *DRE = dyn_cast<DeclRefExpr>(CtorArg); 226 if (!DRE) 227 return nullptr; 228 auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); 229 if (!VD) 230 return nullptr; 231 auto *Init = VD->getInit(); 232 if (!Init) 233 return nullptr; 234 if (auto *Lambda = dyn_cast<LambdaExpr>(Init)) { 235 updateIgnoreList(); 236 return Lambda; 237 } 238 TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts()); 239 if (!TempExpr) 240 return nullptr; 241 updateIgnoreList(); 242 return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr()); 243 } 244 245 void checkCalleeLambda(CallExpr *CE) { 246 auto *Callee = CE->getCallee(); 247 if (!Callee) 248 return; 249 auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts()); 250 if (!DRE) 251 return; 252 auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl()); 253 if (!MD || CE->getNumArgs() < 1) 254 return; 255 auto *Arg = CE->getArg(0)->IgnoreParenCasts(); 256 if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) { 257 LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe. 258 return; 259 } 260 auto *ArgRef = dyn_cast<DeclRefExpr>(Arg); 261 if (!ArgRef) 262 return; 263 auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl()); 264 if (!VD) 265 return; 266 auto *Init = VD->getInit(); 267 if (!Init) 268 return; 269 auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); 270 if (!L) 271 return; 272 DeclRefExprsToIgnore.insert(ArgRef); 273 LambdasToIgnore.insert(L); 274 } 275 276 bool hasProtectedThis(LambdaExpr *L) { 277 for (const LambdaCapture &OtherCapture : L->captures()) { 278 if (!OtherCapture.capturesVariable()) 279 continue; 280 if (auto *ValueDecl = OtherCapture.getCapturedVar()) { 281 if (declProtectsThis(ValueDecl)) { 282 ProtectedThisDecls.insert(ValueDecl); 283 return true; 284 } 285 } 286 } 287 return false; 288 } 289 290 bool declProtectsThis(const ValueDecl *ValueDecl) const { 291 auto *VD = dyn_cast<VarDecl>(ValueDecl); 292 if (!VD) 293 return false; 294 auto *Init = VD->getInit(); 295 if (!Init) 296 return false; 297 const Expr *Arg = Init->IgnoreParenCasts(); 298 do { 299 if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg)) 300 Arg = BTE->getSubExpr()->IgnoreParenCasts(); 301 if (auto *CE = dyn_cast<CXXConstructExpr>(Arg)) { 302 auto *Ctor = CE->getConstructor(); 303 if (!Ctor) 304 return false; 305 auto clsName = safeGetName(Ctor->getParent()); 306 if (Checker->isPtrType(clsName) && CE->getNumArgs()) { 307 Arg = CE->getArg(0)->IgnoreParenCasts(); 308 continue; 309 } 310 if (auto *Type = ClsType.getTypePtrOrNull()) { 311 if (auto *CXXR = Type->getPointeeCXXRecordDecl()) { 312 if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() && 313 CE->getNumArgs() == 1) { 314 Arg = CE->getArg(0)->IgnoreParenCasts(); 315 continue; 316 } 317 } 318 } 319 return false; 320 } 321 if (auto *CE = dyn_cast<CallExpr>(Arg)) { 322 if (CE->isCallToStdMove() && CE->getNumArgs() == 1) { 323 Arg = CE->getArg(0)->IgnoreParenCasts(); 324 continue; 325 } 326 if (auto *Callee = CE->getDirectCallee()) { 327 if (isCtorOfSafePtr(Callee) && CE->getNumArgs() == 1) { 328 Arg = CE->getArg(0)->IgnoreParenCasts(); 329 continue; 330 } 331 } 332 } 333 if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Arg)) { 334 auto OpCode = OpCE->getOperator(); 335 if (OpCode == OO_Star || OpCode == OO_Amp) { 336 auto *Callee = OpCE->getDirectCallee(); 337 if (!Callee) 338 return false; 339 auto clsName = safeGetName(Callee->getParent()); 340 if (!Checker->isPtrType(clsName) || !OpCE->getNumArgs()) 341 return false; 342 Arg = OpCE->getArg(0)->IgnoreParenCasts(); 343 continue; 344 } 345 } 346 if (auto *UO = dyn_cast<UnaryOperator>(Arg)) { 347 auto OpCode = UO->getOpcode(); 348 if (OpCode == UO_Deref || OpCode == UO_AddrOf) { 349 Arg = UO->getSubExpr()->IgnoreParenCasts(); 350 continue; 351 } 352 } 353 break; 354 } while (Arg); 355 if (auto *DRE = dyn_cast<DeclRefExpr>(Arg)) { 356 auto *Decl = DRE->getDecl(); 357 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Decl)) { 358 auto kind = ImplicitParam->getParameterKind(); 359 return kind == ImplicitParamKind::ObjCSelf || 360 kind == ImplicitParamKind::CXXThis; 361 } 362 return ProtectedThisDecls.contains(Decl); 363 } 364 return isa<CXXThisExpr>(Arg); 365 } 366 }; 367 368 LocalVisitor visitor(this); 369 if (RTC) 370 RTC->visitTranslationUnitDecl(TUD); 371 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 372 } 373 374 void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis, const QualType T, 375 bool ignoreParamVarDecl = false) const { 376 if (TFA.isTrivial(L->getBody())) 377 return; 378 for (const LambdaCapture &C : L->captures()) { 379 if (C.capturesVariable()) { 380 ValueDecl *CapturedVar = C.getCapturedVar(); 381 if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar)) 382 continue; 383 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(CapturedVar)) { 384 auto kind = ImplicitParam->getParameterKind(); 385 if ((kind == ImplicitParamKind::ObjCSelf || 386 kind == ImplicitParamKind::CXXThis) && 387 !shouldCheckThis) 388 continue; 389 } 390 QualType CapturedVarQualType = CapturedVar->getType(); 391 auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType()); 392 if (C.getCaptureKind() == LCK_ByCopy && 393 CapturedVarQualType->isReferenceType()) 394 continue; 395 if (IsUncountedPtr && *IsUncountedPtr) 396 reportBug(C, CapturedVar, CapturedVarQualType, L); 397 } else if (C.capturesThis() && shouldCheckThis) { 398 if (ignoreParamVarDecl) // this is always a parameter to this function. 399 continue; 400 reportBugOnThisPtr(C, T); 401 } 402 } 403 } 404 405 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, 406 const QualType T, LambdaExpr *L) const { 407 assert(CapturedVar); 408 409 auto Location = Capture.getLocation(); 410 if (isa<ImplicitParamDecl>(CapturedVar) && !Location.isValid()) 411 Location = L->getBeginLoc(); 412 413 SmallString<100> Buf; 414 llvm::raw_svector_ostream Os(Buf); 415 416 if (Capture.isExplicit()) { 417 Os << "Captured "; 418 } else { 419 Os << "Implicitly captured "; 420 } 421 if (isa<PointerType>(T) || isa<ObjCObjectPointerType>(T)) { 422 Os << "raw-pointer "; 423 } else { 424 Os << "reference "; 425 } 426 427 printQuotedQualifiedName(Os, CapturedVar); 428 Os << " to " << ptrKind(T) << " type is unsafe."; 429 430 PathDiagnosticLocation BSLoc(Location, BR->getSourceManager()); 431 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 432 BR->emitReport(std::move(Report)); 433 } 434 435 void reportBugOnThisPtr(const LambdaCapture &Capture, 436 const QualType T) const { 437 SmallString<100> Buf; 438 llvm::raw_svector_ostream Os(Buf); 439 440 if (Capture.isExplicit()) { 441 Os << "Captured "; 442 } else { 443 Os << "Implicitly captured "; 444 } 445 446 Os << "raw-pointer 'this' to " << ptrKind(T) << " type is unsafe."; 447 448 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); 449 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 450 BR->emitReport(std::move(Report)); 451 } 452 }; 453 454 class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { 455 public: 456 UncountedLambdaCapturesChecker() 457 : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted or " 458 "unchecked variable") {} 459 460 std::optional<bool> isUnsafePtr(QualType QT) const final { 461 auto result1 = isUncountedPtr(QT); 462 auto result2 = isUncheckedPtr(QT); 463 if (result1 && *result1) 464 return true; 465 if (result2 && *result2) 466 return true; 467 if (result1) 468 return *result1; 469 return result2; 470 } 471 472 virtual bool isPtrType(const std::string &Name) const final { 473 return isRefType(Name) || isCheckedPtr(Name); 474 } 475 476 const char *ptrKind(QualType QT) const final { 477 if (isUncounted(QT)) 478 return "uncounted"; 479 return "unchecked"; 480 } 481 }; 482 483 class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { 484 public: 485 UnretainedLambdaCapturesChecker() 486 : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained " 487 "variables") { 488 RTC = RetainTypeChecker(); 489 } 490 491 std::optional<bool> isUnsafePtr(QualType QT) const final { 492 return RTC->isUnretained(QT); 493 } 494 495 virtual bool isPtrType(const std::string &Name) const final { 496 return isRetainPtr(Name); 497 } 498 499 const char *ptrKind(QualType QT) const final { return "unretained"; } 500 }; 501 502 } // namespace 503 504 void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) { 505 Mgr.registerChecker<UncountedLambdaCapturesChecker>(); 506 } 507 508 bool ento::shouldRegisterUncountedLambdaCapturesChecker( 509 const CheckerManager &mgr) { 510 return true; 511 } 512 513 void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) { 514 Mgr.registerChecker<UnretainedLambdaCapturesChecker>(); 515 } 516 517 bool ento::shouldRegisterUnretainedLambdaCapturesChecker( 518 const CheckerManager &mgr) { 519 return true; 520 } 521