Skip to content

Commit 5c03226

Browse files
committed
[webkit.UncountedLambdaCapturesChecker] Detect protectedThis pattern.
In WebKit, we often capture this as Ref or RefPtr in addition to this itself so that the object lives as long as a capturing lambda stays alive. Detect this pattern and treat it as safe. This PR also makes the check for a lambda being passed as a function argument more robust by handling CXXBindTemporaryExpr, CXXConstructExpr, and DeclRefExpr referring to the lambda.
1 parent dd8d85d commit 5c03226

File tree

2 files changed

+113
-13
lines changed

2 files changed

+113
-13
lines changed

clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,47 @@ class UncountedLambdaCapturesChecker
9696
return true;
9797
auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
9898
if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) {
99-
if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
100-
Checker->visitLambdaExpr(L, shouldCheckThis());
101-
}
99+
if (auto *Lambda = findLambdaInArg(Arg))
100+
Checker->visitLambdaExpr(Lambda, shouldCheckThis());
102101
}
103102
++ArgIndex;
104103
}
105104
}
106105
return true;
107106
}
108107

108+
LambdaExpr* findLambdaInArg(Expr* E) {
109+
if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(E))
110+
return Lambda;
111+
auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E);
112+
if (!TempExpr)
113+
return nullptr;
114+
E = TempExpr->getSubExpr()->IgnoreParenCasts();
115+
if (!E)
116+
return nullptr;
117+
if (auto *Lambda = dyn_cast<LambdaExpr>(E))
118+
return Lambda;
119+
auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
120+
if (!CE || !CE->getNumArgs())
121+
return nullptr;
122+
auto *CtorArg = CE->getArg(0)->IgnoreParenCasts();
123+
if (!CtorArg)
124+
return nullptr;
125+
if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg))
126+
return Lambda;
127+
auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);
128+
if (!DRE)
129+
return nullptr;
130+
auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());
131+
if (!VD)
132+
return nullptr;
133+
auto* Init = VD->getInit();
134+
if (!Init)
135+
return nullptr;
136+
TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts());
137+
return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr());
138+
}
139+
109140
void checkCalleeLambda(CallExpr *CE) {
110141
auto *Callee = CE->getCallee();
111142
if (!Callee)
@@ -155,11 +186,51 @@ class UncountedLambdaCapturesChecker
155186
} else if (C.capturesThis() && shouldCheckThis) {
156187
if (ignoreParamVarDecl) // this is always a parameter to this function.
157188
continue;
158-
reportBugOnThisPtr(C);
189+
bool hasProtectThis = false;
190+
for (const LambdaCapture &OtherCapture : L->captures()) {
191+
if (auto *ValueDecl = OtherCapture.getCapturedVar()) {
192+
if (protectThis(ValueDecl)) {
193+
hasProtectThis = true;
194+
break;
195+
}
196+
}
197+
}
198+
if (!hasProtectThis)
199+
reportBugOnThisPtr(C);
159200
}
160201
}
161202
}
162203

204+
bool protectThis(const ValueDecl *ValueDecl) const {
205+
auto* VD = dyn_cast<VarDecl>(ValueDecl);
206+
if (!VD)
207+
return false;
208+
auto *Init = VD->getInit()->IgnoreParenCasts();
209+
if (!Init)
210+
return false;
211+
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Init);
212+
if (!BTE)
213+
return false;
214+
auto *CE = dyn_cast_or_null<CXXConstructExpr>(BTE->getSubExpr());
215+
if (!CE)
216+
return false;
217+
auto* Ctor = CE->getConstructor();
218+
if (!Ctor)
219+
return false;
220+
auto clsName = safeGetName(Ctor->getParent());
221+
if (!isRefType(clsName) || !CE->getNumArgs())
222+
return false;
223+
auto *Arg = CE->getArg(0)->IgnoreParenCasts();
224+
while (auto *UO = dyn_cast<UnaryOperator>(Arg)) {
225+
auto OpCode = UO->getOpcode();
226+
if (OpCode == UO_Deref || OpCode == UO_AddrOf)
227+
Arg = UO->getSubExpr();
228+
else
229+
break;
230+
}
231+
return isa<CXXThisExpr>(Arg);
232+
}
233+
163234
void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
164235
const QualType T) const {
165236
assert(CapturedVar);

clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -verify %s
22

3+
#include "mock-types.h"
4+
35
struct A {
46
static void b();
57
};
68

7-
struct RefCountable {
8-
void ref() {}
9-
void deref() {}
10-
void method();
11-
void constMethod() const;
12-
int trivial() { return 123; }
13-
RefCountable* next();
14-
};
15-
169
RefCountable* make_obj();
1710

1811
void someFunction();
@@ -151,6 +144,42 @@ struct RefCountableWithLambdaCapturingThis {
151144
};
152145
call(lambda);
153146
}
147+
148+
void method_captures_this_unsafe_capture_local_var_explicitly() {
149+
RefCountable* x = make_obj();
150+
call([this, protectedThis = RefPtr { this }, x]() {
151+
// expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
152+
nonTrivial();
153+
x->method();
154+
});
155+
}
156+
157+
void method_captures_this_unsafe_capture_local_var_explicitly_with_deref() {
158+
RefCountable* x = make_obj();
159+
call([this, protectedThis = Ref { *this }, x]() {
160+
// expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
161+
nonTrivial();
162+
x->method();
163+
});
164+
}
165+
166+
void method_captures_this_unsafe_local_var_via_vardecl() {
167+
RefCountable* x = make_obj();
168+
auto lambda = [this, protectedThis = Ref { *this }, x]() {
169+
// expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
170+
nonTrivial();
171+
x->method();
172+
};
173+
call(lambda);
174+
}
175+
176+
void method_captures_this_with_guardian() {
177+
auto lambda = [this, protectedThis = Ref { *this }]() {
178+
nonTrivial();
179+
};
180+
call(lambda);
181+
}
182+
154183
};
155184

156185
struct NonRefCountableWithLambdaCapturingThis {

0 commit comments

Comments
 (0)