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// Matches a cast whose previously-bound BaseID node is a class template
109// specialization and whose previously-bound DerivedID node is one of that
110// specialization's type template arguments, i.e. the CRTP pattern
111// `class Derived : Base<Derived>`.
112AST_MATCHER_P2(Expr, isCRTPCast, std::string, BaseID, std::string, DerivedID) {
113 return Builder->removeBindings([this](const BoundNodesMap &Nodes) {
114 const auto *Base = Nodes.getNodeAs<CXXRecordDecl>(this->BaseID);
115 const auto *Derived = Nodes.getNodeAs<CXXRecordDecl>(this->DerivedID);
116 const auto *CTSD =
117 Base ? dyn_cast<ClassTemplateSpecializationDecl>(Base) : nullptr;
118 if (!CTSD || !Derived)
119 return true;
120 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
121 if (Arg.getKind() != TemplateArgument::Type)
122 continue;
123 QualType ArgType = Arg.getAsType();
124 if (!ArgType.isNull() && ArgType->getAsCXXRecordDecl() == Derived)
125 return false;
126 }
127 return true;
128 });
129}
130} // end anonymous namespace
131
132static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
133 return hasType(pointerType(pointee(hasDeclaration(DeclM))));
134}
135
136// Matches `this` or `*this`, but not member accesses like `this->m_field`.
137static decltype(auto) isThisOrDerefThis() {
138 return ignoringParenImpCasts(anyOf(
139 cxxThisExpr(),
140 unaryOperator(hasOperatorName("*"),
141 hasUnaryOperand(ignoringParenImpCasts(cxxThisExpr())))));
142}
143
144void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D,
145 AnalysisManager &AM,
146 BugReporter &BR) const {
147
148 AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
149
150 // Match downcasts from base type to derived type and warn
151 auto MatchExprPtr = allOf(
152 hasSourceExpression(hasTypePointingTo(cxxRecordDecl().bind(BaseNode))),
153 hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
154 .bind(DerivedNode)),
156 allOf(hasSourceExpression(cxxThisExpr()),
157 isCRTPCast(BaseNode, DerivedNode)))));
158 auto MatchExprPtrObjC = allOf(
159 hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
160 pointee(hasDeclaration(objcInterfaceDecl().bind(BaseNode))))))),
161 ignoringImpCasts(hasType(objcObjectPointerType(pointee(hasDeclaration(
162 objcInterfaceDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
163 .bind(DerivedNode)))))));
164 auto MatchExprRefTypeDef =
165 allOf(hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
166 hasDeclaration(decl(cxxRecordDecl().bind(BaseNode))))))),
167 hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
168 decl(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
169 .bind(DerivedNode)))))),
171 allOf(hasSourceExpression(isThisOrDerefThis()),
172 isCRTPCast(BaseNode, DerivedNode)))));
173 auto MatchExprPtrVoidCast = allOf(
174 anyOf(hasSourceExpression(explicitCastExpr(
175 hasType(pointerType(pointee(voidType()))),
176 hasSourceExpression(ignoringImpCasts(
177 hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))),
178 hasSourceExpression(
179 callExpr(hasType(pointerType(pointee(voidType()))),
180 hasAnyArgument(ignoringImpCasts(hasTypePointingTo(
181 cxxRecordDecl().bind(BaseNode))))))),
182 hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
183 .bind(DerivedNode)));
184
185 auto ExplicitCast =
186 explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef,
187 MatchExprPtrObjC, MatchExprPtrVoidCast))
188 .bind(WarnRecordDecl);
189 auto Cast = stmt(ExplicitCast);
190
191 auto Matches =
192 match(stmt(forEachDescendant(Cast)), *D->getBody(), AM.getASTContext());
193 for (BoundNodes Match : Matches)
194 emitDiagnostics(Match, BR, ADC, this, BT);
195
196 // Match calls returning derived type where an argument is a void pointer.
197 auto VoidPtrCast =
198 castExpr(hasType(pointerType(pointee(voidType()))),
199 hasSourceExpression(ignoringImpCasts(
200 hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))
201 .bind(WarnRecordDecl);
202 auto MatchCallPtrVoidArgCast = callExpr(
203 hasAnyArgument(anyOf(VoidPtrCast,
204 explicitCastExpr(hasSourceExpression(VoidPtrCast)))),
205 hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
206 .bind(DerivedNode)));
207 auto CallArgCast = stmt(MatchCallPtrVoidArgCast);
208 auto MatchesCallArgCast = match(stmt(forEachDescendant(CallArgCast)),
209 *D->getBody(), AM.getASTContext());
210 for (BoundNodes Match : MatchesCallArgCast)
211 emitDiagnostics(Match, BR, ADC, this, BT);
212
213 // Match casts between unrelated types and warn
214 auto MatchExprPtrUnrelatedTypes = allOf(
215 hasSourceExpression(
216 hasTypePointingTo(cxxRecordDecl().bind(FromCastNode))),
217 hasTypePointingTo(cxxRecordDecl().bind(ToCastNode)),
219 isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))),
220 hasSourceExpression(hasTypePointingTo(cxxRecordDecl(
221 isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))));
222 auto MatchExprPtrObjCUnrelatedTypes = allOf(
223 hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
224 pointee(hasDeclaration(objcInterfaceDecl().bind(FromCastNode))))))),
225 ignoringImpCasts(hasType(objcObjectPointerType(
226 pointee(hasDeclaration(objcInterfaceDecl().bind(ToCastNode)))))),
228 ignoringImpCasts(hasType(
230 isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
231 hasSourceExpression(ignoringImpCasts(hasType(
233 isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
234 auto MatchExprRefTypeDefUnrelated = allOf(
235 hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
236 hasDeclaration(decl(cxxRecordDecl().bind(FromCastNode))))))),
237 hasType(hasUnqualifiedDesugaredType(
238 recordType(hasDeclaration(decl(cxxRecordDecl().bind(ToCastNode)))))),
240 hasType(hasUnqualifiedDesugaredType(
242 isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
243 hasSourceExpression(hasType(hasUnqualifiedDesugaredType(
245 isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
246
247 auto ExplicitCastUnrelated =
248 explicitCastExpr(anyOf(MatchExprPtrUnrelatedTypes,
249 MatchExprPtrObjCUnrelatedTypes,
250 MatchExprRefTypeDefUnrelated))
251 .bind(WarnRecordDecl);
252 auto CastUnrelated = stmt(ExplicitCastUnrelated);
253 auto MatchesUnrelatedTypes = match(stmt(forEachDescendant(CastUnrelated)),
254 *D->getBody(), AM.getASTContext());
255 for (BoundNodes Match : MatchesUnrelatedTypes)
256 emitDiagnosticsUnrelated(Match, BR, ADC, this, BT);
257
258 // Match an `id`-typed argument implicitly converted to a specific
259 // Objective-C type at a call, message send, or constructor call, e.g.
260 // passing an `id` where an `NSString *` parameter is expected. Such
261 // conversions compile without a visible cast but throw at runtime if the
262 // object is not actually of that type.
263 auto CastArgFromIdToSpecificType =
265 hasCastKind(CK_BitCast),
266 hasSourceExpression(
267 ignoringParenImpCasts(hasType(qualType(isObjCIdType())))),
268 hasType(qualType(hasCanonicalType(objcObjectPointerType(pointee(
269 hasDeclaration(objcInterfaceDecl().bind(DerivedNode))))))))
270 .bind(WarnRecordDecl);
271 auto MatchCallArgFromId =
272 anyOf(callExpr(hasAnyArgument(CastArgFromIdToSpecificType)),
273 cxxConstructExpr(hasAnyArgument(CastArgFromIdToSpecificType)),
274 objcMessageExpr(hasAnyArgument(CastArgFromIdToSpecificType)));
275 auto MatchesCallArgFromId =
276 match(stmt(forEachDescendant(stmt(MatchCallArgFromId))), *D->getBody(),
277 AM.getASTContext());
278 for (BoundNodes Match : MatchesCallArgFromId)
279 emitDiagnosticsIdArg(Match, BR, ADC, this, BT);
280}
281
282void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
283 Mgr.registerChecker<MemoryUnsafeCastChecker>();
284}
285
286bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) {
287 return true;
288}
#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) { ....
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
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition Expr.h:3687
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
This represents one expression.
Definition Expr.h:112
This represents a decl that may have a name.
Definition Decl.h:274
A (possibly-)qualified type.
Definition TypeBase.h:938
Represents a template argument.
@ Type
The template argument is a type.
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:2830
Top level wrappers for InstallAPI frontend operations.
@ Match
This is not an overload because the signature exactly matches an existing declaration.
Definition Sema.h:825
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...