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