Skip to content

Commit 6be567b

Browse files
authored
[Webkit Checkers] Treat const member variables as a safe origin (#115594)
Treat const Ref, RefPtr, CheckedRef, CheckedPtr member variables as safe pointer origin in WebKit's local variable and call arguments checkers.
1 parent 4f2651c commit 6be567b

File tree

10 files changed

+404
-22
lines changed

10 files changed

+404
-22
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,31 @@ bool isASafeCallArg(const Expr *E) {
142142
return true;
143143
}
144144
}
145+
if (isConstOwnerPtrMemberExpr(E))
146+
return true;
145147

146148
// TODO: checker for method calls on non-refcounted objects
147149
return isa<CXXThisExpr>(E);
148150
}
149151

152+
bool isConstOwnerPtrMemberExpr(const clang::Expr *E) {
153+
if (auto *MCE = dyn_cast<CXXMemberCallExpr>(E)) {
154+
if (auto *Callee = MCE->getDirectCallee()) {
155+
auto Name = safeGetName(Callee);
156+
if (Name == "get" || Name == "ptr") {
157+
auto *ThisArg = MCE->getImplicitObjectArgument();
158+
E = ThisArg;
159+
}
160+
}
161+
}
162+
auto *ME = dyn_cast<MemberExpr>(E);
163+
if (!ME)
164+
return false;
165+
auto *D = ME->getMemberDecl();
166+
if (!D)
167+
return false;
168+
auto T = D->getType();
169+
return isOwnerPtrType(T) && T.isConstQualified();
170+
}
171+
150172
} // namespace clang

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ bool tryToFindPtrOrigin(
6464
/// \returns Whether \p E is a safe call arugment.
6565
bool isASafeCallArg(const clang::Expr *E);
6666

67+
/// \returns true if E is a MemberExpr accessing a const smart pointer type.
68+
bool isConstOwnerPtrMemberExpr(const clang::Expr *E);
69+
6770
/// \returns name of AST node or empty string.
6871
template <typename T> std::string safeGetName(const T *ASTNode) {
6972
const auto *const ND = llvm::dyn_cast_or_null<clang::NamedDecl>(ASTNode);

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,25 +145,37 @@ bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
145145
return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F);
146146
}
147147

148-
bool isSafePtrType(const clang::QualType T) {
148+
template <typename Predicate>
149+
static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
149150
QualType type = T;
150151
while (!type.isNull()) {
151152
if (auto *elaboratedT = type->getAs<ElaboratedType>()) {
152153
type = elaboratedT->desugar();
153154
continue;
154155
}
155-
if (auto *specialT = type->getAs<TemplateSpecializationType>()) {
156-
if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) {
157-
auto name = decl->getNameAsString();
158-
return isRefType(name) || isCheckedPtr(name);
159-
}
156+
auto *SpecialT = type->getAs<TemplateSpecializationType>();
157+
if (!SpecialT)
160158
return false;
161-
}
162-
return false;
159+
auto *Decl = SpecialT->getTemplateName().getAsTemplateDecl();
160+
if (!Decl)
161+
return false;
162+
return Pred(Decl->getNameAsString());
163163
}
164164
return false;
165165
}
166166

