1 //===-- IteratorRangeChecker.cpp ----------------------------------*- C++ -*--//
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
7 //===----------------------------------------------------------------------===//
9 // Defines a checker for dereference of the past-the-end iterator and
10 // out-of-range increments and decrements.
12 //===----------------------------------------------------------------------===//
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/CallEvent.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23 using namespace clang;
25 using namespace iterator;
29 class IteratorRangeChecker
30 : public Checker<check::PreCall, check::PreStmt<UnaryOperator>,
31 check::PreStmt<BinaryOperator>,
32 check::PreStmt<ArraySubscriptExpr>,
33 check::PreStmt<MemberExpr>> {
35 std::unique_ptr<BugType> OutOfRangeBugType;
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;
49 IteratorRangeChecker();
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;
57 using AdvanceFn = void (IteratorRangeChecker::*)(CheckerContext &, SVal,
60 CallDescriptionMap<AdvanceFn> AdvanceFunctions = {
61 {{{"std", "advance"}, 2}, &IteratorRangeChecker::verifyAdvance},
62 {{{"std", "prev"}, 2}, &IteratorRangeChecker::verifyPrev},
63 {{{"std", "next"}, 2}, &IteratorRangeChecker::verifyNext},
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);
74 IteratorRangeChecker::IteratorRangeChecker() {
75 OutOfRangeBugType.reset(
76 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
79 void IteratorRangeChecker::checkPreCall(const CallEvent &Call,
80 CheckerContext &C) const {
81 // Check for out of range access
82 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
86 if (Func->isOverloadedOperator()) {
87 if (isIncrementOperator(Func->getOverloadedOperator())) {
88 // Check for out-of-range incrementions
89 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
90 verifyIncrement(C, InstCall->getCXXThisVal());
92 if (Call.getNumArgs() >= 1) {
93 verifyIncrement(C, Call.getArgSVal(0));
96 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
97 // Check for out-of-range decrementions
98 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
99 verifyDecrement(C, InstCall->getCXXThisVal());
101 if (Call.getNumArgs() >= 1) {
102 verifyDecrement(C, Call.getArgSVal(0));
105 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
106 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
107 // Check for out-of-range incrementions and decrementions
108 if (Call.getNumArgs() >= 1 &&
109 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
110 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
111 InstCall->getCXXThisVal(),
115 if (Call.getNumArgs() >= 2 &&
116 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
117 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
118 Call.getArgSVal(0), Call.getArgSVal(1));
121 } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
122 // Check for dereference of out-of-range iterators
123 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
124 verifyDereference(C, InstCall->getCXXThisVal());
126 verifyDereference(C, Call.getArgSVal(0));
130 const AdvanceFn *Verifier = AdvanceFunctions.lookup(Call);
132 if (Call.getNumArgs() > 1) {
133 (this->**Verifier)(C, Call.getArgSVal(0), Call.getArgSVal(1));
135 auto &BVF = C.getSValBuilder().getBasicValueFactory();
137 C, Call.getArgSVal(0),
138 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
144 void IteratorRangeChecker::checkPreStmt(const UnaryOperator *UO,
145 CheckerContext &C) const {
146 if (isa<CXXThisExpr>(UO->getSubExpr()))
149 ProgramStateRef State = C.getState();
150 UnaryOperatorKind OK = UO->getOpcode();
151 SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext());
153 if (isDereferenceOperator(OK)) {
154 verifyDereference(C, SubVal);
155 } else if (isIncrementOperator(OK)) {
156 verifyIncrement(C, SubVal);
157 } else if (isDecrementOperator(OK)) {
158 verifyDecrement(C, SubVal);
162 void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO,
163 CheckerContext &C) const {
164 ProgramStateRef State = C.getState();
165 BinaryOperatorKind OK = BO->getOpcode();
166 SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
168 if (isDereferenceOperator(OK)) {
169 verifyDereference(C, LVal);
170 } else if (isRandomIncrOrDecrOperator(OK)) {
171 SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
172 verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal,
177 void IteratorRangeChecker::checkPreStmt(const ArraySubscriptExpr *ASE,
178 CheckerContext &C) const {
179 ProgramStateRef State = C.getState();
180 SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext());
181 verifyDereference(C, LVal);
184 void IteratorRangeChecker::checkPreStmt(const MemberExpr *ME,
185 CheckerContext &C) const {
186 if (!ME->isArrow() || ME->isImplicitAccess())
189 ProgramStateRef State = C.getState();
190 SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext());
191 verifyDereference(C, BaseVal);
194 void IteratorRangeChecker::verifyDereference(CheckerContext &C,
196 auto State = C.getState();
197 const auto *Pos = getIteratorPosition(State, Val);
198 if (Pos && isPastTheEnd(State, *Pos)) {
199 auto *N = C.generateErrorNode(State);
202 reportBug("Past-the-end iterator dereferenced.", Val, C, N);
207 void IteratorRangeChecker::verifyIncrement(CheckerContext &C, SVal Iter) const {
208 auto &BVF = C.getSValBuilder().getBasicValueFactory();
209 verifyRandomIncrOrDecr(C, OO_Plus, Iter,
210 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
213 void IteratorRangeChecker::verifyDecrement(CheckerContext &C, SVal Iter) const {
214 auto &BVF = C.getSValBuilder().getBasicValueFactory();
215 verifyRandomIncrOrDecr(C, OO_Minus, Iter,
216 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
219 void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
220 OverloadedOperatorKind Op,
221 SVal LHS, SVal RHS) const {
222 auto State = C.getState();
225 if (auto ValAsLoc = RHS.getAs<Loc>()) {
226 Value = State->getRawSVal(*ValAsLoc);
229 if (Value.isUnknown())
232 // Incremention or decremention by 0 is never a bug.
233 if (isZero(State, Value.castAs<NonLoc>()))
236 // The result may be the past-end iterator of the container, but any other
237 // out of range position is undefined behaviour
238 auto StateAfter = advancePosition(State, LHS, Op, Value);
242 const auto *PosAfter = getIteratorPosition(StateAfter, LHS);
244 "Iterator should have position after successful advancement");
245 if (isAheadOfRange(State, *PosAfter)) {
246 auto *N = C.generateErrorNode(State);
249 reportBug("Iterator decremented ahead of its valid range.", LHS,
252 if (isBehindPastTheEnd(State, *PosAfter)) {
253 auto *N = C.generateErrorNode(State);
256 reportBug("Iterator incremented behind the past-the-end "
257 "iterator.", LHS, C, N);
261 void IteratorRangeChecker::verifyAdvance(CheckerContext &C, SVal LHS,
263 verifyRandomIncrOrDecr(C, OO_PlusEqual, LHS, RHS);
266 void IteratorRangeChecker::verifyPrev(CheckerContext &C, SVal LHS,
268 verifyRandomIncrOrDecr(C, OO_Minus, LHS, RHS);
271 void IteratorRangeChecker::verifyNext(CheckerContext &C, SVal LHS,
273 verifyRandomIncrOrDecr(C, OO_Plus, LHS, RHS);
276 void IteratorRangeChecker::reportBug(const StringRef &Message, SVal Val,
278 ExplodedNode *ErrNode) const {
279 auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
282 const auto *Pos = getIteratorPosition(C.getState(), Val);
283 assert(Pos && "Iterator without known position cannot be out-of-range.");
285 R->markInteresting(Val);
286 R->markInteresting(Pos->getContainer());
287 C.emitReport(std::move(R));
292 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
293 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
294 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
296 bool isZero(ProgramStateRef State, const NonLoc &Val) {
297 auto &BVF = State->getBasicVals();
298 return compare(State, Val,
299 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
303 bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
304 const auto *Cont = Pos.getContainer();
305 const auto *CData = getContainerData(State, Cont);
309 const auto End = CData->getEnd();
311 if (isEqual(State, Pos.getOffset(), End)) {
319 bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
320 const auto *Cont = Pos.getContainer();
321 const auto *CData = getContainerData(State, Cont);
325 const auto Beg = CData->getBegin();
327 if (isLess(State, Pos.getOffset(), Beg)) {
335 bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
336 const auto *Cont = Pos.getContainer();
337 const auto *CData = getContainerData(State, Cont);
341 const auto End = CData->getEnd();
343 if (isGreater(State, Pos.getOffset(), End)) {
351 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
352 return compare(State, Sym1, Sym2, BO_LT);
355 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
356 return compare(State, Sym1, Sym2, BO_GT);
359 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
360 return compare(State, Sym1, Sym2, BO_EQ);
365 void ento::registerIteratorRangeChecker(CheckerManager &mgr) {
366 mgr.registerChecker<IteratorRangeChecker>();
369 bool ento::shouldRegisterIteratorRangeChecker(const CheckerManager &mgr) {