xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
19 
20 using namespace clang;
21 using namespace ento;
22 using namespace ast_matchers;
23 
24 namespace {
25 static constexpr const char *const BaseNode = "BaseNode";
26 static constexpr const char *const DerivedNode = "DerivedNode";
27 static constexpr const char *const FromCastNode = "FromCast";
28 static constexpr const char *const ToCastNode = "ToCast";
29 static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";
30 
31 class MemoryUnsafeCastChecker : public Checker<check::ASTCodeBody> {
32   BugType BT{this, "Unsafe cast", "WebKit coding guidelines"};
33 
34 public:
35   void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
36                         BugReporter &BR) const;
37 };
38 } // end namespace
39 
emitDiagnostics(const BoundNodes & Nodes,BugReporter & BR,AnalysisDeclContext * ADC,const MemoryUnsafeCastChecker * Checker,const BugType & BT)40 static void emitDiagnostics(const BoundNodes &Nodes, BugReporter &BR,
41                             AnalysisDeclContext *ADC,
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 
emitDiagnosticsUnrelated(const BoundNodes & Nodes,BugReporter & BR,AnalysisDeclContext * ADC,const MemoryUnsafeCastChecker * Checker,const BugType & BT)60 static void emitDiagnosticsUnrelated(const BoundNodes &Nodes, BugReporter &BR,
61                                      AnalysisDeclContext *ADC,
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 
80 namespace clang {
81 namespace ast_matchers {
AST_MATCHER_P(StringLiteral,mentionsBoundType,std::string,BindingID)82 AST_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 } // end namespace ast_matchers
92 } // end namespace clang
93 
hasTypePointingTo(DeclarationMatcher DeclM)94 static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
95   return hasType(pointerType(pointee(hasDeclaration(DeclM))));
96 }
97 
checkASTCodeBody(const Decl * D,AnalysisManager & AM,BugReporter & BR) const98 void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D,
99                                                AnalysisManager &AM,
100                                                BugReporter &BR) const {
101 
102   AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
103 
104   // Match downcasts from base type to derived type and warn
105   auto MatchExprPtr = allOf(
106       hasSourceExpression(hasTypePointingTo(cxxRecordDecl().bind(BaseNode))),
107       hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
108                             .bind(DerivedNode)),
109       unless(anyOf(hasSourceExpression(cxxThisExpr()),
110                    hasTypePointingTo(templateTypeParmDecl()))));
111   auto MatchExprPtrObjC = allOf(
112       hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
113           pointee(hasDeclaration(objcInterfaceDecl().bind(BaseNode))))))),
114       ignoringImpCasts(hasType(objcObjectPointerType(pointee(hasDeclaration(
115           objcInterfaceDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
116               .bind(DerivedNode)))))));
117   auto MatchExprRefTypeDef =
118       allOf(hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
119                 hasDeclaration(decl(cxxRecordDecl().bind(BaseNode))))))),
120             hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
121                 decl(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
122                          .bind(DerivedNode)))))),
123             unless(anyOf(hasSourceExpression(hasDescendant(cxxThisExpr())),
124                          hasType(templateTypeParmDecl()))));
125 
126   auto ExplicitCast = explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef,
127                                              MatchExprPtrObjC))
128                           .bind(WarnRecordDecl);
129   auto Cast = stmt(ExplicitCast);
130 
131   auto Matches =
132       match(stmt(forEachDescendant(Cast)), *D->getBody(), AM.getASTContext());
133   for (BoundNodes Match : Matches)
134     emitDiagnostics(Match, BR, ADC, this, BT);
135 
136   // Match casts between unrelated types and warn
137   auto MatchExprPtrUnrelatedTypes = allOf(
138       hasSourceExpression(
139           hasTypePointingTo(cxxRecordDecl().bind(FromCastNode))),
140       hasTypePointingTo(cxxRecordDecl().bind(ToCastNode)),
141       unless(anyOf(hasTypePointingTo(cxxRecordDecl(
142                        isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))),
143                    hasSourceExpression(hasTypePointingTo(cxxRecordDecl(
144                        isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))));
145   auto MatchExprPtrObjCUnrelatedTypes = allOf(
146       hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
147           pointee(hasDeclaration(objcInterfaceDecl().bind(FromCastNode))))))),
148       ignoringImpCasts(hasType(objcObjectPointerType(
149           pointee(hasDeclaration(objcInterfaceDecl().bind(ToCastNode)))))),
150       unless(anyOf(
151           ignoringImpCasts(hasType(
152               objcObjectPointerType(pointee(hasDeclaration(objcInterfaceDecl(
153                   isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
154           hasSourceExpression(ignoringImpCasts(hasType(
155               objcObjectPointerType(pointee(hasDeclaration(objcInterfaceDecl(
156                   isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
157   auto MatchExprRefTypeDefUnrelated = allOf(
158       hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
159           hasDeclaration(decl(cxxRecordDecl().bind(FromCastNode))))))),
160       hasType(hasUnqualifiedDesugaredType(
161           recordType(hasDeclaration(decl(cxxRecordDecl().bind(ToCastNode)))))),
162       unless(anyOf(
163           hasType(hasUnqualifiedDesugaredType(
164               recordType(hasDeclaration(decl(cxxRecordDecl(
165                   isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
166           hasSourceExpression(hasType(hasUnqualifiedDesugaredType(
167               recordType(hasDeclaration(decl(cxxRecordDecl(
168                   isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
169 
170   auto ExplicitCastUnrelated =
171       explicitCastExpr(anyOf(MatchExprPtrUnrelatedTypes,
172                              MatchExprPtrObjCUnrelatedTypes,
173                              MatchExprRefTypeDefUnrelated))
174           .bind(WarnRecordDecl);
175   auto CastUnrelated = stmt(ExplicitCastUnrelated);
176   auto MatchesUnrelatedTypes = match(stmt(forEachDescendant(CastUnrelated)),
177                                      *D->getBody(), AM.getASTContext());
178   for (BoundNodes Match : MatchesUnrelatedTypes)
179     emitDiagnosticsUnrelated(Match, BR, ADC, this, BT);
180 }
181 
registerMemoryUnsafeCastChecker(CheckerManager & Mgr)182 void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
183   Mgr.registerChecker<MemoryUnsafeCastChecker>();
184 }
185 
shouldRegisterMemoryUnsafeCastChecker(const CheckerManager & mgr)186 bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) {
187   return true;
188 }
189