Skip to content

Commit a9767a1

Browse files
authored
Merge pull request #8193 from haoNoQ/static-analyzer-cherrypicks-2024-03
🍒 Static analyzer cherrypicks
2 parents 1591857 + e83c8bc commit a9767a1

File tree

7 files changed

+145
-26
lines changed

7 files changed

+145
-26
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ namespace clang {
1919
std::pair<const Expr *, bool>
2020
tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
2121
while (E) {
22+
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
23+
E = tempExpr->getSubExpr();
24+
continue;
25+
}
2226
if (auto *cast = dyn_cast<CastExpr>(E)) {
2327
if (StopAtFirstRefCountedObj) {
2428
if (auto *ConversionFunc =
@@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
6266
E = call->getArg(0);
6367
continue;
6468
}
69+
if (isReturnValueRefCounted(callee))
70+
return {E, true};
6571

6672
if (isPtrConversion(callee)) {
6773
E = call->getArg(0);

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
119119
|| FunctionName == "Identifier";
120120
}
121121

122+
bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
123+
assert(F);
124+
QualType type = F->getReturnType();
125+
while (!type.isNull()) {
126+
if (auto *elaboratedT = type->getAs<ElaboratedType>()) {
127+
type = elaboratedT->desugar();
128+
continue;
129+
}
130+
if (auto *specialT = type->getAs<TemplateSpecializationType>()) {
131+
if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) {
132+
auto name = decl->getNameAsString();
133+
return name == "Ref" || name == "RefPtr";
134+
}
135+
return false;
136+
}
137+
return false;
138+
}
139+
return false;
140+
}
141+
122142
std::optional<bool> isUncounted(const CXXRecordDecl* Class)
123143
{
124144
// Keep isRefCounted first as it's cheaper.
@@ -194,8 +214,9 @@ bool isPtrConversion(const FunctionDecl *F) {
194214
// FIXME: check # of params == 1
195215
const auto FunctionName = safeGetName(F);
196216
if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
197-
FunctionName == "dynamicDowncast"
198-
|| FunctionName == "downcast" || FunctionName == "bitwise_cast")
217+
FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
218+
FunctionName == "checkedDowncast" ||
219+
FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast")
199220
return true;
200221

201222
return false;

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ std::optional<bool> isUncountedPtr(const clang::Type* T);
5050
/// false if not.
5151
bool isCtorOfRefCounted(const clang::FunctionDecl *F);
5252

53+
/// \returns true if \p F returns a ref-counted object, false if not.
54+
bool isReturnValueRefCounted(const clang::FunctionDecl *F);
55+
5356
/// \returns true if \p M is getter of a ref-counted class, false if not.
5457
std::optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
5558

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

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ class UncountedCallArgsChecker
7171
// or std::function call operator).
7272
unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
7373

74+
if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
75+
auto *E = MemberCallExpr->getImplicitObjectArgument();
76+
QualType ArgType = MemberCallExpr->getObjectType();
77+
std::optional<bool> IsUncounted =
78+
isUncounted(ArgType->getAsCXXRecordDecl());
79+
if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
80+
reportBugOnThis(E);
81+
}
82+
7483
for (auto P = F->param_begin();
7584
// FIXME: Also check variadic function parameters.
7685
// FIXME: Also check default function arguments. Probably a different
@@ -95,32 +104,36 @@ class UncountedCallArgsChecker
95104
if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
96105
Arg = defaultArg->getExpr();
97106

98-
std::pair<const clang::Expr *, bool> ArgOrigin =
99-
tryToFindPtrOrigin(Arg, true);
100-
101-
// Temporary ref-counted object created as part of the call argument
102-
// would outlive the call.
103-
if (ArgOrigin.second)
104-
continue;
105-
106-
if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
107-
// foo(nullptr)
108-
continue;
109-
}
110-
if (isa<IntegerLiteral>(ArgOrigin.first)) {
111-
// FIXME: Check the value.
112-
// foo(NULL)
113-
continue;
114-
}
115-
116-
if (isASafeCallArg(ArgOrigin.first))
107+
if (isPtrOriginSafe(Arg))
117108
continue;
118109

119110
reportBug(Arg, *P);
120111
}
121112
}
122113
}
123114

