• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
29 using namespace clang;
30 using namespace ento;
31 
32 namespace {
33 class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
34   mutable std::unique_ptr<BugType> BT;
initBugType() const35   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:
DynamicTypeBugVisitor(const MemRegion * Reg)44     DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
45 
Profile(llvm::FoldingSetNodeID & ID) const46     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 
66 public:
67   void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
68 };
69 }
70 
reportTypeError(QualType DynamicType,QualType StaticType,const MemRegion * Reg,const Stmt * ReportedNode,CheckerContext & C) const71 void 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 
VisitNode(const ExplodedNode * N,const ExplodedNode * PrevN,BugReporterContext & BRC,BugReport & BR)94 PathDiagnosticPiece *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 
hasDefinition(const ObjCObjectPointerType * ObjPtr)154 static 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?
checkPostStmt(const ImplicitCastExpr * CE,CheckerContext & C) const163 void 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 
registerDynamicTypeChecker(CheckerManager & mgr)211 void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
212   mgr.registerChecker<DynamicTypeChecker>();
213 }
214