25using namespace iterator;
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"};
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),
143void IteratorRangeChecker::checkPreStmt(
const UnaryOperator *UO,
150 SVal SubVal = State->getSVal(UO->
getSubExpr(),
C.getLocationContext());
153 verifyDereference(
C, SubVal);
155 verifyIncrement(
C, SubVal);
157 verifyDecrement(
C, SubVal);
165 SVal LVal = State->getSVal(BO->
getLHS(),
C.getLocationContext());
168 verifyDereference(
C, LVal);
170 SVal RVal = State->getSVal(BO->
getRHS(),
C.getLocationContext());
181 SVal LVal = State->getSVal(ASE->
getLHS(),
C.getLocationContext());
182 verifyDereference(
C, LVal);
185void IteratorRangeChecker::checkPreStmt(
const MemberExpr *ME,
191 SVal BaseVal = State->getSVal(ME->
getBase(),
C.getLocationContext());
192 verifyDereference(
C, BaseVal);
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);
209 auto &BVF =
C.getSValBuilder().getBasicValueFactory();
210 verifyRandomIncrOrDecr(
C, OO_Plus,
Iter,
215 auto &BVF =
C.getSValBuilder().getBasicValueFactory();
216 verifyRandomIncrOrDecr(
C, OO_Minus,
Iter,
223 auto State =
C.getState();
226 if (
auto ValAsLoc = RHS.
getAs<
Loc>()) {
227 Value = State->getRawSVal(*ValAsLoc);
230 if (
Value.isUnknownOrUndef() || !isa<NonLoc>(
Value))
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);
264 verifyRandomIncrOrDecr(
C, OO_PlusEqual, LHS, RHS);
269 verifyRandomIncrOrDecr(
C, OO_Minus, LHS, RHS);
274 verifyRandomIncrOrDecr(
C, OO_Plus, LHS, RHS);
277void IteratorRangeChecker::reportBug(StringRef Message,
SVal Val,
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();
310 const auto End = CData->getEnd();
312 if (isEqual(State, Pos.
getOffset(), End)) {
326 const auto Beg = CData->getBegin();
328 if (isLess(State, Pos.
getOffset(), Beg)) {
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);
370bool ento::shouldRegisterIteratorRangeChecker(
const CheckerManager &mgr) {
static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y)
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Expr * getLHS()
An array access can be written A[4] or 4[A] (both are equivalent).
A builtin binary operation expression such as "x + y" or "x <= y".
static OverloadedOperatorKind getOverloadedOperator(Opcode Opc)
Retrieve the overloaded operator kind that corresponds to the given binary opcode.
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
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.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Expr * getSubExpr() const
An immutable map from CallDescriptions to arbitrary data.
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Value representing integer constant.
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)
The JSON file list parser is used to communicate input to InstallAPI.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
SymbolRef getOffset() const
const MemRegion * getContainer() const