diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp index 599c2179db0f0..201b8770adcd7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp @@ -96,9 +96,8 @@ class UncountedLambdaCapturesChecker return true; auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); if (!Param->hasAttr() && !TreatAllArgsAsNoEscape) { - if (auto *L = dyn_cast_or_null(Arg)) { - Checker->visitLambdaExpr(L, shouldCheckThis()); - } + if (auto *Lambda = findLambdaInArg(Arg)) + Checker->visitLambdaExpr(Lambda, shouldCheckThis()); } ++ArgIndex; } @@ -106,6 +105,38 @@ class UncountedLambdaCapturesChecker return true; } + LambdaExpr *findLambdaInArg(Expr *E) { + if (auto *Lambda = dyn_cast_or_null(E)) + return Lambda; + auto *TempExpr = dyn_cast_or_null(E); + if (!TempExpr) + return nullptr; + E = TempExpr->getSubExpr()->IgnoreParenCasts(); + if (!E) + return nullptr; + if (auto *Lambda = dyn_cast(E)) + return Lambda; + auto *CE = dyn_cast_or_null(E); + if (!CE || !CE->getNumArgs()) + return nullptr; + auto *CtorArg = CE->getArg(0)->IgnoreParenCasts(); + if (!CtorArg) + return nullptr; + if (auto *Lambda = dyn_cast(CtorArg)) + return Lambda; + auto *DRE = dyn_cast(CtorArg); + if (!DRE) + return nullptr; + auto *VD = dyn_cast_or_null(DRE->getDecl()); + if (!VD) + return nullptr; + auto *Init = VD->getInit(); + if (!Init) + return nullptr; + TempExpr = dyn_cast(Init->IgnoreParenCasts()); + return dyn_cast_or_null(TempExpr->getSubExpr()); + } + void checkCalleeLambda(CallExpr *CE) { auto *Callee = CE->getCallee(); if (!Callee) @@ -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(ValueDecl); + if (!VD) + return false; + auto *Init = VD->getInit()->IgnoreParenCasts(); + if (!Init) + return false; + auto *BTE = dyn_cast(Init); + if (!BTE) + return false; + auto *CE = dyn_cast_or_null(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(Arg)) { + auto OpCode = UO->getOpcode(); + if (OpCode == UO_Deref || OpCode == UO_AddrOf) + Arg = UO->getSubExpr(); + else + break; + } + return isa(Arg); + } + void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, const QualType T) const { assert(CapturedVar); diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp index 65eee9d49106d..106fa02f4a89e 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp @@ -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(); @@ -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 {