115+
bool isPtrOriginSafe(const Expr *Arg) const {
116+
std::pair<const clang::Expr *, bool> ArgOrigin =
117+
tryToFindPtrOrigin(Arg, true);
118+
119+
// Temporary ref-counted object created as part of the call argument
120+
// would outlive the call.
121+
if (ArgOrigin.second)
122+
return true;
123+
124+
if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
125+
// foo(nullptr)
126+
return true;
127+
}
128+
if (isa<IntegerLiteral>(ArgOrigin.first)) {
129+
// FIXME: Check the value.
130+
// foo(NULL)
131+
return true;
132+
}
133+
134+
return isASafeCallArg(ArgOrigin.first);
135+
}
136+
124137
bool shouldSkipCall(const CallExpr *CE) const {
125138
if (CE->getNumArgs() == 0)
126139
return false;
@@ -162,13 +175,14 @@ class UncountedCallArgsChecker
162175

163176
auto name = safeGetName(Callee);
164177
if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
165-
name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" ||
166-
name == "is" || name == "equal" || name == "hash" ||
167-
name == "isType"
178+
name == "dynamicDowncast" || name == "downcast" ||
179+
name == "checkedDowncast" || name == "uncheckedDowncast" ||
180+
name == "bitwise_cast" || name == "is" || name == "equal" ||
181+
name == "hash" || name == "isType" ||
168182
// FIXME: Most/all of these should be implemented via attributes.
169-
|| name == "equalIgnoringASCIICase" ||
183+
name == "equalIgnoringASCIICase" ||
170184
name == "equalIgnoringASCIICaseCommon" ||
171-
name == "equalIgnoringNullity")
185+
name == "equalIgnoringNullity" || name == "toString")
172186
return true;
173187

174188
return false;
@@ -197,6 +211,19 @@ class UncountedCallArgsChecker
197211
Report->addRange(CallArg->getSourceRange());
198212
BR->emitReport(std::move(Report));
199213
}
214+
215+
void reportBugOnThis(const Expr *CallArg) const {
216+
assert(CallArg);
217+
218+
const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
219+
220+
PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
221+
auto Report = std::make_unique<BasicBugReport>(
222+
Bug, "Call argument for 'this' parameter is uncounted and unsafe.",
223+
BSLoc);
224+
Report->addRange(CallArg->getSourceRange());
225+
BR->emitReport(std::move(Report));
226+
}
200227
};
201228
} // namespace
202229

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
2+
// expected-no-diagnostics
3+
4+
#include "mock-types.h"
5+
6+
class RefCounted {
7+
public:
8+
void ref();
9+
void deref();
10+
};
11+
12+
class Object {
13+
public:
14+
void someFunction(RefCounted&);
15+
};
16+
17+
RefPtr<Object> object();
18+
RefPtr<RefCounted> protectedTargetObject();
19+
20+
void testFunction() {
21+
if (RefPtr obj = object())
22+
obj->someFunction(*protectedTargetObject());
23+
}

clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp renamed to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,34 @@ class OtherObject {
2323
Derived* obj();
2424
};
2525

26+
class String {
27+
};
28+
2629
template<typename Target, typename Source>
2730
inline Target* dynamicDowncast(Source* source)
2831
{
2932
return static_cast<Target*>(source);
3033
}
3134

35+
template<typename Target, typename Source>
36+
inline Target* checkedDowncast(Source* source)
37+
{
38+
return static_cast<Target*>(source);
39+
}
40+
41+
template<typename Target, typename Source>
42+
inline Target* uncheckedDowncast(Source* source)
43+
{
44+
return static_cast<Target*>(source);
45+
}
46+
47+
template<typename... Types>
48+
String toString(const Types&... values);
49+
3250
void foo(OtherObject* other)
3351
{
3452
dynamicDowncast<SubDerived>(other->obj());
53+
checkedDowncast<SubDerived>(other->obj());
54+
uncheckedDowncast<SubDerived>(other->obj());
55+
toString(other->obj());
3556
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
2+
3+
#include "mock-types.h"
4+
5+
class RefCounted {
6+
public:
7+
void ref() const;
8+
void deref() const;
9+
void someFunction();
10+
};
11+
12+
RefCounted* refCountedObj();
13+
14+
void test()
15+
{
16+
refCountedObj()->someFunction();
17+
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
18+
}

0 commit comments

Comments
 (0)