1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
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 | |
28 | using namespace clang; |
29 | using namespace ento; |
30 | |
31 | namespace { |
32 | class 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 | std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, |
51 | BugReporterContext &BRC, |
52 | BugReport &BR) override; |
53 | |
54 | private: |
55 | |
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 | |
63 | public: |
64 | void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const; |
65 | }; |
66 | } |
67 | |
68 | void 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 | std::unique_ptr<BugReport> R( |
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 | |
91 | std::shared_ptr<PathDiagnosticPiece> |
92 | DynamicTypeChecker::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 | |
108 | const Stmt *S = PathDiagnosticLocation::getStmt(N); |
109 | if (!S) |
110 | return nullptr; |
111 | |
112 | const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); |
113 | |
114 | SmallString<256> Buf; |
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 | |
142 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
143 | N->getLocationContext()); |
144 | return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, |
145 | nullptr); |
146 | } |
147 | |
148 | static 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 | |
157 | void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE, |
158 | CheckerContext &C) const { |
159 | |
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 | |
188 | DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); |
189 | StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); |
190 | |
191 | |
192 | if (StaticObjCType->isSpecialized()) |
193 | return; |
194 | |
195 | if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType)) |
196 | return; |
197 | |
198 | if (DynTypeInfo.canBeASubClass() && |
199 | ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType)) |
200 | return; |
201 | |
202 | reportTypeError(DynType, StaticType, Region, CE, C); |
203 | } |
204 | |
205 | void ento::registerDynamicTypeChecker(CheckerManager &mgr) { |
206 | mgr.registerChecker<DynamicTypeChecker>(); |
207 | } |
208 | |
209 | bool ento::shouldRegisterDynamicTypeChecker(const LangOptions &LO) { |
210 | return true; |
211 | } |
212 | |