1*700637cbSDimitry Andric //=======- MemoryUnsafeCastChecker.cpp -------------------------*- C++ -*-==//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric //
9*700637cbSDimitry Andric // This file defines MemoryUnsafeCast checker, which checks for casts from a
10*700637cbSDimitry Andric // base type to a derived type.
11*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
12*700637cbSDimitry Andric
13*700637cbSDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
14*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
16*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
18*700637cbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
19*700637cbSDimitry Andric
20*700637cbSDimitry Andric using namespace clang;
21*700637cbSDimitry Andric using namespace ento;
22*700637cbSDimitry Andric using namespace ast_matchers;
23*700637cbSDimitry Andric
24*700637cbSDimitry Andric namespace {
25*700637cbSDimitry Andric static constexpr const char *const BaseNode = "BaseNode";
26*700637cbSDimitry Andric static constexpr const char *const DerivedNode = "DerivedNode";
27*700637cbSDimitry Andric static constexpr const char *const FromCastNode = "FromCast";
28*700637cbSDimitry Andric static constexpr const char *const ToCastNode = "ToCast";
29*700637cbSDimitry Andric static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";
30*700637cbSDimitry Andric
31*700637cbSDimitry Andric class MemoryUnsafeCastChecker : public Checker<check::ASTCodeBody> {
32*700637cbSDimitry Andric BugType BT{this, "Unsafe cast", "WebKit coding guidelines"};
33*700637cbSDimitry Andric
34*700637cbSDimitry Andric public:
35*700637cbSDimitry Andric void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
36*700637cbSDimitry Andric BugReporter &BR) const;
37*700637cbSDimitry Andric };
38*700637cbSDimitry Andric } // end namespace
39*700637cbSDimitry Andric
emitDiagnostics(const BoundNodes & Nodes,BugReporter & BR,AnalysisDeclContext * ADC,const MemoryUnsafeCastChecker * Checker,const BugType & BT)40*700637cbSDimitry Andric static void emitDiagnostics(const BoundNodes &Nodes, BugReporter &BR,
41*700637cbSDimitry Andric AnalysisDeclContext *ADC,
42*700637cbSDimitry Andric const MemoryUnsafeCastChecker *Checker,
43*700637cbSDimitry Andric const BugType &BT) {
44*700637cbSDimitry Andric const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
45*700637cbSDimitry Andric const NamedDecl *Base = Nodes.getNodeAs<NamedDecl>(BaseNode);
46*700637cbSDimitry Andric const NamedDecl *Derived = Nodes.getNodeAs<NamedDecl>(DerivedNode);
47*700637cbSDimitry Andric assert(CE && Base && Derived);
48*700637cbSDimitry Andric
49*700637cbSDimitry Andric std::string Diagnostics;
50*700637cbSDimitry Andric llvm::raw_string_ostream OS(Diagnostics);
51*700637cbSDimitry Andric OS << "Unsafe cast from base type '" << Base->getNameAsString()
52*700637cbSDimitry Andric << "' to derived type '" << Derived->getNameAsString() << "'";
53*700637cbSDimitry Andric PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
54*700637cbSDimitry Andric BR.getSourceManager());
55*700637cbSDimitry Andric auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
56*700637cbSDimitry Andric Report->addRange(CE->getSourceRange());
57*700637cbSDimitry Andric BR.emitReport(std::move(Report));
58*700637cbSDimitry Andric }
59*700637cbSDimitry Andric
emitDiagnosticsUnrelated(const BoundNodes & Nodes,BugReporter & BR,AnalysisDeclContext * ADC,const MemoryUnsafeCastChecker * Checker,const BugType & BT)60*700637cbSDimitry Andric static void emitDiagnosticsUnrelated(const BoundNodes &Nodes, BugReporter &BR,
61*700637cbSDimitry Andric AnalysisDeclContext *ADC,
62*700637cbSDimitry Andric const MemoryUnsafeCastChecker *Checker,
63*700637cbSDimitry Andric const BugType &BT) {
64*700637cbSDimitry Andric const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
65*700637cbSDimitry Andric const NamedDecl *FromCast = Nodes.getNodeAs<NamedDecl>(FromCastNode);
66*700637cbSDimitry Andric const NamedDecl *ToCast = Nodes.getNodeAs<NamedDecl>(ToCastNode);
67*700637cbSDimitry Andric assert(CE && FromCast && ToCast);
68*700637cbSDimitry Andric
69*700637cbSDimitry Andric std::string Diagnostics;
70*700637cbSDimitry Andric llvm::raw_string_ostream OS(Diagnostics);
71*700637cbSDimitry Andric OS << "Unsafe cast from type '" << FromCast->getNameAsString()
72*700637cbSDimitry Andric << "' to an unrelated type '" << ToCast->getNameAsString() << "'";
73*700637cbSDimitry Andric PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
74*700637cbSDimitry Andric BR.getSourceManager());
75*700637cbSDimitry Andric auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
76*700637cbSDimitry Andric Report->addRange(CE->getSourceRange());
77*700637cbSDimitry Andric BR.emitReport(std::move(Report));
78*700637cbSDimitry Andric }
79*700637cbSDimitry Andric
80*700637cbSDimitry Andric namespace clang {
81*700637cbSDimitry Andric namespace ast_matchers {
AST_MATCHER_P(StringLiteral,mentionsBoundType,std::string,BindingID)82*700637cbSDimitry Andric AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) {
83*700637cbSDimitry Andric return Builder->removeBindings([this, &Node](const BoundNodesMap &Nodes) {
84*700637cbSDimitry Andric const auto &BN = Nodes.getNode(this->BindingID);
85*700637cbSDimitry Andric if (const auto *ND = BN.get<NamedDecl>()) {
86*700637cbSDimitry Andric return ND->getName() != Node.getString();
87*700637cbSDimitry Andric }
88*700637cbSDimitry Andric return true;
89*700637cbSDimitry Andric });
90*700637cbSDimitry Andric }
91*700637cbSDimitry Andric } // end namespace ast_matchers
92*700637cbSDimitry Andric } // end namespace clang
93*700637cbSDimitry Andric
hasTypePointingTo(DeclarationMatcher DeclM)94*700637cbSDimitry Andric static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
95*700637cbSDimitry Andric return hasType(pointerType(pointee(hasDeclaration(DeclM))));
96*700637cbSDimitry Andric }
97*700637cbSDimitry Andric
checkASTCodeBody(const Decl * D,AnalysisManager & AM,BugReporter & BR) const98*700637cbSDimitry Andric void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D,
99*700637cbSDimitry Andric AnalysisManager &AM,
100*700637cbSDimitry Andric BugReporter &BR) const {
101*700637cbSDimitry Andric
102*700637cbSDimitry Andric AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
103*700637cbSDimitry Andric
104*700637cbSDimitry Andric // Match downcasts from base type to derived type and warn
105*700637cbSDimitry Andric auto MatchExprPtr = allOf(
106*700637cbSDimitry Andric hasSourceExpression(hasTypePointingTo(cxxRecordDecl().bind(BaseNode))),
107*700637cbSDimitry Andric hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
108*700637cbSDimitry Andric .bind(DerivedNode)),
109*700637cbSDimitry Andric unless(anyOf(hasSourceExpression(cxxThisExpr()),
110*700637cbSDimitry Andric hasTypePointingTo(templateTypeParmDecl()))));
111*700637cbSDimitry Andric auto MatchExprPtrObjC = allOf(
112*700637cbSDimitry Andric hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
113*700637cbSDimitry Andric pointee(hasDeclaration(objcInterfaceDecl().bind(BaseNode))))))),
114*700637cbSDimitry Andric ignoringImpCasts(hasType(objcObjectPointerType(pointee(hasDeclaration(
115*700637cbSDimitry Andric objcInterfaceDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
116*700637cbSDimitry Andric .bind(DerivedNode)))))));
117*700637cbSDimitry Andric auto MatchExprRefTypeDef =
118*700637cbSDimitry Andric allOf(hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
119*700637cbSDimitry Andric hasDeclaration(decl(cxxRecordDecl().bind(BaseNode))))))),
120*700637cbSDimitry Andric hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
121*700637cbSDimitry Andric decl(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
122*700637cbSDimitry Andric .bind(DerivedNode)))))),
123*700637cbSDimitry Andric unless(anyOf(hasSourceExpression(hasDescendant(cxxThisExpr())),
124*700637cbSDimitry Andric hasType(templateTypeParmDecl()))));
125*700637cbSDimitry Andric
126*700637cbSDimitry Andric auto ExplicitCast = explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef,
127*700637cbSDimitry Andric MatchExprPtrObjC))
128*700637cbSDimitry Andric .bind(WarnRecordDecl);
129*700637cbSDimitry Andric auto Cast = stmt(ExplicitCast);
130*700637cbSDimitry Andric
131*700637cbSDimitry Andric auto Matches =
132*700637cbSDimitry Andric match(stmt(forEachDescendant(Cast)), *D->getBody(), AM.getASTContext());
133*700637cbSDimitry Andric for (BoundNodes Match : Matches)
134*700637cbSDimitry Andric emitDiagnostics(Match, BR, ADC, this, BT);
135*700637cbSDimitry Andric
136*700637cbSDimitry Andric // Match casts between unrelated types and warn
137*700637cbSDimitry Andric auto MatchExprPtrUnrelatedTypes = allOf(
138*700637cbSDimitry Andric hasSourceExpression(
139*700637cbSDimitry Andric hasTypePointingTo(cxxRecordDecl().bind(FromCastNode))),
140*700637cbSDimitry Andric hasTypePointingTo(cxxRecordDecl().bind(ToCastNode)),
141*700637cbSDimitry Andric unless(anyOf(hasTypePointingTo(cxxRecordDecl(
142*700637cbSDimitry Andric isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))),
143*700637cbSDimitry Andric hasSourceExpression(hasTypePointingTo(cxxRecordDecl(
144*700637cbSDimitry Andric isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))));
145*700637cbSDimitry Andric auto MatchExprPtrObjCUnrelatedTypes = allOf(
146*700637cbSDimitry Andric hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
147*700637cbSDimitry Andric pointee(hasDeclaration(objcInterfaceDecl().bind(FromCastNode))))))),
148*700637cbSDimitry Andric ignoringImpCasts(hasType(objcObjectPointerType(
149*700637cbSDimitry Andric pointee(hasDeclaration(objcInterfaceDecl().bind(ToCastNode)))))),
150*700637cbSDimitry Andric unless(anyOf(
151*700637cbSDimitry Andric ignoringImpCasts(hasType(
152*700637cbSDimitry Andric objcObjectPointerType(pointee(hasDeclaration(objcInterfaceDecl(
153*700637cbSDimitry Andric isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
154*700637cbSDimitry Andric hasSourceExpression(ignoringImpCasts(hasType(
155*700637cbSDimitry Andric objcObjectPointerType(pointee(hasDeclaration(objcInterfaceDecl(
156*700637cbSDimitry Andric isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
157*700637cbSDimitry Andric auto MatchExprRefTypeDefUnrelated = allOf(
158*700637cbSDimitry Andric hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
159*700637cbSDimitry Andric hasDeclaration(decl(cxxRecordDecl().bind(FromCastNode))))))),
160*700637cbSDimitry Andric hasType(hasUnqualifiedDesugaredType(
161*700637cbSDimitry Andric recordType(hasDeclaration(decl(cxxRecordDecl().bind(ToCastNode)))))),
162*700637cbSDimitry Andric unless(anyOf(
163*700637cbSDimitry Andric hasType(hasUnqualifiedDesugaredType(
164*700637cbSDimitry Andric recordType(hasDeclaration(decl(cxxRecordDecl(
165*700637cbSDimitry Andric isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
166*700637cbSDimitry Andric hasSourceExpression(hasType(hasUnqualifiedDesugaredType(
167*700637cbSDimitry Andric recordType(hasDeclaration(decl(cxxRecordDecl(
168*700637cbSDimitry Andric isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
169*700637cbSDimitry Andric
170*700637cbSDimitry Andric auto ExplicitCastUnrelated =
171*700637cbSDimitry Andric explicitCastExpr(anyOf(MatchExprPtrUnrelatedTypes,
172*700637cbSDimitry Andric MatchExprPtrObjCUnrelatedTypes,
173*700637cbSDimitry Andric MatchExprRefTypeDefUnrelated))
174*700637cbSDimitry Andric .bind(WarnRecordDecl);
175*700637cbSDimitry Andric auto CastUnrelated = stmt(ExplicitCastUnrelated);
176*700637cbSDimitry Andric auto MatchesUnrelatedTypes = match(stmt(forEachDescendant(CastUnrelated)),
177*700637cbSDimitry Andric *D->getBody(), AM.getASTContext());
178*700637cbSDimitry Andric for (BoundNodes Match : MatchesUnrelatedTypes)
179*700637cbSDimitry Andric emitDiagnosticsUnrelated(Match, BR, ADC, this, BT);
180*700637cbSDimitry Andric }
181*700637cbSDimitry Andric
registerMemoryUnsafeCastChecker(CheckerManager & Mgr)182*700637cbSDimitry Andric void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
183*700637cbSDimitry Andric Mgr.registerChecker<MemoryUnsafeCastChecker>();
184*700637cbSDimitry Andric }
185*700637cbSDimitry Andric
shouldRegisterMemoryUnsafeCastChecker(const CheckerManager & mgr)186*700637cbSDimitry Andric bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) {
187*700637cbSDimitry Andric return true;
188*700637cbSDimitry Andric }
189