Skip to content

Commit bbef8de

Browse files
committed
[analyzer] Support determining origins in a conditional operator in WebKit checkers.
This PR adds the support for determining the origin of a pointer in a conditional operator. Because such an expression can have two distinct origins each of which needs to be visited, this PR refactors tryToFindPtrOrigin to take a callback instead of returning a pair. The callback is called for the second operand and the third operand of the conditioanl operator (i.e. E2 and E3 in E1 ? E2 : E3). Also treat nullptr and integer literal as safe pointer origins in the local variable checker.
1 parent a312dd6 commit bbef8de

File tree

6 files changed

+113
-62
lines changed

6 files changed

+113
-62
lines changed

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
namespace clang {
1818

19-
std::pair<const Expr *, bool>
20-
tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
19+
bool tryToFindPtrOrigin(
20+
const Expr *E, bool StopAtFirstRefCountedObj,
21+
std::function<bool(const clang::Expr *, bool)> callback) {
2122
while (E) {
2223
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
2324
E = tempExpr->getSubExpr();
@@ -27,12 +28,18 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
2728
E = tempExpr->getSubExpr();
2829
continue;
2930
}
31+
if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {
32+
return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,
33+
callback) &&
34+
tryToFindPtrOrigin(Expr->getFalseExpr(), StopAtFirstRefCountedObj,
35+
callback);
36+
}
3037
if (auto *cast = dyn_cast<CastExpr>(E)) {
3138
if (StopAtFirstRefCountedObj) {
3239
if (auto *ConversionFunc =
3340
dyn_cast_or_null<FunctionDecl>(cast->getConversionFunction())) {
3441
if (isCtorOfRefCounted(ConversionFunc))
35-
return {E, true};
42+
return callback(E, true);
3643
}
3744
}
3845
// FIXME: This can give false "origin" that would lead to false negatives
@@ -47,7 +54,7 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
4754
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
4855
E = memberCall->getImplicitObjectArgument();
4956
if (StopAtFirstRefCountedObj) {
50-
return {E, true};
57+
return callback(E, true);
5158
}
5259
continue;
5360
}
@@ -64,17 +71,17 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
6471
if (auto *callee = call->getDirectCallee()) {
6572
if (isCtorOfRefCounted(callee)) {
6673
if (StopAtFirstRefCountedObj)
67-
return {E, true};
74+
return callback(E, true);
6875

6976
E = call->getArg(0);
7077
continue;
7178
}
7279

7380
if (isReturnValueRefCounted(callee))
74-
return {E, true};
81+
return callback(E, true);
7582

7683
if (isSingleton(callee))
77-
return {E, true};
84+
return callback(E, true);
7885

7986
if (isPtrConversion(callee)) {
8087
E = call->getArg(0);
@@ -91,7 +98,7 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
9198
break;
9299
}
93100
// Some other expression.
94-
return {E, false};
101+
return callback(E, false);
95102
}
96103

97104
bool isASafeCallArg(const Expr *E) {

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "llvm/ADT/APInt.h"
1414
#include "llvm/Support/Casting.h"
1515

16+
#include <functional>
1617
#include <string>
1718
#include <utility>
1819

@@ -48,10 +49,12 @@ class Expr;
4849
/// represents ref-counted object during the traversal we return relevant
4950
/// sub-expression and true.
5051
///
51-
/// \returns subexpression that we traversed to and if \p
52-
/// StopAtFirstRefCountedObj is true we also return whether we stopped early.
53-
std::pair<const clang::Expr *, bool>
54-
tryToFindPtrOrigin(const clang::Expr *E, bool StopAtFirstRefCountedObj);
52+
/// Calls \p callback with the subexpression that we traversed to and if \p
53+
/// StopAtFirstRefCountedObj is true we also specify whether we stopped early.
54+
/// Returns false if any of calls to callbacks returned false. Otherwise true.
55+
bool tryToFindPtrOrigin(
56+
const clang::Expr *E, bool StopAtFirstRefCountedObj,
57+
std::function<bool(const clang::Expr *, bool)> callback);
5558

5659
/// For \p E referring to a ref-countable/-counted pointer/reference we return
5760
/// whether it's a safe call argument. Examples: function parameter or

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

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -126,25 +126,23 @@ class UncountedCallArgsChecker
126126
}
127127

128128
bool isPtrOriginSafe(const Expr *Arg) const {
129-
std::pair<const clang::Expr *, bool> ArgOrigin =
130-
tryToFindPtrOrigin(Arg, true);
131-
132-
// Temporary ref-counted object created as part of the call argument
133-
// would outlive the call.
134-
if (ArgOrigin.second)
135-
return true;
136-
137-
if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
138-
// foo(nullptr)
139-
return true;
140-
}
141-
if (isa<IntegerLiteral>(ArgOrigin.first)) {
142-
// FIXME: Check the value.
143-
// foo(NULL)
144-
return true;
145-
}
146-
147-
return isASafeCallArg(ArgOrigin.first);
129+
return tryToFindPtrOrigin(Arg, /*StopAtFirstRefCountedObj=*/true,
130+
[](const clang::Expr *ArgOrigin, bool IsSafe) {
131+
if (IsSafe)
132+
return true;
133+
if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
134+
// foo(nullptr)
135+
return true;
136+
}
137+
if (isa<IntegerLiteral>(ArgOrigin)) {
138+
// FIXME: Check the value.
139+
// foo(NULL)
140+
return true;
141+
}
142+
if (isASafeCallArg(ArgOrigin))
143+
return true;
144+
return false;
145+
});
148146
}
149147