167+
bool isSafePtrType(const clang::QualType T) {
168+
return isPtrOfType(
169+
T, [](auto Name) { return isRefType(Name) || isCheckedPtr(Name); });
170+
}
171+
172+
bool isOwnerPtrType(const clang::QualType T) {
173+
return isPtrOfType(T, [](auto Name) {
174+
return isRefType(Name) || isCheckedPtr(Name) || Name == "unique_ptr" ||
175+
Name == "UniqueRef" || Name == "LazyUniqueRef";
176+
});
177+
}
178+
167179
std::optional<bool> isUncounted(const QualType T) {
168180
if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(T)) {
169181
if (auto *Decl = Subst->getAssociatedDecl()) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ std::optional<bool> isUnsafePtr(const QualType T);
8383
/// variant, false if not.
8484
bool isSafePtrType(const clang::QualType T);
8585

86+
/// \returns true if \p T is a RefPtr, Ref, CheckedPtr, CheckedRef, or
87+
/// unique_ptr, false if not.
88+
bool isOwnerPtrType(const clang::QualType T);
89+
8690
/// \returns true if \p F creates ref-countable object from uncounted parameter,
8791
/// false if not.
8892
bool isCtorOfRefCounted(const clang::FunctionDecl *F);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,9 @@ class RawPtrRefLocalVarsChecker
281281
if (isa<IntegerLiteral>(InitArgOrigin))
282282
return true;
283283

284+
if (isConstOwnerPtrMemberExpr(InitArgOrigin))
285+
return true;
286+
284287
if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
285288
if (auto *MaybeGuardian =
286289
dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncheckedCallArgsChecker -verify %s
2+
3+
#include "mock-types.h"
4+
5+
namespace call_args_const_checkedptr_member {
6+
7+
class Foo {
8+
public:
9+
Foo();
10+
void bar();
11+
12+
private:
13+
const CheckedPtr<CheckedObj> m_obj1;
14+
CheckedPtr<CheckedObj> m_obj2;
15+
};
16+
17+
void Foo::bar() {
18+
m_obj1->method();
19+
m_obj2->method();
20+
// expected-warning@-1{{Call argument for 'this' parameter is unchecked and unsafe}}
21+
}
22+
23+
} // namespace call_args_const_checkedptr_member
24+
25+
namespace call_args_const_checkedref_member {
26+
27+
class Foo {
28+
public:
29+
Foo();
30+
void bar();
31+
32+
private:
33+
const CheckedRef<CheckedObj> m_obj1;
34+
CheckedRef<CheckedObj> m_obj2;
35+
};
36+
37+
void Foo::bar() {
38+
m_obj1->method();
39+
m_obj2->method();
40+
// expected-warning@-1{{Call argument for 'this' parameter is unchecked and unsafe}}
41+
}
42+
43+
} // namespace call_args_const_checkedref_member
44+
45+
namespace call_args_const_unique_ptr {
46+
47+
class Foo {
48+
public:
49+
Foo();
50+
void bar();
51+
52+
private:
53+
const std::unique_ptr<CheckedObj> m_obj1;
54+
std::unique_ptr<CheckedObj> m_obj2;
55+
};
56+
57+
void Foo::bar() {
58+
m_obj1->method();
59+
m_obj2->method();
60+
// expected-warning@-1{{Call argument for 'this' parameter is unchecked and unsafe}}
61+
}
62+
63+
} // namespace call_args_const_unique_ptr
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
2+
3+
#include "mock-types.h"
4+
5+
namespace std {
6+
}
7+
8+
namespace call_args_const_refptr_member {
9+
10+
class Foo {
11+
public:
12+
Foo();
13+
void bar();
14+
15+
private:
16+
const RefPtr<RefCountable> m_obj1;
17+
RefPtr<RefCountable> m_obj2;
18+
};
19+
20+
void Foo::bar() {
21+
m_obj1->method();
22+
m_obj2->method();
23+
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
24+
}
25+
26+
} // namespace call_args_const_refptr_member
27+
28+
namespace call_args_const_ref_member {
29+
30+
class Foo {
31+
public:
32+
Foo();
33+
void bar();
34+
35+
private:
36+
const Ref<RefCountable> m_obj1;
37+
Ref<RefCountable> m_obj2;
38+
};
39+
40+
void Foo::bar() {
41+
m_obj1->method();
42+
m_obj2->method();
43+
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
44+
}
45+
46+
} // namespace call_args_const_ref_member
47+
48+
namespace call_args_const_unique_ptr {
49+
50+
class Foo {
51+
public:
52+
Foo();
53+
void bar();
54+
55+
private:
56+
const std::unique_ptr<RefCountable> m_obj1;
57+
std::unique_ptr<RefCountable> m_obj2;
58+
};
59+
60+
void Foo::bar() {
61+
m_obj1->method();
62+
m_obj2->method();
63+
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
64+
}
65+
66+
} // namespace call_args_const_unique_ptr
67+
68+
namespace call_args_const_unique_ref {
69+
70+
class Foo {
71+
public:
72+
Foo();
73+
void bar();
74+
75+
private:
76+
const UniqueRef<RefCountable> m_obj1;
77+
UniqueRef<RefCountable> m_obj2;
78+
};
79+
80+
void Foo::bar() {
81+
m_obj1->method();
82+
m_obj2->method();
83+
// expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}}
84+
}
85+
86+
} // namespace call_args_const_unique_ref
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncheckedLocalVarsChecker -verify %s
2+
3+
#include "mock-types.h"
4+
#include "mock-system-header.h"
5+
6+
void someFunction();
7+
8+
namespace local_vars_const_checkedptr_member {
9+
10+
class Foo {
11+
public:
12+
Foo();
13+
void bar();
14+
15+
private:
16+
const CheckedPtr<CheckedObj> m_obj1;
17+
CheckedPtr<CheckedObj> m_obj2;
18+
};
19+
20+
void Foo::bar() {
21+
auto* obj1 = m_obj1.get();
22+
obj1->method();
23+
auto* obj2 = m_obj2.get();
24+
// expected-warning@-1{{Local variable 'obj2' is unchecked and unsafe [alpha.webkit.UncheckedLocalVarsChecker]}}
25+
obj2->method();
26+
}
27+
28+
} // namespace local_vars_const_checkedptr_member
29+
30+
namespace local_vars_const_checkedref_member {
31+
32+
class Foo {
33+
public:
34+
Foo();
35+
void bar();
36+
37+
private:
38+
const CheckedRef<CheckedObj> m_obj1;
39+
CheckedRef<CheckedObj> m_obj2;
40+
};
41+
42+
void Foo::bar() {
43+
auto& obj1 = m_obj1.get();
44+
obj1.method();
45+
auto& obj2 = m_obj2.get();
46+
// expected-warning@-1{{Local variable 'obj2' is unchecked and unsafe [alpha.webkit.UncheckedLocalVarsChecker]}}
47+
obj2.method();
48+
}
49+
50+
} // namespace local_vars_const_ref_member
51+
52+
namespace call_args_const_unique_ptr {
53+
54+
class Foo {
55+
public:
56+
Foo();
57+
void bar();
58+
59+
private:
60+
const std::unique_ptr<CheckedObj> m_obj1;
61+
std::unique_ptr<CheckedObj> m_obj2;
62+
};
63+
64+
void Foo::bar() {
65+
auto* obj1 = m_obj1.get();
66+
obj1->method();
67+
auto* obj2 = m_obj2.get();
68+
// expected-warning@-1{{Local variable 'obj2' is unchecked and unsafe [alpha.webkit.UncheckedLocalVarsChecker]}}
69+
obj2->method();
70+
}
71+
72+
} // namespace call_args_const_unique_ptr

0 commit comments

Comments
 (0)