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/DynamicType.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<BugType> BT;
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 &ID) const override {
45      static int X = 0;
46      ID.AddPointer(&X);
47      ID.AddPointer(Reg);
48    }
49
50    PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
51                                     BugReporterContext &BRC,
52                                     PathSensitiveBugReport &BR) override;
53
54  private:
55    // The tracked region.
56    const MemRegion *Reg;
57  };
58
59  void reportTypeError(QualType DynamicType, QualType StaticType,
60                       const MemRegion *Reg, const Stmt *ReportedNode,
61                       CheckerContext &C) const;
62
63public:
64  void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
65};
66}
67
68void DynamicTypeChecker::reportTypeError(QualType DynamicType,
69                                         QualType StaticType,
70                                         const MemRegion *Reg,
71                                         const Stmt *ReportedNode,
72                                         CheckerContext &C) const {
73  initBugType();
74  SmallString<192> Buf;
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  auto R = std::make_unique<PathSensitiveBugReport>(
84      *BT, OS.str(), C.generateNonFatalErrorNode());
85  R->markInteresting(Reg);
86  R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg));
87  R->addRange(ReportedNode->getSourceRange());
88  C.emitReport(std::move(R));
89}
90
91PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
92    const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
93  ProgramStateRef State = N->getState();
94  ProgramStateRef StatePrev = N->getFirstPred()->getState();
95
96  DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
97  DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
98  if (!TrackedType.isValid())
99    return nullptr;
100
101  if (TrackedTypePrev.isValid() &&
102      TrackedTypePrev.getType() == TrackedType.getType())
103    return nullptr;
104
105  // Retrieve the associated statement.
106  const Stmt *S = N->getStmtForDiagnostics();
107  if (!S)
108    return nullptr;
109
110  const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
111
112  SmallString<256> Buf;
113  llvm::raw_svector_ostream OS(Buf);
114  OS << "Type '";
115  QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
116                  LangOpts, llvm::Twine());
117  OS << "' is inferred from ";
118
119  if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
120    OS << "explicit cast (from '";
121    QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
122                    Qualifiers(), OS, LangOpts, llvm::Twine());
123    OS << "' to '";
124    QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
125                    LangOpts, llvm::Twine());
126    OS << "')";
127  } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
128    OS << "implicit cast (from '";
129    QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
130                    Qualifiers(), OS, LangOpts, llvm::Twine());
131    OS << "' to '";
132    QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
133                    LangOpts, llvm::Twine());
134    OS << "')";
135  } else {
136    OS << "this context";
137  }
138
139  // Generate the extra diagnostic.
140  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
141                             N->getLocationContext());
142  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
143}
144
145static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
146  const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
147  if (!Decl)
148    return false;
149
150  return Decl->getDefinition();
151}
152
153// TODO: consider checking explicit casts?
154void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
155                                       CheckerContext &C) const {
156  // TODO: C++ support.
157  if (CE->getCastKind() != CK_BitCast)
158    return;
159
160  const MemRegion *Region = C.getSVal(CE).getAsRegion();
161  if (!Region)
162    return;
163
164  ProgramStateRef State = C.getState();
165  DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
166
167  if (!DynTypeInfo.isValid())
168    return;
169
170  QualType DynType = DynTypeInfo.getType();
171  QualType StaticType = CE->getType();
172
173  const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
174  const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
175
176  if (!DynObjCType || !StaticObjCType)
177    return;
178
179  if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
180    return;
181
182  ASTContext &ASTCtxt = C.getASTContext();
183
184  // Strip kindeofness to correctly detect subtyping relationships.
185  DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
186  StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
187
188  // Specialized objects are handled by the generics checker.
189  if (StaticObjCType->isSpecialized())
190    return;
191
192  if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
193    return;
194
195  if (DynTypeInfo.canBeASubClass() &&
196      ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
197    return;
198
199  reportTypeError(DynType, StaticType, Region, CE, C);
200}
201
202void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
203  mgr.registerChecker<DynamicTypeChecker>();
204}
205
206bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) {
207  return true;
208}
209