189 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines MemoryUnsafeCast checker, which checks for casts from a10// 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 20using namespace clang;21using namespace ento;22using namespace ast_matchers;23 24namespace {25static constexpr const char *const BaseNode = "BaseNode";26static constexpr const char *const DerivedNode = "DerivedNode";27static constexpr const char *const FromCastNode = "FromCast";28static constexpr const char *const ToCastNode = "ToCast";29static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";30 31class MemoryUnsafeCastChecker : public Checker<check::ASTCodeBody> {32 BugType BT{this, "Unsafe cast", "WebKit coding guidelines"};33 34public:35 void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,36 BugReporter &BR) const;37};38} // end namespace39 40static 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 60static 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 80namespace clang {81namespace ast_matchers {82AST_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_matchers92} // end namespace clang93 94static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {95 return hasType(pointerType(pointee(hasDeclaration(DeclM))));96}97 98void 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 warn105 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 warn137 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 182void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {183 Mgr.registerChecker<MemoryUnsafeCastChecker>();184}185 186bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) {187 return true;188}189