|
| 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 | +} |
0 commit comments