File: MemoryUnsafeCastChecker.cpp

package info (click to toggle)
llvm-toolchain-20 1%3A20.1.6-1~exp1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 2,111,304 kB
  • sloc: cpp: 7,438,677; ansic: 1,393,822; asm: 1,012,926; python: 241,650; f90: 86,635; objc: 75,479; lisp: 42,144; pascal: 17,286; sh: 10,027; ml: 5,082; perl: 4,730; awk: 3,523; makefile: 3,349; javascript: 2,251; xml: 892; fortran: 672
file content (188 lines) | stat: -rw-r--r-- 8,843 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//=======- MemoryUnsafeCastChecker.cpp -------------------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines MemoryUnsafeCast checker, which checks for casts from a
// base type to a derived type.
//===----------------------------------------------------------------------===//

#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"

using namespace clang;
using namespace ento;
using namespace ast_matchers;

namespace {
static constexpr const char *const BaseNode = "BaseNode";
static constexpr const char *const DerivedNode = "DerivedNode";
static constexpr const char *const FromCastNode = "FromCast";
static constexpr const char *const ToCastNode = "ToCast";
static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";

class MemoryUnsafeCastChecker : public Checker<check::ASTCodeBody> {
  BugType BT{this, "Unsafe cast", "WebKit coding guidelines"};

public:
  void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
                        BugReporter &BR) const;
};
} // end namespace

static void emitDiagnostics(const BoundNodes &Nodes, BugReporter &BR,
                            AnalysisDeclContext *ADC,
                            const MemoryUnsafeCastChecker *Checker,
                            const BugType &BT) {
  const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
  const NamedDecl *Base = Nodes.getNodeAs<NamedDecl>(BaseNode);
  const NamedDecl *Derived = Nodes.getNodeAs<NamedDecl>(DerivedNode);
  assert(CE && Base && Derived);

  std::string Diagnostics;
  llvm::raw_string_ostream OS(Diagnostics);
  OS << "Unsafe cast from base type '" << Base->getNameAsString()
     << "' to derived type '" << Derived->getNameAsString() << "'";
  PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
                               BR.getSourceManager());
  auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
  Report->addRange(CE->getSourceRange());
  BR.emitReport(std::move(Report));
}

static void emitDiagnosticsUnrelated(const BoundNodes &Nodes, BugReporter &BR,
                                     AnalysisDeclContext *ADC,
                                     const MemoryUnsafeCastChecker *Checker,
                                     const BugType &BT) {
  const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
  const NamedDecl *FromCast = Nodes.getNodeAs<NamedDecl>(FromCastNode);
  const NamedDecl *ToCast = Nodes.getNodeAs<NamedDecl>(ToCastNode);
  assert(CE && FromCast && ToCast);

  std::string Diagnostics;
  llvm::raw_string_ostream OS(Diagnostics);
  OS << "Unsafe cast from type '" << FromCast->getNameAsString()
     << "' to an unrelated type '" << ToCast->getNameAsString() << "'";
  PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
                               BR.getSourceManager());
  auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
  Report->addRange(CE->getSourceRange());
  BR.emitReport(std::move(Report));
}

namespace clang {
namespace ast_matchers {
AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) {
  return Builder->removeBindings([this, &Node](const BoundNodesMap &Nodes) {
    const auto &BN = Nodes.getNode(this->BindingID);
    if (const auto *ND = BN.get<NamedDecl>()) {
      return ND->getName() != Node.getString();
    }
    return true;
  });
}
} // end namespace ast_matchers
} // end namespace clang

static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
  return hasType(pointerType(pointee(hasDeclaration(DeclM))));
}

