Clang Project

clang_source_code/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
1//== DynamicTypeChecker.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 checker looks for cases where the dynamic type of an object is unrelated
10// to its static type. The type information utilized by this check is collected
11// by the DynamicTypePropagation checker. This check does not report any type
12// error for ObjC Generic types, in order to avoid duplicate erros from the
13// ObjC Generics checker. This checker is not supposed to modify the program
14// state, it is just the observer of the type information provided by other
15// checkers.
16//
17//===----------------------------------------------------------------------===//
18
19#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
20#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21#include "clang/StaticAnalyzer/Core/Checker.h"
22#include "clang/StaticAnalyzer/Core/CheckerManager.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
24#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
25#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
27
28using namespace clang;
29using namespace ento;
30
31namespace {
32class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
33  mutable std::unique_ptr<BugTypeBT;
34  void initBugType() const {
35    if (!BT)
36      BT.reset(
37          new BugType(this"Dynamic and static type mismatch""Type Error"));
38  }
39
40  class DynamicTypeBugVisitor : public BugReporterVisitor {
41  public:
42    DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
43
44    void Profile(llvm::FoldingSetNodeID &IDconst override {
45      static int X = 0;
46      ID.AddPointer(&X);
47      ID.AddPointer(Reg);
48    }
49
50    std::shared_ptr<PathDiagnosticPieceVisitNode(const ExplodedNode *N,
51                                                   BugReporterContext &BRC,
52                                                   BugReport &BR) override;
53
54  private:
55    // The tracked region.
56    const MemRegion *Reg;
57  };
58
59  void reportTypeError(QualType DynamicTypeQualType StaticType,
60                       const MemRegion *Regconst Stmt *ReportedNode,
61                       CheckerContext &Cconst;
62
63public:
64  void checkPostStmt(const ImplicitCastExpr *CECheckerContext &Cconst;
65};
66}
67
68void DynamicTypeChecker::reportTypeError(QualType DynamicType,
69                                         QualType StaticType,
70                                         const MemRegion *Reg,
71                                         const Stmt *ReportedNode,
72                                         CheckerContext &Cconst {
73  initBugType();
74  SmallString<192Buf;
75  llvm::raw_svector_ostream OS(Buf);
76  OS << "Object has a dynamic type '";
77  QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
78                  llvm::Twine());
79  OS << "' which is incompatible with static type '";
80  QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
81                  llvm::Twine());
82  OS << "'";
83  std::unique_ptr<BugReportR(
84      new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode()));
85  R->markInteresting(Reg);
86  R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg));
87  R->addRange(ReportedNode->getSourceRange());
88  C.emitReport(std::move(R));
89}
90
91std::shared_ptr<PathDiagnosticPiece>
92DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N,
93                                                     BugReporterContext &BRC,
94                                                     BugReport &) {
95  ProgramStateRef State = N->getState();
96  ProgramStateRef StatePrev = N->getFirstPred()->getState();
97
98  DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
99  DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
100  if (!TrackedType.isValid())
101    return nullptr;
102
103  if (TrackedTypePrev.isValid() &&
104      TrackedTypePrev.getType() == TrackedType.getType())
105    return nullptr;
106
107  // Retrieve the associated statement.
108  const Stmt *S = PathDiagnosticLocation::getStmt(N);
109  if (!S)
110    return nullptr;
111
112  const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
113
114  SmallString<256Buf;
115  llvm::raw_svector_ostream OS(Buf);
116  OS << "Type '";
117  QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
118                  LangOpts, llvm::Twine());
119  OS << "' is inferred from ";
120
121  if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
122    OS << "explicit cast (from '";
123    QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
124                    Qualifiers(), OS, LangOpts, llvm::Twine());
125    OS << "' to '";
126    QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
127                    LangOpts, llvm::Twine());
128    OS << "')";
129  } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
130    OS << "implicit cast (from '";
131    QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
132                    Qualifiers(), OS, LangOpts, llvm::Twine());
133    OS << "' to '";
134    QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
135                    LangOpts, llvm::Twine());
136    OS << "')";
137  } else {
138    OS << "this context";
139  }
140
141  // Generate the extra diagnostic.
142  PathDiagnosticLocation Pos(SBRC.getSourceManager(),
143                             N->getLocationContext());
144  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
145                                                    nullptr);
146}
147
148static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
149  const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
150  if (!Decl)
151    return false;
152
153  return Decl->getDefinition();
154}
155
156// TODO: consider checking explicit casts?
157void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
158                                       CheckerContext &Cconst {
159  // TODO: C++ support.
160  if (CE->getCastKind() != CK_BitCast)
161    return;
162
163  const MemRegion *Region = C.getSVal(CE).getAsRegion();
164  if (!Region)
165    return;
166
167  ProgramStateRef State = C.getState();
168  DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
169
170  if (!DynTypeInfo.isValid())
171    return;
172
173  QualType DynType = DynTypeInfo.getType();
174  QualType StaticType = CE->getType();
175
176  const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
177  const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
178
179  if (!DynObjCType || !StaticObjCType)
180    return;
181
182  if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
183    return;
184
185  ASTContext &ASTCtxt = C.getASTContext();
186
187  // Strip kindeofness to correctly detect subtyping relationships.
188  DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
189  StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
190
191  // Specialized objects are handled by the generics checker.
192  if (StaticObjCType->isSpecialized())
193    return;
194
195  if (ASTCtxt.canAssignObjCInterfaces(StaticObjCTypeDynObjCType))
196    return;
197
198  if (DynTypeInfo.canBeASubClass() &&
199      ASTCtxt.canAssignObjCInterfaces(DynObjCTypeStaticObjCType))
200    return;
201
202  reportTypeError(DynTypeStaticTypeRegionCEC);
203}
204
205void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
206  mgr.registerChecker<DynamicTypeChecker>();
207}
208
209bool ento::shouldRegisterDynamicTypeChecker(const LangOptions &LO) {
210  return true;
211}
212