clang 24.0.0git
MemoryUnsafeCastChecker.cpp
Go to the documentation of this file.
1//=======- MemoryUnsafeCastChecker.cpp -------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines MemoryUnsafeCast checker, which checks for casts from a
10// base type to a derived type.
11//===----------------------------------------------------------------------===//
12
19
20using namespace clang;
21using namespace ento;
22using namespace ast_matchers;
23
24namespace {
25static constexpr const char *const BaseNode = "BaseNode";
26static constexpr const char *const DerivedNode = "DerivedNode";
27static constexpr const char *const FromCastNode = "FromCast";
28static constexpr const char *const ToCastNode = "ToCast";
29static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";
30
31class MemoryUnsafeCastChecker : public Checker<check::ASTCodeBody> {
32 BugType BT{this, "Unsafe cast", "WebKit coding guidelines"};
33
34public:
35 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
36 BugReporter &BR) const;
37};
38} // end namespace
39
40static void emitDiagnostics(const BoundNodes &Nodes, BugReporter &BR,
42 const MemoryUnsafeCastChecker *Checker,
43 const BugType &BT) {
44 const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
45 const NamedDecl *Base = Nodes.getNodeAs<NamedDecl>(BaseNode);
46 const NamedDecl *Derived = Nodes.getNodeAs<NamedDecl>(DerivedNode);
47 assert(CE && Base && Derived);
48
49 std::string Diagnostics;
50 llvm::raw_string_ostream OS(Diagnostics);
51 OS << "Unsafe cast from base type '" << Base->getNameAsString()
52 << "' to derived type '" << Derived->getNameAsString() << "'";
53 PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
54 BR.getSourceManager());
55 auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
56 Report->addRange(CE->getSourceRange());
57 Report->setDeclWithIssue(ADC->getDecl());
58 BR.emitReport(std::move(Report));
59}
60
61static void emitDiagnosticsUnrelated(const BoundNodes &Nodes, BugReporter &BR,
63 const MemoryUnsafeCastChecker *Checker,
64 const BugType &BT) {
65 const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
66 const NamedDecl *FromCast = Nodes.getNodeAs<NamedDecl>(FromCastNode);
67 const NamedDecl *ToCast = Nodes.getNodeAs<NamedDecl>(ToCastNode);
68 assert(CE && FromCast && ToCast);
69
70 std::string Diagnostics;
71 llvm::raw_string_ostream OS(Diagnostics);
72 OS << "Unsafe cast from type '" << FromCast->getNameAsString()
73 << "' to an unrelated type '" << ToCast->getNameAsString() << "'";
74 PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
75 BR.getSourceManager());
76 auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
77 Report->addRange(CE->getSourceRange());
78 Report->setDeclWithIssue(ADC->getDecl());
79 BR.emitReport(std::move(Report));
80}
81
82static void emitDiagnosticsIdArg(const BoundNodes &Nodes, BugReporter &BR,
84 const MemoryUnsafeCastChecker *Checker,
85 const BugType &BT) {
86 const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
87 const NamedDecl *Derived = Nodes.getNodeAs<NamedDecl>(DerivedNode);
88 assert(CE && Derived);
89
90 std::string Diagnostics;
91 llvm::raw_string_ostream OS(Diagnostics);
92 OS << "Unsafe implicit cast from 'id' to specific type '"
93 << Derived->getNameAsString() << "'";
94 PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
95 BR.getSourceManager());
96 auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
97 Report->addRange(CE->getSourceRange());
98 Report->setDeclWithIssue(ADC->getDecl());
99 BR.emitReport(std::move(Report));
100}
101
102namespace {
103using BoundNodesMap = ::clang::ast_matchers::internal::BoundNodesMap;
104
105// Matches the plain `id` type.
106AST_MATCHER(QualType, isObjCIdType) { return Node->isObjCIdType(); }
107
108// Like equalsBoundNode, but compares canonical declarations so that different
109// redeclarations of the same class (e.g. a forward declaration and the
110// definition) are treated as the same declaration.
111AST_MATCHER_P(Decl, hasSameCanonicalDeclAsBoundNode, std::string, ID) {
112 return Builder->removeBindings([&](const BoundNodesMap &Nodes) {
113 const auto *Bound = Nodes.getNodeAs<Decl>(ID);
114 return !Bound || Bound->getCanonicalDecl() != Node.getCanonicalDecl();
115 });
116}
117
118// Matches a cast whose previously-bound BaseID node is a class template
119// specialization and whose previously-bound DerivedID node is one of that
120// specialization's type template arguments, i.e. the CRTP pattern
121// `class Derived : Base<Derived>`.
122AST_MATCHER_P2(Expr, isCRTPCast, std::string, BaseID, std::string, DerivedID) {
123 return Builder->removeBindings([this](const BoundNodesMap &Nodes) {
124 const auto *Base = Nodes.getNodeAs<CXXRecordDecl>(this->BaseID);
125 const auto *Derived = Nodes.getNodeAs<CXXRecordDecl>(this->DerivedID);
126 const auto *CTSD =
127 Base ? dyn_cast<ClassTemplateSpecializationDecl>(Base) : nullptr;
128 if (!CTSD || !Derived)
129 return true;
130 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
131 if (Arg.getKind() != TemplateArgument::Type)
132 continue;
133 QualType ArgType = Arg.getAsType();
134 if (ArgType.isNull())
135 continue;
136 const auto *ArgDecl = ArgType->getAsCXXRecordDecl();
137 if (ArgDecl && ArgDecl->getCanonicalDecl() == Derived->getCanonicalDecl())
138 return false;
139 }
140 return true;
141 });
142}
143} // end anonymous namespace
144
145static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
146 return hasType(pointerType(pointee(hasDeclaration(DeclM))));
147}
148
149// Matches `this` or `*this`, but not member accesses like `this->m_field`.
150static decltype(auto) isThisOrDerefThis() {
151 return ignoringParenImpCasts(anyOf(
152 cxxThisExpr(),
153 unaryOperator(hasOperatorName("*"),
154 hasUnaryOperand(ignoringParenImpCasts(cxxThisExpr())))));
155}
156
157void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D,
158 AnalysisManager &AM,
159 BugReporter &BR) const {
160
161 AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
162
163 // Match downcasts from base type to derived type and warn
164 auto MatchExprPtr = allOf(
165 hasSourceExpression(hasTypePointingTo(cxxRecordDecl().bind(BaseNode))),
168 isDerivedFrom(hasSameCanonicalDeclAsBoundNode(BaseNode)))
169 .bind(DerivedNode)),
171 allOf(hasSourceExpression(cxxThisExpr()),
172 isCRTPCast(BaseNode, DerivedNode)))));
173 auto MatchExprPtrObjC = allOf(
174 hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
175 pointee(hasDeclaration(objcInterfaceDecl().bind(BaseNode))))))),
176 ignoringImpCasts(hasType(objcObjectPointerType(pointee(hasDeclaration(
178 isDerivedFrom(hasSameCanonicalDeclAsBoundNode(BaseNode)))
179 .bind(DerivedNode)))))));
180 auto MatchExprRefTypeDef = allOf(
181 hasSourceExpression(hasType(hasUnqualifiedDesugaredType(
182 recordType(hasDeclaration(decl(cxxRecordDecl().bind(BaseNode))))))),
183 hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
185 isDerivedFrom(hasSameCanonicalDeclAsBoundNode(BaseNode)))
186 .bind(DerivedNode)))))),
188 allOf(hasSourceExpression(isThisOrDerefThis()),
189 isCRTPCast(BaseNode, DerivedNode)))));
190 auto MatchExprPtrVoidCast =
191 allOf(anyOf(hasSourceExpression(explicitCastExpr(
192 hasType(pointerType(pointee(voidType()))),
193 hasSourceExpression(ignoringImpCasts(
194 hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))),
195 hasSourceExpression(callExpr(
196 hasType(pointerType(pointee(voidType()))),
197 hasAnyArgument(ignoringImpCasts(hasTypePointingTo(
198 cxxRecordDecl().bind(BaseNode))))))),
201 isDerivedFrom(hasSameCanonicalDeclAsBoundNode(BaseNode)))
202 .bind(DerivedNode)));
203
204 auto ExplicitCast =
205 explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef,
206 MatchExprPtrObjC, MatchExprPtrVoidCast))
207 .bind(WarnRecordDecl);
208 auto Cast = stmt(ExplicitCast);
209
210 auto Matches =
211 match(stmt(forEachDescendant(Cast)), *D->getBody(), AM.getASTContext());
212 for (BoundNodes Match : Matches)
213 emitDiagnostics(Match, BR, ADC, this, BT);
214
215 // Match calls returning derived type where an argument is a void pointer.
216 auto VoidPtrCast =
217 castExpr(hasType(pointerType(pointee(voidType()))),
218 hasSourceExpression(ignoringImpCasts(
219 hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))
220 .bind(WarnRecordDecl);
221 auto MatchCallPtrVoidArgCast = callExpr(
222 hasAnyArgument(anyOf(VoidPtrCast,
223 explicitCastExpr(hasSourceExpression(VoidPtrCast)))),
226 isDerivedFrom(hasSameCanonicalDeclAsBoundNode(BaseNode)))
227 .bind(DerivedNode)));
228 auto CallArgCast = stmt(MatchCallPtrVoidArgCast);
229 auto MatchesCallArgCast = match(stmt(forEachDescendant(CallArgCast)),
230 *D->getBody(), AM.getASTContext());
231 for (BoundNodes Match : MatchesCallArgCast)
232 emitDiagnostics(Match, BR, ADC, this, BT);
233
234 // Match casts between unrelated types and warn
235 auto MatchExprPtrUnrelatedTypes = allOf(
236 hasSourceExpression(
237 hasTypePointingTo(cxxRecordDecl().bind(FromCastNode))),
238 hasTypePointingTo(cxxRecordDecl().bind(ToCastNode)),
239 unless(anyOf(hasTypePointingTo(cxxRecordDecl(isSameOrDerivedFrom(
240 hasSameCanonicalDeclAsBoundNode(FromCastNode)))),
241 hasSourceExpression(
242 hasTypePointingTo(cxxRecordDecl(isSameOrDerivedFrom(
243 hasSameCanonicalDeclAsBoundNode(ToCastNode))))))));
244 auto MatchExprPtrObjCUnrelatedTypes = allOf(
245 hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
246 pointee(hasDeclaration(objcInterfaceDecl().bind(FromCastNode))))))),
247 ignoringImpCasts(hasType(objcObjectPointerType(
248 pointee(hasDeclaration(objcInterfaceDecl().bind(ToCastNode)))))),
250 ignoringImpCasts(hasType(objcObjectPointerType(
251 pointee(hasDeclaration(objcInterfaceDecl(isSameOrDerivedFrom(
252 hasSameCanonicalDeclAsBoundNode(FromCastNode)))))))),
253 hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
254 pointee(hasDeclaration(objcInterfaceDecl(isSameOrDerivedFrom(
255 hasSameCanonicalDeclAsBoundNode(ToCastNode))))))))))));
256 auto MatchExprRefTypeDefUnrelated = allOf(
257 hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
258 hasDeclaration(decl(cxxRecordDecl().bind(FromCastNode))))))),
259 hasType(hasUnqualifiedDesugaredType(
260 recordType(hasDeclaration(decl(cxxRecordDecl().bind(ToCastNode)))))),
262 hasType(hasUnqualifiedDesugaredType(
263 recordType(hasDeclaration(decl(cxxRecordDecl(isSameOrDerivedFrom(
264 hasSameCanonicalDeclAsBoundNode(FromCastNode)))))))),
265 hasSourceExpression(hasType(hasUnqualifiedDesugaredType(
266 recordType(hasDeclaration(decl(cxxRecordDecl(isSameOrDerivedFrom(
267 hasSameCanonicalDeclAsBoundNode(ToCastNode))))))))))));
268
269 auto ExplicitCastUnrelated =
270 explicitCastExpr(anyOf(MatchExprPtrUnrelatedTypes,
271 MatchExprPtrObjCUnrelatedTypes,
272 MatchExprRefTypeDefUnrelated))
273 .bind(WarnRecordDecl);
274 auto CastUnrelated = stmt(ExplicitCastUnrelated);
275 auto MatchesUnrelatedTypes = match(stmt(forEachDescendant(CastUnrelated)),
276 *D->getBody(), AM.getASTContext());
277 for (BoundNodes Match : MatchesUnrelatedTypes)
278 emitDiagnosticsUnrelated(Match, BR, ADC, this, BT);
279
280 // Match an `id`-typed argument implicitly converted to a specific
281 // Objective-C type at a call, message send, or constructor call, e.g.
282 // passing an `id` where an `NSString *` parameter is expected. Such
283 // conversions compile without a visible cast but throw at runtime if the
284 // object is not actually of that type.
285 auto CastArgFromIdToSpecificType =
287 hasCastKind(CK_BitCast),
288 hasSourceExpression(
289 ignoringParenImpCasts(hasType(qualType(isObjCIdType())))),
290 hasType(qualType(hasCanonicalType(objcObjectPointerType(pointee(
291 hasDeclaration(objcInterfaceDecl().bind(DerivedNode))))))))
292 .bind(WarnRecordDecl);
293 auto MatchCallArgFromId =
294 anyOf(callExpr(hasAnyArgument(CastArgFromIdToSpecificType)),
295 cxxConstructExpr(hasAnyArgument(CastArgFromIdToSpecificType)),
296 objcMessageExpr(hasAnyArgument(CastArgFromIdToSpecificType)));
297 auto MatchesCallArgFromId =
298 match(stmt(forEachDescendant(stmt(MatchCallArgFromId))), *D->getBody(),
299 AM.getASTContext());
300 for (BoundNodes Match : MatchesCallArgFromId)
301 emitDiagnosticsIdArg(Match, BR, ADC, this, BT);
302}
303
304void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
305 Mgr.registerChecker<MemoryUnsafeCastChecker>();
306}
307
308bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) {
309 return true;
310}
#define AST_MATCHER(Type, DefineMatcher)
AST_MATCHER(Type, DefineMatcher) { ... } defines a zero parameter function named DefineMatcher() that...
#define AST_MATCHER_P2(Type, DefineMatcher, ParamType1, Param1, ParamType2, Param2)
AST_MATCHER_P2( Type, DefineMatcher, ParamType1, Param1, ParamType2, Param2) { ....
#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param)
AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... } defines a single-parameter function name...
static void emitDiagnostics(const BoundNodes &Nodes, BugReporter &BR, AnalysisDeclContext *ADC, const MemoryUnsafeCastChecker *Checker, const BugType &BT)
static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM)
static void emitDiagnosticsIdArg(const BoundNodes &Nodes, BugReporter &BR, AnalysisDeclContext *ADC, const MemoryUnsafeCastChecker *Checker, const BugType &BT)
static decltype(auto) isThisOrDerefThis()
static void emitDiagnosticsUnrelated(const BoundNodes &Nodes, BugReporter &BR, AnalysisDeclContext *ADC, const MemoryUnsafeCastChecker *Checker, const BugType &BT)
static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM)
static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR, AnalysisManager &AM, const ObjCAutoreleaseWriteChecker *Checker)
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
CXXRecordDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition DeclCXX.h:523
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition Expr.h:3724
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition DeclBase.h:1104
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
Definition DeclBase.h:995
This represents one expression.
Definition Expr.h:113
This represents a decl that may have a name.
Definition Decl.h:275
A (possibly-)qualified type.
Definition TypeBase.h:938
Represents a template argument.
@ Type
The template argument is a type.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
Maps string IDs to AST nodes matched by parts of a matcher.
const T * getNodeAs(StringRef ID) const
Returns the AST node bound to ID.
ASTContext & getASTContext() override
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
BugReporter is a utility class for generating PathDiagnostics for analysis.
const SourceManager & getSourceManager()
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:565
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
const AstTypeMatcher< ObjCObjectPointerType > objcObjectPointerType
internal::Matcher< Decl > DeclarationMatcher
Types of matchers for the top-level classes in the AST class hierarchy.
const internal::VariadicDynCastAllOfMatcher< Stmt, ImplicitCastExpr > implicitCastExpr
Matches the implicit cast nodes of Clang's AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachDescendantMatcher > forEachDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher.
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator
Matches unary operator expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, ObjCMessageExpr > objcMessageExpr
Matches ObjectiveC Message invocation expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, TemplateTypeParmDecl > templateTypeParmDecl
Matches template type parameter declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr
Matches constructor call expressions (including implicit ones).
const internal::VariadicDynCastAllOfMatcher< Decl, ObjCInterfaceDecl > objcInterfaceDecl
Matches Objective-C interface declarations.
const AstTypeMatcher< PointerType > pointerType
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
const AstTypeMatcher< RecordType > recordType
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
const internal::VariadicAllOfMatcher< Decl > decl
Matches declarations.
internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)
Matches a node if the declaration associated with that node matches the given matcher.
const internal::VariadicAllOfMatcher< Stmt > stmt
Matches statements.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
const internal::VariadicDynCastAllOfMatcher< Stmt, CastExpr > castExpr
Matches any cast nodes of Clang's AST.
const internal::VariadicAllOfMatcher< QualType > qualType
Matches QualTypes in the clang AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXThisExpr > cxxThisExpr
Matches implicit and explicit this expressions.
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
bool Cast(InterpState &S, CodePtr OpPC)
Definition Interp.h:2878
Top level wrappers for InstallAPI frontend operations.
@ Match
This is not an overload because the signature exactly matches an existing declaration.
Definition Sema.h:824
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...