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