1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
15 | #include "clang/AST/ExprObjC.h" |
16 | #include "clang/AST/ExprOpenMP.h" |
17 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
18 | #include "clang/StaticAnalyzer/Core/Checker.h" |
19 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
20 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" |
22 | #include "llvm/ADT/SmallString.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | |
25 | using namespace clang; |
26 | using namespace ento; |
27 | |
28 | namespace { |
29 | class DereferenceChecker |
30 | : public Checker< check::Location, |
31 | check::Bind, |
32 | EventDispatcher<ImplicitNullDerefEvent> > { |
33 | mutable std::unique_ptr<BuiltinBug> BT_null; |
34 | mutable std::unique_ptr<BuiltinBug> BT_undef; |
35 | |
36 | void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const; |
37 | |
38 | public: |
39 | void checkLocation(SVal location, bool isLoad, const Stmt* S, |
40 | CheckerContext &C) const; |
41 | void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; |
42 | |
43 | static void AddDerefSource(raw_ostream &os, |
44 | SmallVectorImpl<SourceRange> &Ranges, |
45 | const Expr *Ex, const ProgramState *state, |
46 | const LocationContext *LCtx, |
47 | bool loadedFrom = false); |
48 | }; |
49 | } |
50 | |
51 | void |
52 | DereferenceChecker::AddDerefSource(raw_ostream &os, |
53 | SmallVectorImpl<SourceRange> &Ranges, |
54 | const Expr *Ex, |
55 | const ProgramState *state, |
56 | const LocationContext *LCtx, |
57 | bool loadedFrom) { |
58 | Ex = Ex->IgnoreParenLValueCasts(); |
59 | switch (Ex->getStmtClass()) { |
60 | default: |
61 | break; |
62 | case Stmt::DeclRefExprClass: { |
63 | const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); |
64 | if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { |
65 | os << " (" << (loadedFrom ? "loaded from" : "from") |
66 | << " variable '" << VD->getName() << "')"; |
67 | Ranges.push_back(DR->getSourceRange()); |
68 | } |
69 | break; |
70 | } |
71 | case Stmt::MemberExprClass: { |
72 | const MemberExpr *ME = cast<MemberExpr>(Ex); |
73 | os << " (" << (loadedFrom ? "loaded from" : "via") |
74 | << " field '" << ME->getMemberNameInfo() << "')"; |
75 | SourceLocation L = ME->getMemberLoc(); |
76 | Ranges.push_back(SourceRange(L, L)); |
77 | break; |
78 | } |
79 | case Stmt::ObjCIvarRefExprClass: { |
80 | const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex); |
81 | os << " (" << (loadedFrom ? "loaded from" : "via") |
82 | << " ivar '" << IV->getDecl()->getName() << "')"; |
83 | SourceLocation L = IV->getLocation(); |
84 | Ranges.push_back(SourceRange(L, L)); |
85 | break; |
86 | } |
87 | } |
88 | } |
89 | |
90 | static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){ |
91 | const Expr *E = nullptr; |
92 | |
93 | |
94 | |
95 | if (const Expr *expr = dyn_cast<Expr>(S)) |
96 | E = expr->IgnoreParenLValueCasts(); |
97 | |
98 | if (IsBind) { |
99 | const VarDecl *VD; |
100 | const Expr *Init; |
101 | std::tie(VD, Init) = parseAssignment(S); |
102 | if (VD && Init) |
103 | E = Init; |
104 | } |
105 | return E; |
106 | } |
107 | |
108 | static bool suppressReport(const Expr *E) { |
109 | |
110 | return E->getType().getQualifiers().hasAddressSpace(); |
111 | } |
112 | |
113 | static bool isDeclRefExprToReference(const Expr *E) { |
114 | if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) |
115 | return DRE->getDecl()->getType()->isReferenceType(); |
116 | return false; |
117 | } |
118 | |
119 | void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, |
120 | CheckerContext &C) const { |
121 | |
122 | ExplodedNode *N = C.generateErrorNode(State); |
123 | if (!N) |
124 | return; |
125 | |
126 | |
127 | |
128 | if (!BT_null) |
129 | BT_null.reset(new BuiltinBug(this, "Dereference of null pointer")); |
130 | |
131 | SmallString<100> buf; |
132 | llvm::raw_svector_ostream os(buf); |
133 | |
134 | SmallVector<SourceRange, 2> Ranges; |
135 | |
136 | switch (S->getStmtClass()) { |
137 | case Stmt::ArraySubscriptExprClass: { |
138 | os << "Array access"; |
139 | const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); |
140 | AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), |
141 | State.get(), N->getLocationContext()); |
142 | os << " results in a null pointer dereference"; |
143 | break; |
144 | } |
145 | case Stmt::OMPArraySectionExprClass: { |
146 | os << "Array access"; |
147 | const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S); |
148 | AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), |
149 | State.get(), N->getLocationContext()); |
150 | os << " results in a null pointer dereference"; |
151 | break; |
152 | } |
153 | case Stmt::UnaryOperatorClass: { |
154 | os << "Dereference of null pointer"; |
155 | const UnaryOperator *U = cast<UnaryOperator>(S); |
156 | AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), |
157 | State.get(), N->getLocationContext(), true); |
158 | break; |
159 | } |
160 | case Stmt::MemberExprClass: { |
161 | const MemberExpr *M = cast<MemberExpr>(S); |
162 | if (M->isArrow() || isDeclRefExprToReference(M->getBase())) { |
163 | os << "Access to field '" << M->getMemberNameInfo() |
164 | << "' results in a dereference of a null pointer"; |
165 | AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), |
166 | State.get(), N->getLocationContext(), true); |
167 | } |
168 | break; |
169 | } |
170 | case Stmt::ObjCIvarRefExprClass: { |
171 | const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); |
172 | os << "Access to instance variable '" << *IV->getDecl() |
173 | << "' results in a dereference of a null pointer"; |
174 | AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), |
175 | State.get(), N->getLocationContext(), true); |
176 | break; |
177 | } |
178 | default: |
179 | break; |
180 | } |
181 | |
182 | auto report = llvm::make_unique<BugReport>( |
183 | *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N); |
184 | |
185 | bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); |
186 | |
187 | for (SmallVectorImpl<SourceRange>::iterator |
188 | I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) |
189 | report->addRange(*I); |
190 | |
191 | C.emitReport(std::move(report)); |
192 | } |
193 | |
194 | void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, |
195 | CheckerContext &C) const { |
196 | |
197 | if (l.isUndef()) { |
198 | if (ExplodedNode *N = C.generateErrorNode()) { |
199 | if (!BT_undef) |
200 | BT_undef.reset( |
201 | new BuiltinBug(this, "Dereference of undefined pointer value")); |
202 | |
203 | auto report = |
204 | llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N); |
205 | bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); |
206 | C.emitReport(std::move(report)); |
207 | } |
208 | return; |
209 | } |
210 | |
211 | DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); |
212 | |
213 | |
214 | if (!location.getAs<Loc>()) |
215 | return; |
216 | |
217 | ProgramStateRef state = C.getState(); |
218 | |
219 | ProgramStateRef notNullState, nullState; |
220 | std::tie(notNullState, nullState) = state->assume(location); |
221 | |
222 | |
223 | if (nullState) { |
224 | if (!notNullState) { |
225 | const Expr *expr = getDereferenceExpr(S); |
226 | if (!suppressReport(expr)) { |
227 | reportBug(nullState, expr, C); |
228 | return; |
229 | } |
230 | } |
231 | |
232 | |
233 | |
234 | |
235 | if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { |
236 | ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), |
237 | }; |
238 | dispatchEvent(event); |
239 | } |
240 | } |
241 | |
242 | |
243 | C.addTransition(notNullState); |
244 | } |
245 | |
246 | void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, |
247 | CheckerContext &C) const { |
248 | |
249 | if (V.isUndef()) |
250 | return; |
251 | |
252 | const MemRegion *MR = L.getAsRegion(); |
253 | const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); |
254 | if (!TVR) |
255 | return; |
256 | |
257 | if (!TVR->getValueType()->isReferenceType()) |
258 | return; |
259 | |
260 | ProgramStateRef State = C.getState(); |
261 | |
262 | ProgramStateRef StNonNull, StNull; |
263 | std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); |
264 | |
265 | if (StNull) { |
266 | if (!StNonNull) { |
267 | const Expr *expr = getDereferenceExpr(S, ); |
268 | if (!suppressReport(expr)) { |
269 | reportBug(StNull, expr, C); |
270 | return; |
271 | } |
272 | } |
273 | |
274 | |
275 | |
276 | if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { |
277 | ImplicitNullDerefEvent event = {V, , N, |
278 | &C.getBugReporter(), |
279 | }; |
280 | dispatchEvent(event); |
281 | } |
282 | } |
283 | |
284 | |
285 | |
286 | |
287 | |
288 | |
289 | |
290 | |
291 | |
292 | |
293 | |
294 | |
295 | |
296 | |
297 | |
298 | |
299 | |
300 | C.addTransition(State, this); |
301 | } |
302 | |
303 | void ento::registerDereferenceChecker(CheckerManager &mgr) { |
304 | mgr.registerChecker<DereferenceChecker>(); |
305 | } |
306 | |
307 | bool ento::shouldRegisterDereferenceChecker(const LangOptions &LO) { |
308 | return true; |
309 | } |
310 | |