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