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 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 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 { 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 94 static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) { 95 return hasType(pointerType(pointee(hasDeclaration(DeclM)))); 96 } 97 98 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 182 void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) { 183 Mgr.registerChecker<MemoryUnsafeCastChecker>(); 184 } 185 186 bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) { 187 return true; 188 } 189