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