xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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