29class IteratorRangeChecker
30 :
public Checker<check::PreCall, check::PreStmt<UnaryOperator>,
31 check::PreStmt<BinaryOperator>,
32 check::PreStmt<ArraySubscriptExpr>,
33 check::PreStmt<MemberExpr>> {
35 const BugType OutOfRangeBugType{
this,
"Iterator out of range",
36 "Misuse of STL APIs"};
38 void verifyDereference(CheckerContext &
C, SVal Val)
const;
39 void verifyIncrement(CheckerContext &
C, SVal Iter)
const;
40 void verifyDecrement(CheckerContext &
C, SVal Iter)
const;
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;
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;
56 using AdvanceFn = void (IteratorRangeChecker::*)(CheckerContext &, SVal,
61 CallDescriptionMap<AdvanceFn> AdvanceFunctions = {
62 {{CDM::SimpleFunc, {
"std",
"advance"}, 2},
63 &IteratorRangeChecker::verifyAdvance},
64 {{CDM::SimpleFunc, {
"std",
"prev"}, 2},
65 &IteratorRangeChecker::verifyPrev},
66 {{CDM::SimpleFunc, {
"std",
"next"}, 2},
67 &IteratorRangeChecker::verifyNext},
81 const auto *
Func = dyn_cast_or_null<FunctionDecl>(
Call.getDecl());
85 if (
Func->isOverloadedOperator()) {
88 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
89 verifyIncrement(
C, InstCall->getCXXThisVal());
91 if (
Call.getNumArgs() >= 1) {
92 verifyIncrement(
C,
Call.getArgSVal(0));
97 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
98 verifyDecrement(
C, InstCall->getCXXThisVal());
100 if (
Call.getNumArgs() >= 1) {
101 verifyDecrement(
C,
Call.getArgSVal(0));
105 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
107 if (
Call.getNumArgs() >= 1 &&
108 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
109 verifyRandomIncrOrDecr(
C,
Func->getOverloadedOperator(),
110 InstCall->getCXXThisVal(),
114 if (
Call.getNumArgs() >= 2 &&
115 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
116 verifyRandomIncrOrDecr(
C,
Func->getOverloadedOperator(),
117 Call.getArgSVal(0),
Call.getArgSVal(1));
122 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
123 verifyDereference(
C, InstCall->getCXXThisVal());
125 verifyDereference(
C,
Call.getArgSVal(0));
129 const AdvanceFn *Verifier = AdvanceFunctions.lookup(
Call);
131 if (
Call.getNumArgs() > 1) {
132 (this->**Verifier)(
C,
Call.getArgSVal(0),
Call.getArgSVal(1));
134 auto &BVF =
C.getSValBuilder().getBasicValueFactory();
136 C,
Call.getArgSVal(0),
137 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
143void IteratorRangeChecker::checkPreStmt(
const UnaryOperator *UO,
144 CheckerContext &
C)
const {
150 SVal SubVal = State->getSVal(UO->
getSubExpr(),
C.getLocationContext());
153 verifyDereference(
C, SubVal);
155 verifyIncrement(
C, SubVal);
157 verifyDecrement(
C, SubVal);
161void IteratorRangeChecker::checkPreStmt(
const BinaryOperator *BO,
162 CheckerContext &
C)
const {
165 SVal LVal = State->getSVal(BO->
getLHS(),
C.getLocationContext());
168 verifyDereference(
C, LVal);
170 SVal RVal = State->getSVal(BO->
getRHS(),
C.getLocationContext());
178void IteratorRangeChecker::checkPreStmt(
const ArraySubscriptExpr *ASE,
179 CheckerContext &
C)
const {
181 SVal LVal = State->getSVal(ASE->
getLHS(),
C.getLocationContext());
182 verifyDereference(
C, LVal);
185void IteratorRangeChecker::checkPreStmt(
const MemberExpr *ME,
186 CheckerContext &
C)
const {
191 SVal BaseVal = State->getSVal(ME->
getBase(),
C.getLocationContext());
192 verifyDereference(
C, BaseVal);
195void IteratorRangeChecker::verifyDereference(CheckerContext &
C,
197 auto State =
C.getState();
199 if (Pos && isPastTheEnd(State, *Pos)) {
200 auto *N =
C.generateErrorNode(State);
203 reportBug(
"Past-the-end iterator dereferenced.", Val,
C, N);
208void IteratorRangeChecker::verifyIncrement(CheckerContext &
C, SVal Iter)
const {
209 auto &BVF =
C.getSValBuilder().getBasicValueFactory();
210 verifyRandomIncrOrDecr(
C, OO_Plus, Iter,
211 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
214void IteratorRangeChecker::verifyDecrement(CheckerContext &
C, SVal Iter)
const {
215 auto &BVF =
C.getSValBuilder().getBasicValueFactory();
216 verifyRandomIncrOrDecr(
C, OO_Minus, Iter,
217 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
220void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &
C,
222 SVal LHS, SVal RHS)
const {
223 auto State =
C.getState();
226 if (
auto ValAsLoc = RHS.
getAs<Loc>()) {
227 Value = State->getRawSVal(*ValAsLoc);
234 if (isZero(State,
Value.castAs<NonLoc>()))
245 "Iterator should have position after successful advancement");
246 if (isAheadOfRange(State, *PosAfter)) {
247 auto *N =
C.generateErrorNode(State);
250 reportBug(
"Iterator decremented ahead of its valid range.", LHS,
253 if (isBehindPastTheEnd(State, *PosAfter)) {
254 auto *N =
C.generateErrorNode(State);
257 reportBug(
"Iterator incremented behind the past-the-end "
258 "iterator.", LHS,
C, N);
262void IteratorRangeChecker::verifyAdvance(CheckerContext &
C, SVal LHS,
264 verifyRandomIncrOrDecr(
C, OO_PlusEqual, LHS, RHS);
267void IteratorRangeChecker::verifyPrev(CheckerContext &
C, SVal LHS,
269 verifyRandomIncrOrDecr(
C, OO_Minus, LHS, RHS);
272void IteratorRangeChecker::verifyNext(CheckerContext &
C, SVal LHS,
274 verifyRandomIncrOrDecr(
C, OO_Plus, LHS, RHS);
277void IteratorRangeChecker::reportBug(StringRef Message, SVal Val,
279 ExplodedNode *ErrNode)
const {
280 auto R = std::make_unique<PathSensitiveBugReport>(OutOfRangeBugType, Message,
284 assert(Pos &&
"Iterator without known position cannot be out-of-range.");
286 R->markInteresting(Val);
287 R->markInteresting(Pos->getContainer());
288 C.emitReport(std::move(R));
298 auto &BVF = State->getBasicVals();
300 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
310 const auto End = CData->getEnd();
312 if (isEqual(State, Pos.
getOffset(), End)) {
320bool isAheadOfRange(
ProgramStateRef State,
const IteratorPosition &Pos) {
326 const auto Beg = CData->getBegin();
328 if (isLess(State, Pos.
getOffset(), Beg)) {
336bool isBehindPastTheEnd(
ProgramStateRef State,
const IteratorPosition &Pos) {
342 const auto End = CData->getEnd();
344 if (isGreater(State, Pos.
getOffset(), End)) {
353 return compare(State, Sym1, Sym2, BO_LT);
357 return compare(State, Sym1, Sym2, BO_GT);
361 return compare(State, Sym1, Sym2, BO_EQ);
366void ento::registerIteratorRangeChecker(CheckerManager &mgr) {
370bool ento::shouldRegisterIteratorRangeChecker(
const CheckerManager &mgr) {
static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y)
Expr * getLHS()
An array access can be written A[4] or 4[A] (both are equivalent).
static OverloadedOperatorKind getOverloadedOperator(Opcode Opc)
Retrieve the overloaded operator kind that corresponds to the given binary opcode.
bool isImplicitAccess() const
Determine whether the base of this explicit is implicit.
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
Expr * getSubExpr() const
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
const IteratorPosition * getIteratorPosition(ProgramStateRef State, SVal Val)
bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK)
ProgramStateRef advancePosition(ProgramStateRef State, SVal Iter, OverloadedOperatorKind Op, SVal Distance)
const ContainerData * getContainerData(ProgramStateRef State, const MemRegion *Cont)
bool isDecrementOperator(OverloadedOperatorKind OK)
bool isDereferenceOperator(OverloadedOperatorKind OK)
bool isIncrementOperator(OverloadedOperatorKind OK)
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
The JSON file list parser is used to communicate input to InstallAPI.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
bool isa(CodeGen::Address addr)
SymbolRef getOffset() const
const MemRegion * getContainer() const