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