Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,47 @@ class UncountedLambdaCapturesChecker
return true;
auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) {
if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) {
Checker->visitLambdaExpr(L, shouldCheckThis());
}
if (auto *Lambda = findLambdaInArg(Arg))
Checker->visitLambdaExpr(Lambda, shouldCheckThis());
}
++ArgIndex;
}
}
return true;
}

LambdaExpr *findLambdaInArg(Expr *E) {
if (auto *Lambda = dyn_cast_or_null<LambdaExpr>(E))
return Lambda;
auto *TempExpr = dyn_cast_or_null<CXXBindTemporaryExpr>(E);
if (!TempExpr)
return nullptr;
E = TempExpr->getSubExpr()->IgnoreParenCasts();
if (!E)
return nullptr;
if (auto *Lambda = dyn_cast<LambdaExpr>(E))
return Lambda;
auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
if (!CE || !CE->getNumArgs())
return nullptr;
auto *CtorArg = CE->getArg(0)->IgnoreParenCasts();
if (!CtorArg)
return nullptr;
if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg))
return Lambda;
auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);
if (!DRE)
return nullptr;
auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl());
if (!VD)
return nullptr;
auto *Init = VD->getInit();
if (!Init)
return nullptr;
TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts());
return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr());
}

void checkCalleeLambda(CallExpr *CE) {
auto *Callee = CE->getCallee();
if (!Callee)
Expand Down Expand Up @@ -155,11 +186,51 @@ class UncountedLambdaCapturesChecker
} else if (C.capturesThis() && shouldCheckThis) {
if (ignoreParamVarDecl) // this is always a parameter to this function.
continue;
reportBugOnThisPtr(C);
bool hasProtectThis = false;
for (const LambdaCapture &OtherCapture : L->captures()) {
if (auto *ValueDecl = OtherCapture.getCapturedVar()) {
if (protectThis(ValueDecl)) {
hasProtectThis = true;
break;
}
}
}
if (!hasProtectThis)
reportBugOnThisPtr(C);
}
}
}

bool protectThis(const ValueDecl *ValueDecl) const {
auto *VD = dyn_cast<VarDecl>(ValueDecl);
if (!VD)
return false;
auto *Init = VD->getInit()->IgnoreParenCasts();
if (!Init)
return false;
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Init);
if (!BTE)
return false;
auto *CE = dyn_cast_or_null<CXXConstructExpr>(BTE->getSubExpr());
if (!CE)
return false;
auto *Ctor = CE->getConstructor();
if (!Ctor)
return false;
auto clsName = safeGetName(Ctor->getParent());
if (!isRefType(clsName) || !CE->getNumArgs())
return false;
auto *Arg = CE->getArg(0)->IgnoreParenCasts();
while (auto *UO = dyn_cast<UnaryOperator>(Arg)) {
auto OpCode = UO->getOpcode();
if (OpCode == UO_Deref || OpCode == UO_AddrOf)
Arg = UO->getSubExpr();
else
break;
}
return isa<CXXThisExpr>(Arg);
}

void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
const QualType T) const {
assert(CapturedVar);
Expand Down
47 changes: 38 additions & 9 deletions clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.UncountedLambdaCapturesChecker -verify %s

#include "mock-types.h"

struct A {
static void b();
};

struct RefCountable {
void ref() {}
void deref() {}
void method();
void constMethod() const;
int trivial() { return 123; }
RefCountable* next();
};

RefCountable* make_obj();

void someFunction();
Expand Down Expand Up @@ -151,6 +144,42 @@ struct RefCountableWithLambdaCapturingThis {
};
call(lambda);
}

void method_captures_this_unsafe_capture_local_var_explicitly() {
RefCountable* x = make_obj();
call([this, protectedThis = RefPtr { this }, x]() {
// expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
nonTrivial();
x->method();
});
}

void method_captures_this_unsafe_capture_local_var_explicitly_with_deref() {
RefCountable* x = make_obj();
call([this, protectedThis = Ref { *this }, x]() {
// expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
nonTrivial();
x->method();
});
}

void method_captures_this_unsafe_local_var_via_vardecl() {
RefCountable* x = make_obj();
auto lambda = [this, protectedThis = Ref { *this }, x]() {
// expected-warning@-1{{Captured raw-pointer 'x' to ref-counted type or CheckedPtr-capable type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
nonTrivial();
x->method();
};
call(lambda);
}

void method_captures_this_with_guardian() {
auto lambda = [this, protectedThis = Ref { *this }]() {
nonTrivial();
};
call(lambda);
}

};

struct NonRefCountableWithLambdaCapturingThis {
Expand Down
Loading