void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D,
                                               AnalysisManager &AM,
                                               BugReporter &BR) const {

  AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);

  // Match downcasts from base type to derived type and warn
  auto MatchExprPtr = allOf(
      hasSourceExpression(hasTypePointingTo(cxxRecordDecl().bind(BaseNode))),
      hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
                            .bind(DerivedNode)),
      unless(anyOf(hasSourceExpression(cxxThisExpr()),
                   hasTypePointingTo(templateTypeParmDecl()))));
  auto MatchExprPtrObjC = allOf(
      hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
          pointee(hasDeclaration(objcInterfaceDecl().bind(BaseNode))))))),
      ignoringImpCasts(hasType(objcObjectPointerType(pointee(hasDeclaration(
          objcInterfaceDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
              .bind(DerivedNode)))))));
  auto MatchExprRefTypeDef =
      allOf(hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
                hasDeclaration(decl(cxxRecordDecl().bind(BaseNode))))))),
            hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
                decl(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
                         .bind(DerivedNode)))))),
            unless(anyOf(hasSourceExpression(hasDescendant(cxxThisExpr())),
                         hasType(templateTypeParmDecl()))));

  auto ExplicitCast = explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef,
                                             MatchExprPtrObjC))
                          .bind(WarnRecordDecl);
  auto Cast = stmt(ExplicitCast);

  auto Matches =
      match(stmt(forEachDescendant(Cast)), *D->getBody(), AM.getASTContext());
  for (BoundNodes Match : Matches)
    emitDiagnostics(Match, BR, ADC, this, BT);

  // Match casts between unrelated types and warn
  auto MatchExprPtrUnrelatedTypes = allOf(
      hasSourceExpression(
          hasTypePointingTo(cxxRecordDecl().bind(FromCastNode))),
      hasTypePointingTo(cxxRecordDecl().bind(ToCastNode)),
      unless(anyOf(hasTypePointingTo(cxxRecordDecl(
                       isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))),
                   hasSourceExpression(hasTypePointingTo(cxxRecordDecl(
                       isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))));
  auto MatchExprPtrObjCUnrelatedTypes = allOf(
      hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
          pointee(hasDeclaration(objcInterfaceDecl().bind(FromCastNode))))))),
      ignoringImpCasts(hasType(objcObjectPointerType(
          pointee(hasDeclaration(objcInterfaceDecl().bind(ToCastNode)))))),
      unless(anyOf(
          ignoringImpCasts(hasType(
              objcObjectPointerType(pointee(hasDeclaration(objcInterfaceDecl(
                  isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
          hasSourceExpression(ignoringImpCasts(hasType(
              objcObjectPointerType(pointee(hasDeclaration(objcInterfaceDecl(
                  isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
  auto MatchExprRefTypeDefUnrelated = allOf(
      hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
          hasDeclaration(decl(cxxRecordDecl().bind(FromCastNode))))))),
      hasType(hasUnqualifiedDesugaredType(
          recordType(hasDeclaration(decl(cxxRecordDecl().bind(ToCastNode)))))),
      unless(anyOf(
          hasType(hasUnqualifiedDesugaredType(
              recordType(hasDeclaration(decl(cxxRecordDecl(
                  isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
          hasSourceExpression(hasType(hasUnqualifiedDesugaredType(
              recordType(hasDeclaration(decl(cxxRecordDecl(
                  isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));

  auto ExplicitCastUnrelated =
      explicitCastExpr(anyOf(MatchExprPtrUnrelatedTypes,
                             MatchExprPtrObjCUnrelatedTypes,
                             MatchExprRefTypeDefUnrelated))
          .bind(WarnRecordDecl);
  auto CastUnrelated = stmt(ExplicitCastUnrelated);
  auto MatchesUnrelatedTypes = match(stmt(forEachDescendant(CastUnrelated)),
                                     *D->getBody(), AM.getASTContext());
  for (BoundNodes Match : MatchesUnrelatedTypes)
    emitDiagnosticsUnrelated(Match, BR, ADC, this, BT);
}

void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
  Mgr.registerChecker<MemoryUnsafeCastChecker>();
}

bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) {
  return true;
}