150148
bool shouldSkipCall(const CallExpr *CE) const {

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

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -188,39 +188,50 @@ class UncountedLocalVarsChecker
188188
if (!InitExpr)
189189
return; // FIXME: later on we might warn on uninitialized vars too
190190

191-
const clang::Expr *const InitArgOrigin =
192-
tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
193-
.first;
194-
if (!InitArgOrigin)
191+
if (tryToFindPtrOrigin(
192+
InitExpr, /*StopAtFirstRefCountedObj=*/false,
193+
[&](const clang::Expr *InitArgOrigin, bool IsSafe) {
194+
if (!InitArgOrigin)
195+
return true;
196+
197+
if (isa<CXXThisExpr>(InitArgOrigin))
198+
return true;
199+
200+
if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin))
201+
return true;
202+
203+
if (isa<IntegerLiteral>(InitArgOrigin))
204+
return true;
205+
206+
if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
207+
if (auto *MaybeGuardian =
208+
dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
209+
const auto *MaybeGuardianArgType =
210+
MaybeGuardian->getType().getTypePtr();
211+
if (MaybeGuardianArgType) {
212+
const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
213+
MaybeGuardianArgType->getAsCXXRecordDecl();
214+
if (MaybeGuardianArgCXXRecord) {
215+
if (MaybeGuardian->isLocalVarDecl() &&
216+
(isRefCounted(MaybeGuardianArgCXXRecord) ||
217+
isRefcountedStringsHack(MaybeGuardian)) &&
218+
isGuardedScopeEmbeddedInGuardianScope(
219+
V, MaybeGuardian))
220+
return true;
221+
}
222+
}
223+
224+
// Parameters are guaranteed to be safe for the duration of
225+
// the call by another checker.
226+
if (isa<ParmVarDecl>(MaybeGuardian))
227+
return true;
228+
}
229+
}
230+
231+
return false;
232+
}))
195233
return;
196234

197-
if (isa<CXXThisExpr>(InitArgOrigin))
198-
return;
199-
200-
if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
201-
if (auto *MaybeGuardian =
202-
dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
203-
const auto *MaybeGuardianArgType =
204-
MaybeGuardian->getType().getTypePtr();
205-
if (MaybeGuardianArgType) {
206-
const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
207-
MaybeGuardianArgType->getAsCXXRecordDecl();
208-
if (MaybeGuardianArgCXXRecord) {
209-
if (MaybeGuardian->isLocalVarDecl() &&
210-
(isRefCounted(MaybeGuardianArgCXXRecord) ||
211-
isRefcountedStringsHack(MaybeGuardian)) &&
212-
isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
213-
return;
214-
}
215-
}
216-
217-
// Parameters are guaranteed to be safe for the duration of the call
218-
// by another checker.
219-
if (isa<ParmVarDecl>(MaybeGuardian))
220-
return;
221-
}
222-
}
223-
224235
reportBug(V);
225236
}
226237
}

clang/test/Analysis/Checkers/WebKit/call-args.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,17 @@ namespace cxx_member_operator_call {
333333
// expected-warning@-1{{Call argument for parameter 'bad' is uncounted and unsafe}}
334334
}
335335
}
336+
337+
namespace call_with_ptr_on_ref {
338+
Ref<RefCountable> provideProtected();
339+
void bar(RefCountable* bad);
340+
bool baz();
341+
void foo(bool v) {
342+
bar(v ? nullptr : provideProtected().ptr());
343+
bar(baz() ? provideProtected().ptr() : nullptr);
344+
bar(v ? provide() : provideProtected().ptr());
345+
// expected-warning@-1{{Call argument for parameter 'bad' is uncounted and unsafe}}
346+
bar(v ? provideProtected().ptr() : provide());
347+
// expected-warning@-1{{Call argument for parameter 'bad' is uncounted and unsafe}}
348+
}
349+
}

clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,21 @@ void bar() {
187187
}
188188

189189
} // namespace ignore_for_if
190+
191+
namespace conditional_op {
192+
RefCountable *provide_ref_ctnbl();
193+
bool bar();
194+
195+
void foo() {
196+
RefCountable *a = bar() ? nullptr : provide_ref_ctnbl();
197+
// expected-warning@-1{{Local variable 'a' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
198+
RefPtr<RefCountable> b = provide_ref_ctnbl();
199+
{
200+
RefCountable* c = bar() ? nullptr : b.get();
201+
c->method();
202+
RefCountable* d = bar() ? b.get() : nullptr;
203+
d->method();
204+
}
205+
}
206+
207+
} // namespace conditional_op

0 commit comments

Comments
 (0)