/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- IteratorRangeChecker.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 | | // Defines a checker for dereference of the past-the-end iterator and |
10 | | // out-of-range increments and decrements. |
11 | | // |
12 | | //===----------------------------------------------------------------------===// |
13 | | |
14 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
15 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
16 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
17 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
18 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
20 | | |
21 | | #include "Iterator.h" |
22 | | |
23 | | using namespace clang; |
24 | | using namespace ento; |
25 | | using namespace iterator; |
26 | | |
27 | | namespace { |
28 | | |
29 | | class IteratorRangeChecker |
30 | | : public Checker<check::PreCall, check::PreStmt<UnaryOperator>, |
31 | | check::PreStmt<BinaryOperator>, |
32 | | check::PreStmt<ArraySubscriptExpr>, |
33 | | check::PreStmt<MemberExpr>> { |
34 | | |
35 | | std::unique_ptr<BugType> OutOfRangeBugType; |
36 | | |
37 | | void verifyDereference(CheckerContext &C, SVal Val) const; |
38 | | void verifyIncrement(CheckerContext &C, SVal Iter) const; |
39 | | void verifyDecrement(CheckerContext &C, SVal Iter) const; |
40 | | void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op, |
41 | | SVal LHS, SVal RHS) const; |
42 | | void verifyAdvance(CheckerContext &C, SVal LHS, SVal RHS) const; |
43 | | void verifyPrev(CheckerContext &C, SVal LHS, SVal RHS) const; |
44 | | void verifyNext(CheckerContext &C, SVal LHS, SVal RHS) const; |
45 | | void reportBug(const StringRef &Message, SVal Val, CheckerContext &C, |
46 | | ExplodedNode *ErrNode) const; |
47 | | |
48 | | public: |
49 | | IteratorRangeChecker(); |
50 | | |
51 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
52 | | void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const; |
53 | | void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const; |
54 | | void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const; |
55 | | void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const; |
56 | | |
57 | | using AdvanceFn = void (IteratorRangeChecker::*)(CheckerContext &, SVal, |
58 | | SVal) const; |
59 | | |
60 | | CallDescriptionMap<AdvanceFn> AdvanceFunctions = { |
61 | | {{{"std", "advance"}, 2}, &IteratorRangeChecker::verifyAdvance}, |
62 | | {{{"std", "prev"}, 2}, &IteratorRangeChecker::verifyPrev}, |
63 | | {{{"std", "next"}, 2}, &IteratorRangeChecker::verifyNext}, |
64 | | }; |
65 | | }; |
66 | | |
67 | | bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos); |
68 | | bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos); |
69 | | bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos); |
70 | | bool isZero(ProgramStateRef State, const NonLoc &Val); |
71 | | |
72 | | } //namespace |
73 | | |
74 | 2 | IteratorRangeChecker::IteratorRangeChecker() { |
75 | 2 | OutOfRangeBugType.reset( |
76 | 2 | new BugType(this, "Iterator out of range", "Misuse of STL APIs")); |
77 | 2 | } |
78 | | |
79 | | void IteratorRangeChecker::checkPreCall(const CallEvent &Call, |
80 | 1.77k | CheckerContext &C) const { |
81 | | // Check for out of range access |
82 | 1.77k | const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); |
83 | 1.77k | if (!Func) |
84 | 0 | return; |
85 | | |
86 | 1.77k | if (Func->isOverloadedOperator()) { |
87 | 374 | if (isIncrementOperator(Func->getOverloadedOperator())) { |
88 | | // Check for out-of-range incrementions |
89 | 82 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
90 | 82 | verifyIncrement(C, InstCall->getCXXThisVal()); |
91 | 82 | } else { |
92 | 0 | if (Call.getNumArgs() >= 1) { |
93 | 0 | verifyIncrement(C, Call.getArgSVal(0)); |
94 | 0 | } |
95 | 0 | } |
96 | 292 | } else if (isDecrementOperator(Func->getOverloadedOperator())) { |
97 | | // Check for out-of-range decrementions |
98 | 84 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
99 | 84 | verifyDecrement(C, InstCall->getCXXThisVal()); |
100 | 84 | } else { |
101 | 0 | if (Call.getNumArgs() >= 1) { |
102 | 0 | verifyDecrement(C, Call.getArgSVal(0)); |
103 | 0 | } |
104 | 0 | } |
105 | 208 | } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { |
106 | 162 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
107 | | // Check for out-of-range incrementions and decrementions |
108 | 162 | if (Call.getNumArgs() >= 1 && |
109 | 162 | Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { |
110 | 162 | verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), |
111 | 162 | InstCall->getCXXThisVal(), |
112 | 162 | Call.getArgSVal(0)); |
113 | 162 | } |
114 | 162 | } else { |
115 | 0 | if (Call.getNumArgs() >= 2 && |
116 | 0 | Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { |
117 | 0 | verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), |
118 | 0 | Call.getArgSVal(0), Call.getArgSVal(1)); |
119 | 0 | } |
120 | 0 | } |
121 | 162 | } else if (46 isDereferenceOperator(Func->getOverloadedOperator())46 ) { |
122 | | // Check for dereference of out-of-range iterators |
123 | 46 | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
124 | 46 | verifyDereference(C, InstCall->getCXXThisVal()); |
125 | 46 | } else { |
126 | 0 | verifyDereference(C, Call.getArgSVal(0)); |
127 | 0 | } |
128 | 46 | } |
129 | 1.40k | } else { |
130 | 1.40k | const AdvanceFn *Verifier = AdvanceFunctions.lookup(Call); |
131 | 1.40k | if (Verifier) { |
132 | 230 | if (Call.getNumArgs() > 1) { |
133 | 230 | (this->**Verifier)(C, Call.getArgSVal(0), Call.getArgSVal(1)); |
134 | 230 | } else { |
135 | 0 | auto &BVF = C.getSValBuilder().getBasicValueFactory(); |
136 | 0 | (this->**Verifier)( |
137 | 0 | C, Call.getArgSVal(0), |
138 | 0 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); |
139 | 0 | } |
140 | 230 | } |
141 | 1.40k | } |
142 | 1.77k | } |
143 | | |
144 | | void IteratorRangeChecker::checkPreStmt(const UnaryOperator *UO, |
145 | 294 | CheckerContext &C) const { |
146 | 294 | if (isa<CXXThisExpr>(UO->getSubExpr())) |
147 | 79 | return; |
148 | | |
149 | 215 | ProgramStateRef State = C.getState(); |
150 | 215 | UnaryOperatorKind OK = UO->getOpcode(); |
151 | 215 | SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext()); |
152 | | |
153 | 215 | if (isDereferenceOperator(OK)) { |
154 | 18 | verifyDereference(C, SubVal); |
155 | 197 | } else if (isIncrementOperator(OK)) { |
156 | 43 | verifyIncrement(C, SubVal); |
157 | 154 | } else if (isDecrementOperator(OK)) { |
158 | 44 | verifyDecrement(C, SubVal); |
159 | 44 | } |
160 | 215 | } |
161 | | |
162 | | void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO, |
163 | 99 | CheckerContext &C) const { |
164 | 99 | ProgramStateRef State = C.getState(); |
165 | 99 | BinaryOperatorKind OK = BO->getOpcode(); |
166 | 99 | SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); |
167 | | |
168 | 99 | if (isDereferenceOperator(OK)) { |
169 | 2 | verifyDereference(C, LVal); |
170 | 97 | } else if (isRandomIncrOrDecrOperator(OK)) { |
171 | 97 | SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); |
172 | 97 | if (!BO->getRHS()->getType()->isIntegralOrEnumerationType()) |
173 | 2 | return; |
174 | 95 | verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal, |
175 | 95 | RVal); |
176 | 95 | } |
177 | 99 | } |
178 | | |
179 | | void IteratorRangeChecker::checkPreStmt(const ArraySubscriptExpr *ASE, |
180 | 2 | CheckerContext &C) const { |
181 | 2 | ProgramStateRef State = C.getState(); |
182 | 2 | SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext()); |
183 | 2 | verifyDereference(C, LVal); |
184 | 2 | } |
185 | | |
186 | | void IteratorRangeChecker::checkPreStmt(const MemberExpr *ME, |
187 | 634 | CheckerContext &C) const { |
188 | 634 | if (!ME->isArrow() || ME->isImplicitAccess()317 ) |
189 | 630 | return; |
190 | | |
191 | 4 | ProgramStateRef State = C.getState(); |
192 | 4 | SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext()); |
193 | 4 | verifyDereference(C, BaseVal); |
194 | 4 | } |
195 | | |
196 | | void IteratorRangeChecker::verifyDereference(CheckerContext &C, |
197 | 72 | SVal Val) const { |
198 | 72 | auto State = C.getState(); |
199 | 72 | const auto *Pos = getIteratorPosition(State, Val); |
200 | 72 | if (Pos && isPastTheEnd(State, *Pos)54 ) { |
201 | 20 | auto *N = C.generateErrorNode(State); |
202 | 20 | if (!N) |
203 | 0 | return; |
204 | 20 | reportBug("Past-the-end iterator dereferenced.", Val, C, N); |
205 | 20 | return; |
206 | 20 | } |
207 | 72 | } |
208 | | |
209 | 125 | void IteratorRangeChecker::verifyIncrement(CheckerContext &C, SVal Iter) const { |
210 | 125 | auto &BVF = C.getSValBuilder().getBasicValueFactory(); |
211 | 125 | verifyRandomIncrOrDecr(C, OO_Plus, Iter, |
212 | 125 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); |
213 | 125 | } |
214 | | |
215 | 128 | void IteratorRangeChecker::verifyDecrement(CheckerContext &C, SVal Iter) const { |
216 | 128 | auto &BVF = C.getSValBuilder().getBasicValueFactory(); |
217 | 128 | verifyRandomIncrOrDecr(C, OO_Minus, Iter, |
218 | 128 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); |
219 | 128 | } |
220 | | |
221 | | void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C, |
222 | | OverloadedOperatorKind Op, |
223 | 740 | SVal LHS, SVal RHS) const { |
224 | 740 | auto State = C.getState(); |
225 | | |
226 | 740 | auto Value = RHS; |
227 | 740 | if (auto ValAsLoc = RHS.getAs<Loc>()) { |
228 | 4 | Value = State->getRawSVal(*ValAsLoc); |
229 | 4 | } |
230 | | |
231 | 740 | if (Value.isUnknownOrUndef() || !isa<NonLoc>(Value)738 ) |
232 | 4 | return; |
233 | | |
234 | | // Incremention or decremention by 0 is never a bug. |
235 | 736 | if (isZero(State, Value.castAs<NonLoc>())) |
236 | 99 | return; |
237 | | |
238 | | // The result may be the past-end iterator of the container, but any other |
239 | | // out of range position is undefined behaviour |
240 | 637 | auto StateAfter = advancePosition(State, LHS, Op, Value); |
241 | 637 | if (!StateAfter) |
242 | 147 | return; |
243 | | |
244 | 490 | const auto *PosAfter = getIteratorPosition(StateAfter, LHS); |
245 | 490 | assert(PosAfter && |
246 | 490 | "Iterator should have position after successful advancement"); |
247 | 490 | if (isAheadOfRange(State, *PosAfter)) { |
248 | 38 | auto *N = C.generateErrorNode(State); |
249 | 38 | if (!N) |
250 | 0 | return; |
251 | 38 | reportBug("Iterator decremented ahead of its valid range.", LHS, |
252 | 38 | C, N); |
253 | 38 | } |
254 | 490 | if (isBehindPastTheEnd(State, *PosAfter)) { |
255 | 38 | auto *N = C.generateErrorNode(State); |
256 | 38 | if (!N) |
257 | 0 | return; |
258 | 38 | reportBug("Iterator incremented behind the past-the-end " |
259 | 38 | "iterator.", LHS, C, N); |
260 | 38 | } |
261 | 490 | } |
262 | | |
263 | | void IteratorRangeChecker::verifyAdvance(CheckerContext &C, SVal LHS, |
264 | 126 | SVal RHS) const { |
265 | 126 | verifyRandomIncrOrDecr(C, OO_PlusEqual, LHS, RHS); |
266 | 126 | } |
267 | | |
268 | | void IteratorRangeChecker::verifyPrev(CheckerContext &C, SVal LHS, |
269 | 52 | SVal RHS) const { |
270 | 52 | verifyRandomIncrOrDecr(C, OO_Minus, LHS, RHS); |
271 | 52 | } |
272 | | |
273 | | void IteratorRangeChecker::verifyNext(CheckerContext &C, SVal LHS, |
274 | 52 | SVal RHS) const { |
275 | 52 | verifyRandomIncrOrDecr(C, OO_Plus, LHS, RHS); |
276 | 52 | } |
277 | | |
278 | | void IteratorRangeChecker::reportBug(const StringRef &Message, SVal Val, |
279 | | CheckerContext &C, |
280 | 96 | ExplodedNode *ErrNode) const { |
281 | 96 | auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message, |
282 | 96 | ErrNode); |
283 | | |
284 | 96 | const auto *Pos = getIteratorPosition(C.getState(), Val); |
285 | 96 | assert(Pos && "Iterator without known position cannot be out-of-range."); |
286 | | |
287 | 96 | R->markInteresting(Val); |
288 | 96 | R->markInteresting(Pos->getContainer()); |
289 | 96 | C.emitReport(std::move(R)); |
290 | 96 | } |
291 | | |
292 | | namespace { |
293 | | |
294 | | bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); |
295 | | bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); |
296 | | bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); |
297 | | |
298 | 736 | bool isZero(ProgramStateRef State, const NonLoc &Val) { |
299 | 736 | auto &BVF = State->getBasicVals(); |
300 | 736 | return compare(State, Val, |
301 | 736 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))), |
302 | 736 | BO_EQ); |
303 | 736 | } |
304 | | |
305 | 54 | bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) { |
306 | 54 | const auto *Cont = Pos.getContainer(); |
307 | 54 | const auto *CData = getContainerData(State, Cont); |
308 | 54 | if (!CData) |
309 | 0 | return false; |
310 | | |
311 | 54 | const auto End = CData->getEnd(); |
312 | 54 | if (End) { |
313 | 28 | if (isEqual(State, Pos.getOffset(), End)) { |
314 | 20 | return true; |
315 | 20 | } |
316 | 28 | } |
317 | | |
318 | 34 | return false; |
319 | 54 | } |
320 | | |
321 | 490 | bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) { |
322 | 490 | const auto *Cont = Pos.getContainer(); |
323 | 490 | const auto *CData = getContainerData(State, Cont); |
324 | 490 | if (!CData) |
325 | 0 | return false; |
326 | | |
327 | 490 | const auto Beg = CData->getBegin(); |
328 | 490 | if (Beg) { |
329 | 284 | if (isLess(State, Pos.getOffset(), Beg)) { |
330 | 38 | return true; |
331 | 38 | } |
332 | 284 | } |
333 | | |
334 | 452 | return false; |
335 | 490 | } |
336 | | |
337 | 490 | bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) { |
338 | 490 | const auto *Cont = Pos.getContainer(); |
339 | 490 | const auto *CData = getContainerData(State, Cont); |
340 | 490 | if (!CData) |
341 | 0 | return false; |
342 | | |
343 | 490 | const auto End = CData->getEnd(); |
344 | 490 | if (End) { |
345 | 206 | if (isGreater(State, Pos.getOffset(), End)) { |
346 | 38 | return true; |
347 | 38 | } |
348 | 206 | } |
349 | | |
350 | 452 | return false; |
351 | 490 | } |
352 | | |
353 | 284 | bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { |
354 | 284 | return compare(State, Sym1, Sym2, BO_LT); |
355 | 284 | } |
356 | | |
357 | 206 | bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { |
358 | 206 | return compare(State, Sym1, Sym2, BO_GT); |
359 | 206 | } |
360 | | |
361 | 28 | bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { |
362 | 28 | return compare(State, Sym1, Sym2, BO_EQ); |
363 | 28 | } |
364 | | |
365 | | } // namespace |
366 | | |
367 | 2 | void ento::registerIteratorRangeChecker(CheckerManager &mgr) { |
368 | 2 | mgr.registerChecker<IteratorRangeChecker>(); |
369 | 2 | } |
370 | | |
371 | 6 | bool ento::shouldRegisterIteratorRangeChecker(const CheckerManager &mgr) { |
372 | 6 | return true; |
373 | 6 | } |