1//=======- RawPtrRefLambdaCapturesChecker.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 "RawPtrRefSafetyModel.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
20using namespace clang;
21using namespace ento;
22
23namespace {
24class RawPtrRefLambdaCapturesChecker
25 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
26private:
27 BugType Bug;
28 mutable BugReporter *BR = nullptr;
29 TrivialFunctionAnalysis TFA;
30
31protected:
32 const std::unique_ptr<PtrRefSafetyModel> Model;
33
34public:
35 RawPtrRefLambdaCapturesChecker(const char *description,
36 std::unique_ptr<PtrRefSafetyModel> Model)
37 : Bug(this, description, "WebKit coding guidelines"),
38 Model(std::move(Model)) {}
39
40 std::optional<bool> isUnsafePtr(QualType QT) const {
41 return isUnsafePtrForStorage(Model: *Model, T: QT);
42 }
43 bool isPtrType(const std::string &Name) const {
44 return Model->isPtrType(Name);
45 }
46
47 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
48 BugReporter &BRArg) const {
49 BR = &BRArg;
50
51 // The calls to checkAST* from AnalysisConsumer don't
52 // visit template instantiations or lambda classes. We
53 // want to visit those, so we make our own RecursiveASTVisitor.
54 struct LocalVisitor : DynamicRecursiveASTVisitor {
55 const RawPtrRefLambdaCapturesChecker *Checker;
56 llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
57 llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
58 llvm::DenseSet<const ValueDecl *> ProtectedThisDecls;
59 llvm::DenseSet<const CallExpr *> CallToIgnore;
60 llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore;
61 llvm::DenseMap<const VarDecl *, SmallVector<const LambdaExpr *>>
62 LambdaOwnerMap;
63
64 QualType ClsType;
65
66 explicit LocalVisitor(const RawPtrRefLambdaCapturesChecker *Checker)
67 : Checker(Checker) {
68 assert(Checker);
69 ShouldVisitTemplateInstantiations = true;
70 ShouldVisitImplicitCode = false;
71 }
72
73 bool TraverseDecl(Decl *D) override {
74 // A template pattern is checked through its instantiations, which are
75 // traversed from the TemplateDecl itself. In the pattern the callee of
76 // a call may still be an unresolved overload set, so whether a lambda
77 // argument can escape isn't known, and a lambda in a pattern which is
78 // never instantiated is never used. Don't enter it at all.
79 if (D && !isa<TemplateDecl>(Val: D) && D->isTemplated())
80 return true;
81 return DynamicRecursiveASTVisitor::TraverseDecl(D);
82 }
83
84 bool TraverseCXXConstructorDecl(CXXConstructorDecl *Ctor) override {
85 llvm::SaveAndRestore SavedDecl(ClsType);
86 ClsType = Ctor->getThisType();
87 return DynamicRecursiveASTVisitor::TraverseCXXConstructorDecl(D: Ctor);
88 }
89
90 bool TraverseCXXDestructorDecl(CXXDestructorDecl *Dtor) override {
91 llvm::SaveAndRestore SavedDecl(ClsType);
92 ClsType = Dtor->getThisType();
93 return DynamicRecursiveASTVisitor::TraverseCXXDestructorDecl(D: Dtor);
94 }
95
96 bool TraverseCXXMethodDecl(CXXMethodDecl *CXXMD) override {
97 llvm::SaveAndRestore SavedDecl(ClsType);
98 // 'this' in the body of a lambda's call operator refers to the object
99 // of the enclosing method, so keep the class of that method. This
100 // matters for the instantiations of a generic lambda, which are
101 // traversed as declarations rather than as a body.
102 if (CXXMD->isInstance() && !CXXMD->getParent()->isLambda())
103 ClsType = CXXMD->getThisType();
104 return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(D: CXXMD);
105 }
106
107 bool TraverseObjCMethodDecl(ObjCMethodDecl *OCMD) override {
108 llvm::SaveAndRestore SavedDecl(ClsType);
109 if (OCMD && OCMD->isInstanceMethod()) {
110 if (auto *ImplParamDecl = OCMD->getSelfDecl())
111 ClsType = ImplParamDecl->getType();
112 }
113 return DynamicRecursiveASTVisitor::TraverseObjCMethodDecl(D: OCMD);
114 }
115
116 bool VisitTypedefDecl(TypedefDecl *TD) override {
117 if (auto *RTC = Checker->Model->retainTypeChecker())
118 RTC->visitTypedef(TD);
119 return true;
120 }
121
122 bool shouldCheckThis() {
123 // A pointer to the object itself is not a loan on its interior.
124 if (Checker->Model->checksForInteriorDestruction())
125 return false;
126 auto result =
127 !ClsType.isNull() ? Checker->isUnsafePtr(QT: ClsType) : std::nullopt;
128 return result && *result;
129 }
130
131 bool VisitLambdaExpr(LambdaExpr *L) override {
132 if (LambdasToIgnore.contains(V: L))
133 return true;
134 Checker->visitLambdaExpr(L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L),
135 T: ClsType);
136 return true;
137 }
138
139 bool TraverseLambdaExpr(LambdaExpr *L) override {
140 auto *FTD = L->getLambdaClass()->getDependentLambdaCallOperator();
141 if (!FTD)
142 return DynamicRecursiveASTVisitor::TraverseLambdaExpr(S: L);
143 // The body of a generic lambda is the pattern of its call operator,
144 // but it is reached from the LambdaExpr as a statement, so TraverseDecl
145 // never gets to skip it. Visit the lambda itself, traverse the capture
146 // initializers, which are evaluated in the enclosing scope, and then
147 // the call operator, of which the pattern is skipped like any other and
148 // the instantiations are traversed. The initializers are traversed as
149 // expressions because the variable of an init capture is declared in
150 // the pattern.
151 if (!VisitLambdaExpr(L))
152 return false;
153 for (unsigned I = 0, N = L->capture_size(); I != N; ++I) {
154 if (!(L->capture_begin() + I)->isExplicit())
155 continue;
156 if (auto *Init = L->capture_init_begin()[I];
157 Init && !TraverseStmt(S: Init))
158 return false;
159 }
160 return TraverseDecl(D: FTD);
161 }
162
163 bool VisitVarDecl(VarDecl *VD) override {
164 auto *Init = VD->getInit();
165 if (!Init)
166 return true;
167 if (auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts())) {
168 LambdasToIgnore.insert(V: L); // Evaluate lambdas in VisitDeclRefExpr.
169 return true;
170 }
171 if (!VD->hasLocalStorage())
172 return true;
173 if (auto *E = dyn_cast<ExprWithCleanups>(Val: Init))
174 Init = E->getSubExpr();
175 if (auto *E = dyn_cast<CXXBindTemporaryExpr>(Val: Init))
176 Init = E->getSubExpr();
177 if (auto *CE = dyn_cast<CallExpr>(Val: Init)) {
178 if (auto *Callee = CE->getDirectCallee()) {
179 auto FnName = safeGetName(ASTNode: Callee);
180 unsigned ArgCnt = CE->getNumArgs();
181 if (FnName == "makeScopeExit" && ArgCnt == 1) {
182 auto *Arg = CE->getArg(Arg: 0);
183 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
184 Arg = E->getSubExpr();
185 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
186 addLambdaOwner(VD, CE, L);
187 } else if (FnName == "makeVisitor") {
188 for (unsigned ArgIndex = 0; ArgIndex < ArgCnt; ++ArgIndex) {
189 auto *Arg = CE->getArg(Arg: ArgIndex);
190 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
191 Arg = E->getSubExpr();
192 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
193 addLambdaOwner(VD, CE, L);
194 }
195 }
196 }
197 } else if (auto *CE = dyn_cast<CXXConstructExpr>(Val: Init)) {
198 if (auto *Ctor = CE->getConstructor()) {
199 if (auto *Cls = Ctor->getParent()) {
200 auto FnName = safeGetName(ASTNode: Cls);
201 unsigned ArgCnt = CE->getNumArgs();
202 if (FnName == "ScopeExit" && ArgCnt == 1) {
203 auto *Arg = CE->getArg(Arg: 0);
204 if (auto *E = dyn_cast<MaterializeTemporaryExpr>(Val: Arg))
205 Arg = E->getSubExpr();
206 if (auto *L = dyn_cast<LambdaExpr>(Val: Arg))
207 addLambdaOwner(VD, CE, L);
208 }
209 }
210 }
211 }
212 return true;
213 }
214
215 void addLambdaOwner(VarDecl *VD, CallExpr *CE, LambdaExpr *L) {
216 auto result = LambdaOwnerMap.insert(
217 KV: std::make_pair(x&: VD, y: SmallVector<const LambdaExpr *>{L}));
218 if (!result.second)
219 result.first->second.push_back(Elt: L);
220 CallToIgnore.insert(V: CE);
221 LambdasToIgnore.insert(V: L);
222 }
223
224 void addLambdaOwner(VarDecl *VD, CXXConstructExpr *CE, LambdaExpr *L) {
225 auto result = LambdaOwnerMap.insert(
226 KV: std::make_pair(x&: VD, y: SmallVector<const LambdaExpr *>{L}));
227 if (!result.second)
228 result.first->second.push_back(Elt: L);
229 ConstructToIgnore.insert(V: CE);
230 LambdasToIgnore.insert(V: L);
231 }
232
233 bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
234 if (DeclRefExprsToIgnore.contains(V: DRE))
235 return true;
236 auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl());
237 if (!VD)
238 return true;
239 if (auto It = LambdaOwnerMap.find(Val: VD); It != LambdaOwnerMap.end()) {
240 for (auto *L : It->second) {
241 Checker->visitLambdaExpr(
242 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
243 }
244 return true;
245 }
246 auto *Init = VD->getInit();
247 if (!Init)
248 return true;
249 auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts());
250 if (!L)
251 return true;
252 LambdasToIgnore.insert(V: L);
253 Checker->visitLambdaExpr(L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L),
254 T: ClsType);
255 return true;
256 }
257
258 bool shouldTreatAllArgAsNoEscape(FunctionDecl *FDecl) {
259 std::string PreviousName = safeGetName(ASTNode: FDecl);
260 for (auto *Decl = FDecl->getParent(); Decl; Decl = Decl->getParent()) {
261 if (!isa<NamespaceDecl>(Val: Decl) && !isa<CXXRecordDecl>(Val: Decl))
262 return false;
263 if (auto *NS = dyn_cast<NamespaceDecl>(Val: Decl); NS && NS->isInline())
264 continue;
265 auto Name = safeGetName(ASTNode: Decl);
266 // WTF::switchOn(T, F... f) is a variadic template function and
267 // couldn't be annotated with NOESCAPE. We hard code it here to
268 // workaround that.
269 if (Name == "WTF" && PreviousName == "switchOn")
270 return true;
271 if (Name == "std") {
272 // Treat every argument of functions in std::ranges as noescape.
273 if (PreviousName == "ranges")
274 return true;
275 // Treat every argument of call_once as noescape even though only
276 // the second argument is lambda since we can't add annotation to
277 // a std function.
278 if (PreviousName == "call_once")
279 return true;
280 }
281 PreviousName = Name;
282 }
283 return false;
284 }
285
286 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
287 if (ConstructToIgnore.contains(V: CE))
288 return true;
289 if (auto *Callee = CE->getConstructor()) {
290 unsigned ArgIndex = 0;
291 for (auto *Param : Callee->parameters()) {
292 if (ArgIndex >= CE->getNumArgs())
293 return true;
294 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
295 if (auto *L = findLambdaInArg(E: Arg)) {
296 LambdasToIgnore.insert(V: L);
297 if (!Param->hasAttr<NoEscapeAttr>())
298 Checker->visitLambdaExpr(
299 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
300 }
301 ++ArgIndex;
302 }
303 }
304 return true;
305 }
306
307 bool VisitCallExpr(CallExpr *CE) override {
308 if (CallToIgnore.contains(V: CE))
309 return true;
310 checkCalleeLambda(CE);
311 if (auto *Callee = CE->getDirectCallee()) {
312 if (isVisitFunction(CallExpr: CE, FnDecl: Callee))
313 return true;
314 checkParameters(CE, Callee);
315 } else if (auto *CalleeE = CE->getCallee()) {
316 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: CalleeE->IgnoreParenCasts())) {
317 if (auto *Callee = dyn_cast_or_null<FunctionDecl>(Val: DRE->getDecl()))
318 checkParameters(CE, Callee);
319 }
320 }
321 return true;
322 }
323
324 bool isVisitFunction(CallExpr *CallExpr, FunctionDecl *FnDecl) {
325 bool IsVisitFn = safeGetName(ASTNode: FnDecl) == "visit";
326 if (!IsVisitFn)
327 return false;
328 bool ArgCnt = CallExpr->getNumArgs();
329 if (!ArgCnt)
330 return false;
331 auto *Ns = FnDecl->getParent();
332 if (!Ns)
333 return false;
334 auto NsName = safeGetName(ASTNode: Ns);
335 if (NsName != "WTF" && NsName != "std")
336 return false;
337 auto *Arg = CallExpr->getArg(Arg: 0);
338 if (!Arg)
339 return false;
340 auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg->IgnoreParenCasts());
341 if (!DRE)
342 return false;
343 auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl());
344 if (!VD)
345 return false;
346 if (!LambdaOwnerMap.contains(Val: VD))
347 return false;
348 DeclRefExprsToIgnore.insert(V: DRE);
349 return true;
350 }
351
352 void checkParameters(CallExpr *CE, FunctionDecl *Callee) {
353 unsigned ArgIndex = isa<CXXOperatorCallExpr>(Val: CE);
354 bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(FDecl: Callee);
355 for (auto *Param : Callee->parameters()) {
356 if (ArgIndex >= CE->getNumArgs())
357 return;
358 auto *Arg = CE->getArg(Arg: ArgIndex)->IgnoreParenCasts();
359 if (auto *L = findLambdaInArg(E: Arg)) {
360 LambdasToIgnore.insert(V: L);
361 if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
362 Checker->visitLambdaExpr(
363 L, shouldCheckThis: shouldCheckThis() && !hasProtectedThis(L), T: ClsType);
364 }
365 ++ArgIndex;
366 }
367 }
368
369 LambdaExpr *findLambdaInArg(Expr *E) {
370 if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(Val: E))
371 return Lambda;
372 auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(Val: E);
373 if (!TempExpr)
374 return nullptr;
375 E = TempExpr->getSubExpr()->IgnoreParenCasts();
376 if (!E)
377 return nullptr;
378 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: E))
379 return Lambda;
380 auto *CE = dyn_cast_or_null<CXXConstructExpr>(Val: E);
381 if (!CE || !CE->getNumArgs())
382 return nullptr;
383 auto *CtorArg = CE->getArg(Arg: 0)->IgnoreParenCasts();
384 if (!CtorArg)
385 return nullptr;
386 auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(Val: CtorArg);
387 if (InnerCE && InnerCE->getNumArgs())
388 CtorArg = InnerCE->getArg(Arg: 0)->IgnoreParenCasts();
389 auto updateIgnoreList = [&] {
390 ConstructToIgnore.insert(V: CE);
391 if (InnerCE)
392 ConstructToIgnore.insert(V: InnerCE);
393 };
394 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: CtorArg)) {
395 updateIgnoreList();
396 return Lambda;
397 }
398 if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(Val: CtorArg)) {
399 E = TempExpr->getSubExpr()->IgnoreParenCasts();
400 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: E)) {
401 updateIgnoreList();
402 return Lambda;
403 }
404 }
405 auto *DRE = dyn_cast<DeclRefExpr>(Val: CtorArg);
406 if (!DRE)
407 return nullptr;
408 auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl());
409 if (!VD)
410 return nullptr;
411 auto *Init = VD->getInit();
412 if (!Init)
413 return nullptr;
414 if (auto *Lambda = dyn_cast<LambdaExpr>(Val: Init)) {
415 DeclRefExprsToIgnore.insert(V: DRE);
416 updateIgnoreList();
417 return Lambda;
418 }
419 return nullptr;
420 }
421
422 void checkCalleeLambda(CallExpr *CE) {
423 auto *Callee = CE->getCallee();
424 if (!Callee)
425 return;
426 Callee = Callee->IgnoreParenCasts();
427 if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: Callee)) {
428 Callee = MTE->getSubExpr();
429 if (!Callee)
430 return;
431 Callee = Callee->IgnoreParenCasts();
432 }
433 if (auto *L = dyn_cast<LambdaExpr>(Val: Callee)) {
434 LambdasToIgnore.insert(V: L); // Calling a lambda upon creation is safe.
435 return;
436 }
437 auto *DRE = dyn_cast<DeclRefExpr>(Val: Callee->IgnoreParenCasts());
438 if (!DRE)
439 return;
440 auto *MD = dyn_cast_or_null<CXXMethodDecl>(Val: DRE->getDecl());
441 if (!MD || CE->getNumArgs() < 1)
442 return;
443 auto *Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
444 if (auto *L = dyn_cast_or_null<LambdaExpr>(Val: Arg)) {
445 LambdasToIgnore.insert(V: L); // Calling a lambda upon creation is safe.
446 return;
447 }
448 auto *ArgRef = dyn_cast<DeclRefExpr>(Val: Arg);
449 if (!ArgRef)
450 return;
451 auto *VD = dyn_cast_or_null<VarDecl>(Val: ArgRef->getDecl());
452 if (!VD)
453 return;
454 auto *Init = VD->getInit();
455 if (!Init)
456 return;
457 auto *L = dyn_cast_or_null<LambdaExpr>(Val: Init->IgnoreParenCasts());
458 if (!L)
459 return;
460 DeclRefExprsToIgnore.insert(V: ArgRef);
461 LambdasToIgnore.insert(V: L);
462 }
463
464 bool hasProtectedThis(const LambdaExpr *L) {
465 for (const LambdaCapture &OtherCapture : L->captures()) {
466 if (!OtherCapture.capturesVariable())
467 continue;
468 if (auto *ValueDecl = OtherCapture.getCapturedVar()) {
469 if (declProtectsThis(ValueDecl)) {
470 ProtectedThisDecls.insert(V: ValueDecl);
471 return true;
472 }
473 }
474 }
475 return false;
476 }
477
478 bool declProtectsThis(const ValueDecl *ValueDecl) const {
479 auto *VD = dyn_cast<VarDecl>(Val: ValueDecl);
480 if (!VD)
481 return false;
482 auto *Init = VD->getInit();
483 if (!Init)
484 return false;
485 const Expr *Arg = Init->IgnoreParenCasts();
486 do {
487 if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Val: Arg))
488 Arg = BTE->getSubExpr()->IgnoreParenCasts();
489 if (auto *CE = dyn_cast<CXXConstructExpr>(Val: Arg)) {
490 auto *Ctor = CE->getConstructor();
491 if (!Ctor)
492 return false;
493 auto ArgClsTy = dyn_cast_or_null<CXXRecordDecl>(Val: Ctor->getParent());
494 if (Checker->Model->isSafePtr(Record: ArgClsTy) && CE->getNumArgs()) {
495 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
496 continue;
497 }
498 if (auto *Type = ClsType.getTypePtrOrNull()) {
499 if (auto *CXXR = Type->getPointeeCXXRecordDecl()) {
500 if (CXXR == Ctor->getParent() && Ctor->isMoveConstructor() &&
501 CE->getNumArgs() == 1) {
502 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
503 continue;
504 }
505 }
506 }
507 return false;
508 }
509 if (auto *CE = dyn_cast<CallExpr>(Val: Arg)) {
510 if (auto *Callee = CE->getDirectCallee()) {
511 if ((isStdOrWTFMove(F: Callee) || isCtorOfSafePtr(F: Callee)) &&
512 CE->getNumArgs() == 1) {
513 Arg = CE->getArg(Arg: 0)->IgnoreParenCasts();
514 continue;
515 }
516 }
517 }
518 if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Val: Arg)) {
519 auto OpCode = OpCE->getOperator();
520 if (OpCode == OO_Star || OpCode == OO_Amp) {
521 auto *Callee = OpCE->getDirectCallee();
522 if (!Callee)
523 return false;
524 auto clsName = safeGetName(ASTNode: Callee->getParent());
525 if (!Checker->isPtrType(Name: clsName) || !OpCE->getNumArgs())
526 return false;
527 Arg = OpCE->getArg(Arg: 0)->IgnoreParenCasts();
528 continue;
529 }
530 }
531 if (auto *UO = dyn_cast<UnaryOperator>(Val: Arg)) {
532 auto OpCode = UO->getOpcode();
533 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
534 Arg = UO->getSubExpr()->IgnoreParenCasts();
535 continue;
536 }
537 }
538 break;
539 } while (Arg);
540 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: Arg)) {
541 auto *Decl = DRE->getDecl();
542 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Val: Decl)) {
543 auto kind = ImplicitParam->getParameterKind();
544 return kind == ImplicitParamKind::ObjCSelf ||
545 kind == ImplicitParamKind::CXXThis;
546 }
547 return ProtectedThisDecls.contains(V: Decl);
548 }
549 return isa<CXXThisExpr>(Val: Arg);
550 }
551 };
552
553 LocalVisitor visitor(this);
554 if (auto *RTC = Model->retainTypeChecker())
555 RTC->visitTranslationUnitDecl(TUD);
556 visitor.TraverseDecl(D: const_cast<TranslationUnitDecl *>(TUD));
557 }
558
559 void visitLambdaExpr(const LambdaExpr *L, bool shouldCheckThis,
560 const QualType T,
561 bool ignoreParamVarDecl = false) const {
562 if (BR->getSourceManager().isInSystemHeader(Loc: L->getBeginLoc()))
563 return;
564 // FIXME: This check is unsound. Destruction can happen in three places,
565 // and a capture is only safe when all three are trivial: the lambda's
566 // body, the callee that receives it, and the scope that creates it.
567 if (TFA.isTrivial(S: L->getBody()))
568 return;
569 for (auto [C, CaptureInit] :
570 llvm::zip_equal(t: L->captures(), u: L->capture_inits())) {
571 if (C.capturesVariable()) {
572 ValueDecl *CapturedVar = C.getCapturedVar();
573 if (ignoreParamVarDecl && isa<ParmVarDecl>(Val: CapturedVar))
574 continue;
575 if (auto *ImplicitParam = dyn_cast<ImplicitParamDecl>(Val: CapturedVar)) {
576 auto kind = ImplicitParam->getParameterKind();
577 if ((kind == ImplicitParamKind::ObjCSelf ||
578 kind == ImplicitParamKind::CXXThis) &&
579 !shouldCheckThis)
580 continue;
581 }
582 QualType CapturedVarQualType = CapturedVar->getType();
583 auto IsUncountedPtr = isUnsafePtr(QT: CapturedVarQualType);
584 if (C.getCaptureKind() == LCK_ByCopy &&
585 CapturedVarQualType->isReferenceType())
586 continue;
587 if (!IsUncountedPtr || !*IsUncountedPtr)
588 continue;
589 const Expr *Origin = nullptr;
590 if (Model->checksForInteriorDestruction()) {
591 if (!CaptureInit)
592 continue;
593 if (isCaptureOriginSafeToEscape(CaptureInit, Origin,
594 SinkType: CapturedVarQualType))
595 continue;
596 }
597 reportBug(Capture: C, CapturedVar, T: CapturedVarQualType, L, Origin);
598 } else if (C.capturesThis() && shouldCheckThis) {
599 if (ignoreParamVarDecl)
600 continue;
601 reportBugOnThisPtr(Capture: C, T);
602 }
603 }
604 }
605
606 bool isCaptureOriginSafeToEscape(const Expr *CaptureInit, const Expr *&Origin,
607 QualType SinkType) const {
608 return tryToFindPtrOrigin(
609 E: CaptureInit, /*StopAtFirstRefCountedObj=*/false,
610 FollowLifetimeBound: Model->checksForInteriorDestruction(),
611
612 // A smart pointer, even if safe, has shorter lifetime than an
613 // escaping lambda, so we do not treat it as a guarantee of safety.
614 /*isSafePtr=*/[](const clang::CXXRecordDecl *) { return false; },
615 /*isSafePtrType=*/[](const clang::QualType) { return false; },
616
617 // A global has longer lifetime, and can be safe.
618 /*isSafeGlobalDecl=*/
619 [&](const clang::Decl *D) {
620 return Model->isSafeDecl(D, BR->getSourceManager());
621 },
622
623 callback: [&](const clang::Expr *CaptureOrigin, bool IsSafe,
624 bool /*OriginDependsOnFullExpressionTemporary*/,
625 bool PtrIsLifetimeBoundToOrigin) {
626 if (!CaptureOrigin)
627 return true;
628 if (isa<CXXThisExpr>(Val: CaptureOrigin))
629 return true;
630 if (IsSafe)
631 return true;
632 if (Model->isSafeExpr(Origin: CaptureOrigin, PtrIsLifetimeBoundToOrigin,
633 SinkType, /*SinkMayEscape=*/true))
634 return true;
635 if (!Origin)
636 Origin = CaptureOrigin;
637 return false;
638 });
639 }
640
641 void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
642 const QualType T, const LambdaExpr *L,
643 const Expr *Origin) const {
644 assert(CapturedVar);
645
646 auto Location = Capture.getLocation();
647 if (isa<ImplicitParamDecl>(Val: CapturedVar) && !Location.isValid())
648 Location = L->getBeginLoc();
649
650 SmallString<100> Buf;
651 llvm::raw_svector_ostream Os(Buf);
652
653 if (Capture.isExplicit())
654 Os << "Captured ";
655 else
656 Os << "Implicitly captured ";
657 Os << "variable ";
658 printQuotedQualifiedName(Os, D: CapturedVar);
659
660 bool IsUnsafePtr = CapturedVar->getType() == T;
661 if (IsUnsafePtr)
662 Os << " is a ";
663 else
664 Os << " contains a ";
665 if (Model->checksForInteriorDestruction())
666 Model->describeHazard(Os, Origin, SinkType: T);
667 else
668 printPointer(Os, T: T.getTypePtrOrNull());
669
670 PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
671 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
672 BR->emitReport(R: std::move(Report));
673 }
674
675 void reportBugOnThisPtr(const LambdaCapture &Capture,
676 const QualType T) const {
677 SmallString<100> Buf;
678 llvm::raw_svector_ostream Os(Buf);
679
680 if (Capture.isExplicit()) {
681 Os << "Captured ";
682 } else {
683 Os << "Implicitly captured ";
684 }
685
686 Os << "variable 'this' is a raw pointer to " << Model->typeName();
687 if (auto *RD = T->getPointeeCXXRecordDecl()) {
688 Os << " ";
689 printQuotedQualifiedName(Os, D: RD);
690 }
691
692 PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
693 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
694 BR->emitReport(R: std::move(Report));
695 }
696
697 void printPointer(llvm::raw_svector_ostream &Os, const Type *T) const {
698 if (Model->retainTypeChecker()) {
699 // An OS object may be spelled as an id qualified by an OS_-prefixed
700 // protocol; print that protocol name.
701 if (auto *ObjCPtr = dyn_cast<ObjCObjectPointerType>(Val: T)) {
702 for (ObjCProtocolDecl *P : ObjCPtr->quals()) {
703 if (const auto *II = P->getIdentifier()) {
704 auto Name = II->getName();
705 if (Name.starts_with(Prefix: "OS_")) {
706 Os << Model->typeName() << " ";
707 printQuotedQualifiedName(Os, D: P);
708 return;
709 }
710 }
711 }
712 }
713 // Retain/OS types are frequently spelled through a typedef (e.g.
714 // CFXXXRef); print the typedef name rather than desugaring.
715 if (!isa<ObjCObjectPointerType>(Val: T) && T->getAs<TypedefType>()) {
716 auto Typedef = T->getAs<TypedefType>();
717 assert(Typedef);
718 Os << Model->typeName() << " ";
719 printQuotedQualifiedName(Os, D: Typedef->getDecl());
720 return;
721 }
722 }
723 T = T->getUnqualifiedDesugaredType();
724 bool IsPtr = isa<PointerType>(Val: T) || isa<ObjCObjectPointerType>(Val: T);
725 Os << (IsPtr ? "raw pointer" : "raw reference") << " to ";
726 Os << Model->typeName();
727
728 if (auto *RD = T->getPointeeType()->getAsRecordDecl()) {
729 Os << " ";
730 printQuotedQualifiedName(Os, D: RD);
731 } else if (auto *ObjCDecl = getObjCDeclFromObjCPtr(TypePtr: T)) {
732 Os << " ";
733 printQuotedQualifiedName(Os, D: ObjCDecl);
734 }
735 }
736};
737
738class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
739public:
740 UncountedLambdaCapturesChecker()
741 : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted variable",
742 makeRefPtrSafetyModel()) {}
743};
744
745class UncheckedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
746public:
747 UncheckedLambdaCapturesChecker()
748 : RawPtrRefLambdaCapturesChecker("Lambda capture of unchecked variable",
749 makeCheckedPtrSafetyModel()) {}
750};
751
752class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
753public:
754 UnretainedLambdaCapturesChecker()
755 : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained "
756 "variables",
757 makeRetainPtrSafetyModel()) {}
758};
759
760class UnborrowedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
761public:
762 UnborrowedLambdaCapturesChecker()
763 : RawPtrRefLambdaCapturesChecker("Lambda capture of a loan on a "
764 "CanBorrow object",
765 makeBorrowSafetyModel()) {}
766};
767
768} // namespace
769
770void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
771 Mgr.registerChecker<UncountedLambdaCapturesChecker>();
772}
773
774bool ento::shouldRegisterUncountedLambdaCapturesChecker(
775 const CheckerManager &mgr) {
776 return true;
777}
778
779void ento::registerUncheckedLambdaCapturesChecker(CheckerManager &Mgr) {
780 Mgr.registerChecker<UncheckedLambdaCapturesChecker>();
781}
782
783bool ento::shouldRegisterUncheckedLambdaCapturesChecker(
784 const CheckerManager &mgr) {
785 return true;
786}
787
788void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) {
789 Mgr.registerChecker<UnretainedLambdaCapturesChecker>();
790}
791
792bool ento::shouldRegisterUnretainedLambdaCapturesChecker(
793 const CheckerManager &mgr) {
794 return true;
795}
796
797void ento::registerUnborrowedLambdaCapturesChecker(CheckerManager &Mgr) {
798 Mgr.registerChecker<UnborrowedLambdaCapturesChecker>();
799}
800
801bool ento::shouldRegisterUnborrowedLambdaCapturesChecker(
802 const CheckerManager &mgr) {
803 return true;
804}
805