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/CallDescription.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19 #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 if (!BO
->getRHS()->getType()->isIntegralOrEnumerationType())
174 verifyRandomIncrOrDecr(C
, BinaryOperator::getOverloadedOperator(OK
), LVal
,
179 void IteratorRangeChecker::checkPreStmt(const ArraySubscriptExpr
*ASE
,
180 CheckerContext
&C
) const {
181 ProgramStateRef State
= C
.getState();
182 SVal LVal
= State
->getSVal(ASE
->getLHS(), C
.getLocationContext());
183 verifyDereference(C
, LVal
);
186 void IteratorRangeChecker::checkPreStmt(const MemberExpr
*ME
,
187 CheckerContext
&C
) const {
188 if (!ME
->isArrow() || ME
->isImplicitAccess())
191 ProgramStateRef State
= C
.getState();
192 SVal BaseVal
= State
->getSVal(ME
->getBase(), C
.getLocationContext());
193 verifyDereference(C
, BaseVal
);
196 void IteratorRangeChecker::verifyDereference(CheckerContext
&C
,
198 auto State
= C
.getState();
199 const auto *Pos
= getIteratorPosition(State
, Val
);
200 if (Pos
&& isPastTheEnd(State
, *Pos
)) {
201 auto *N
= C
.generateErrorNode(State
);
204 reportBug("Past-the-end iterator dereferenced.", Val
, C
, N
);
209 void IteratorRangeChecker::verifyIncrement(CheckerContext
&C
, SVal Iter
) const {
210 auto &BVF
= C
.getSValBuilder().getBasicValueFactory();
211 verifyRandomIncrOrDecr(C
, OO_Plus
, Iter
,
212 nonloc::ConcreteInt(BVF
.getValue(llvm::APSInt::get(1))));
215 void IteratorRangeChecker::verifyDecrement(CheckerContext
&C
, SVal Iter
) const {
216 auto &BVF
= C
.getSValBuilder().getBasicValueFactory();
217 verifyRandomIncrOrDecr(C
, OO_Minus
, Iter
,
218 nonloc::ConcreteInt(BVF
.getValue(llvm::APSInt::get(1))));
221 void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext
&C
,
222 OverloadedOperatorKind Op
,
223 SVal LHS
, SVal RHS
) const {
224 auto State
= C
.getState();
227 if (auto ValAsLoc
= RHS
.getAs
<Loc
>()) {
228 Value
= State
->getRawSVal(*ValAsLoc
);
231 if (Value
.isUnknownOrUndef() || !isa
<NonLoc
>(Value
))
234 // Incremention or decremention by 0 is never a bug.
235 if (isZero(State
, Value
.castAs
<NonLoc
>()))
238 // The result may be the past-end iterator of the container, but any other
239 // out of range position is undefined behaviour
240 auto StateAfter
= advancePosition(State
, LHS
, Op
, Value
);
244 const auto *PosAfter
= getIteratorPosition(StateAfter
, LHS
);
246 "Iterator should have position after successful advancement");
247 if (isAheadOfRange(State
, *PosAfter
)) {
248 auto *N
= C
.generateErrorNode(State
);
251 reportBug("Iterator decremented ahead of its valid range.", LHS
,
254 if (isBehindPastTheEnd(State
, *PosAfter
)) {
255 auto *N
= C
.generateErrorNode(State
);
258 reportBug("Iterator incremented behind the past-the-end "
259 "iterator.", LHS
, C
, N
);
263 void IteratorRangeChecker::verifyAdvance(CheckerContext
&C
, SVal LHS
,
265 verifyRandomIncrOrDecr(C
, OO_PlusEqual
, LHS
, RHS
);
268 void IteratorRangeChecker::verifyPrev(CheckerContext
&C
, SVal LHS
,
270 verifyRandomIncrOrDecr(C
, OO_Minus
, LHS
, RHS
);
273 void IteratorRangeChecker::verifyNext(CheckerContext
&C
, SVal LHS
,
275 verifyRandomIncrOrDecr(C
, OO_Plus
, LHS
, RHS
);
278 void IteratorRangeChecker::reportBug(const StringRef
&Message
, SVal Val
,
280 ExplodedNode
*ErrNode
) const {
281 auto R
= std::make_unique
<PathSensitiveBugReport
>(*OutOfRangeBugType
, Message
,
284 const auto *Pos
= getIteratorPosition(C
.getState(), Val
);
285 assert(Pos
&& "Iterator without known position cannot be out-of-range.");
287 R
->markInteresting(Val
);
288 R
->markInteresting(Pos
->getContainer());
289 C
.emitReport(std::move(R
));
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
);
298 bool isZero(ProgramStateRef State
, const NonLoc
&Val
) {
299 auto &BVF
= State
->getBasicVals();
300 return compare(State
, Val
,
301 nonloc::ConcreteInt(BVF
.getValue(llvm::APSInt::get(0))),
305 bool isPastTheEnd(ProgramStateRef State
, const IteratorPosition
&Pos
) {
306 const auto *Cont
= Pos
.getContainer();
307 const auto *CData
= getContainerData(State
, Cont
);
311 const auto End
= CData
->getEnd();
313 if (isEqual(State
, Pos
.getOffset(), End
)) {
321 bool isAheadOfRange(ProgramStateRef State
, const IteratorPosition
&Pos
) {
322 const auto *Cont
= Pos
.getContainer();
323 const auto *CData
= getContainerData(State
, Cont
);
327 const auto Beg
= CData
->getBegin();
329 if (isLess(State
, Pos
.getOffset(), Beg
)) {
337 bool isBehindPastTheEnd(ProgramStateRef State
, const IteratorPosition
&Pos
) {
338 const auto *Cont
= Pos
.getContainer();
339 const auto *CData
= getContainerData(State
, Cont
);
343 const auto End
= CData
->getEnd();
345 if (isGreater(State
, Pos
.getOffset(), End
)) {
353 bool isLess(ProgramStateRef State
, SymbolRef Sym1
, SymbolRef Sym2
) {
354 return compare(State
, Sym1
, Sym2
, BO_LT
);
357 bool isGreater(ProgramStateRef State
, SymbolRef Sym1
, SymbolRef Sym2
) {
358 return compare(State
, Sym1
, Sym2
, BO_GT
);
361 bool isEqual(ProgramStateRef State
, SymbolRef Sym1
, SymbolRef Sym2
) {
362 return compare(State
, Sym1
, Sym2
, BO_EQ
);
367 void ento::registerIteratorRangeChecker(CheckerManager
&mgr
) {
368 mgr
.registerChecker
<IteratorRangeChecker
>();
371 bool ento::shouldRegisterIteratorRangeChecker(const CheckerManager
&mgr
) {