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