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 BR.emitReport(std::move(Report));
58}
59
60static void emitDiagnosticsUnrelated(const BoundNodes &Nodes, BugReporter &BR,
62 const MemoryUnsafeCastChecker *Checker,
63 const BugType &BT) {
64 const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
65 const NamedDecl *FromCast = Nodes.getNodeAs<NamedDecl>(FromCastNode);
66 const NamedDecl *ToCast = Nodes.getNodeAs<NamedDecl>(ToCastNode);
67 assert(CE && FromCast && ToCast);
68
69 std::string Diagnostics;
70 llvm::raw_string_ostream OS(Diagnostics);
71 OS << "Unsafe cast from type '" << FromCast->getNameAsString()
72 << "' to an unrelated type '" << ToCast->getNameAsString() << "'";
73 PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
74 BR.getSourceManager());
75 auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
76 Report->addRange(CE->getSourceRange());
77 BR.emitReport(std::move(Report));
78}
79
80namespace clang {
81namespace ast_matchers {
82AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) {
83 return Builder->removeBindings([this, &Node](const BoundNodesMap &Nodes) {
84 const auto &BN = Nodes.getNode(this->BindingID);
85 if (const auto *ND = BN.get<NamedDecl>()) {
86 return ND->getName() != Node.getString();
87 }
88 return true;
89 });
90}
91
92// Matches a cast whose previously-bound BaseID node is a class template
93// specialization and whose previously-bound DerivedID node is one of that
94// specialization's type template arguments, i.e. the CRTP pattern
95// `class Derived : Base<Derived>`.
96AST_MATCHER_P2(Expr, isCRTPCast, std::string, BaseID, std::string, DerivedID) {
97 return Builder->removeBindings([this](const BoundNodesMap &Nodes) {
98 const auto *Base = Nodes.getNodeAs<CXXRecordDecl>(this->BaseID);
99 const auto *Derived = Nodes.getNodeAs<CXXRecordDecl>(this->DerivedID);
100 const auto *CTSD =
101 Base ? dyn_cast<ClassTemplateSpecializationDecl>(Base) : nullptr;
102 if (!CTSD || !Derived)
103 return true;
104 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
105 if (Arg.getKind() != TemplateArgument::Type)
106 continue;
107 QualType ArgType = Arg.getAsType();
108 if (!ArgType.isNull() && ArgType->getAsCXXRecordDecl() == Derived)
109 return false;
110 }
111 return true;
112 });
113}
114} // end namespace ast_matchers
115} // end namespace clang
116
117static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
118 return hasType(pointerType(pointee(hasDeclaration(DeclM))));
119}
120
121// Matches `this` or `*this`, but not member accesses like `this->m_field`.
122static decltype(auto) isThisOrDerefThis() {
123 return ignoringParenImpCasts(anyOf(
124 cxxThisExpr(),
125 unaryOperator(hasOperatorName("*"),
126 hasUnaryOperand(ignoringParenImpCasts(cxxThisExpr())))));
127}
128
129void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D,
130 AnalysisManager &AM,
131 BugReporter &BR) const {
132
133 AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
134
135 // Match downcasts from base type to derived type and warn
136 auto MatchExprPtr = allOf(
137 hasSourceExpression(hasTypePointingTo(cxxRecordDecl().bind(BaseNode))),
138 hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
139 .bind(DerivedNode)),
141 allOf(hasSourceExpression(cxxThisExpr()),
142 isCRTPCast(BaseNode, DerivedNode)))));
143 auto MatchExprPtrObjC = allOf(
144 hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
145 pointee(hasDeclaration(objcInterfaceDecl().bind(BaseNode))))))),
146 ignoringImpCasts(hasType(objcObjectPointerType(pointee(hasDeclaration(
147 objcInterfaceDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
148 .bind(DerivedNode)))))));
149 auto MatchExprRefTypeDef =
150 allOf(hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
151 hasDeclaration(decl(cxxRecordDecl().bind(BaseNode))))))),
152 hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
153 decl(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
154 .bind(DerivedNode)))))),
156 allOf(hasSourceExpression(isThisOrDerefThis()),
157 isCRTPCast(BaseNode, DerivedNode)))));
158 auto MatchExprPtrVoidCast = allOf(
159 anyOf(hasSourceExpression(explicitCastExpr(
160 hasType(pointerType(pointee(voidType()))),
161 hasSourceExpression(ignoringImpCasts(
162 hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))),
163 hasSourceExpression(
164 callExpr(hasType(pointerType(pointee(voidType()))),
165 hasAnyArgument(ignoringImpCasts(hasTypePointingTo(
166 cxxRecordDecl().bind(BaseNode))))))),
167 hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
168 .bind(DerivedNode)));
169
170 auto ExplicitCast =
171 explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef,
172 MatchExprPtrObjC, MatchExprPtrVoidCast))
173 .bind(WarnRecordDecl);
174 auto Cast = stmt(ExplicitCast);
175
176 auto Matches =
177 match(stmt(forEachDescendant(Cast)), *D->getBody(), AM.getASTContext());
178 for (BoundNodes Match : Matches)
179 emitDiagnostics(Match, BR, ADC, this, BT);
180
181 // Match calls returning derived type where an argument is a void pointer.
182 auto VoidPtrCast =
183 castExpr(hasType(pointerType(pointee(voidType()))),
184 hasSourceExpression(ignoringImpCasts(
185 hasTypePointingTo(cxxRecordDecl().bind(BaseNode)))))
186 .bind(WarnRecordDecl);
187 auto MatchCallPtrVoidArgCast = callExpr(
188 hasAnyArgument(anyOf(VoidPtrCast,
189 explicitCastExpr(hasSourceExpression(VoidPtrCast)))),
190 hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
191 .bind(DerivedNode)));
192 auto CallArgCast = stmt(MatchCallPtrVoidArgCast);
193 auto MatchesCallArgCast = match(stmt(forEachDescendant(CallArgCast)),
194 *D->getBody(), AM.getASTContext());
195 for (BoundNodes Match : MatchesCallArgCast)
196 emitDiagnostics(Match, BR, ADC, this, BT);
197
198 // Match casts between unrelated types and warn
199 auto MatchExprPtrUnrelatedTypes = allOf(
200 hasSourceExpression(
201 hasTypePointingTo(cxxRecordDecl().bind(FromCastNode))),
202 hasTypePointingTo(cxxRecordDecl().bind(ToCastNode)),
204 isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))),
205 hasSourceExpression(hasTypePointingTo(cxxRecordDecl(
206 isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))));
207 auto MatchExprPtrObjCUnrelatedTypes = allOf(
208 hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
209 pointee(hasDeclaration(objcInterfaceDecl().bind(FromCastNode))))))),
210 ignoringImpCasts(hasType(objcObjectPointerType(
211 pointee(hasDeclaration(objcInterfaceDecl().bind(ToCastNode)))))),
213 ignoringImpCasts(hasType(
215 isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
216 hasSourceExpression(ignoringImpCasts(hasType(
218 isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
219 auto MatchExprRefTypeDefUnrelated = allOf(
220 hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
221 hasDeclaration(decl(cxxRecordDecl().bind(FromCastNode))))))),
222 hasType(hasUnqualifiedDesugaredType(
223 recordType(hasDeclaration(decl(cxxRecordDecl().bind(ToCastNode)))))),
225 hasType(hasUnqualifiedDesugaredType(
227 isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
228 hasSourceExpression(hasType(hasUnqualifiedDesugaredType(
230 isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
231
232 auto ExplicitCastUnrelated =
233 explicitCastExpr(anyOf(MatchExprPtrUnrelatedTypes,
234 MatchExprPtrObjCUnrelatedTypes,
235 MatchExprRefTypeDefUnrelated))
236 .bind(WarnRecordDecl);
237 auto CastUnrelated = stmt(ExplicitCastUnrelated);
238 auto MatchesUnrelatedTypes = match(stmt(forEachDescendant(CastUnrelated)),
239 *D->getBody(), AM.getASTContext());
240 for (BoundNodes Match : MatchesUnrelatedTypes)
241 emitDiagnosticsUnrelated(Match, BR, ADC, this, BT);
242}
243
244void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
245 Mgr.registerChecker<MemoryUnsafeCastChecker>();
246}
247
248bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) {
249 return true;
250}
#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 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:3682
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, 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< Decl, TemplateTypeParmDecl > templateTypeParmDecl
Matches template type parameter declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
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::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:2820
The JSON file list parser is used to communicate input to InstallAPI.
@ Match
This is not an overload because the signature exactly matches an existing declaration.
Definition Sema.h:830