1afb13afcSAdam Balogh //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--//
2afb13afcSAdam Balogh //
3afb13afcSAdam Balogh // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4afb13afcSAdam Balogh // See https://llvm.org/LICENSE.txt for license information.
5afb13afcSAdam Balogh // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6afb13afcSAdam Balogh //
7afb13afcSAdam Balogh //===----------------------------------------------------------------------===//
8afb13afcSAdam Balogh //
99a08a3faSAdam Balogh // Defines a modeling-checker for modeling STL iterator-like iterators.
10afb13afcSAdam Balogh //
11afb13afcSAdam Balogh //===----------------------------------------------------------------------===//
12afb13afcSAdam Balogh //
13afb13afcSAdam Balogh // In the code, iterator can be represented as a:
14afb13afcSAdam Balogh // * type-I: typedef-ed pointer. Operations over such iterator, such as
15afb13afcSAdam Balogh // comparisons or increments, are modeled straightforwardly by the
16afb13afcSAdam Balogh // analyzer.
17afb13afcSAdam Balogh // * type-II: structure with its method bodies available. Operations over such
18afb13afcSAdam Balogh // iterator are inlined by the analyzer, and results of modeling
19afb13afcSAdam Balogh // these operations are exposing implementation details of the
20afb13afcSAdam Balogh // iterators, which is not necessarily helping.
21afb13afcSAdam Balogh // * type-III: completely opaque structure. Operations over such iterator are
22afb13afcSAdam Balogh // modeled conservatively, producing conjured symbols everywhere.
23afb13afcSAdam Balogh //
24afb13afcSAdam Balogh // To handle all these types in a common way we introduce a structure called
25afb13afcSAdam Balogh // IteratorPosition which is an abstraction of the position the iterator
26afb13afcSAdam Balogh // represents using symbolic expressions. The checker handles all the
27afb13afcSAdam Balogh // operations on this structure.
28afb13afcSAdam Balogh //
29afb13afcSAdam Balogh // Additionally, depending on the circumstances, operators of types II and III
30afb13afcSAdam Balogh // can be represented as:
31afb13afcSAdam Balogh // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
32afb13afcSAdam Balogh // from conservatively evaluated methods such as
33afb13afcSAdam Balogh // `.begin()`.
34afb13afcSAdam Balogh // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
35afb13afcSAdam Balogh // variables or temporaries, when the iterator object is
36afb13afcSAdam Balogh // currently treated as an lvalue.
37afb13afcSAdam Balogh // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
38afb13afcSAdam Balogh // iterator object is treated as an rvalue taken of a
39afb13afcSAdam Balogh // particular lvalue, eg. a copy of "type-a" iterator
40afb13afcSAdam Balogh // object, or an iterator that existed before the
41afb13afcSAdam Balogh // analysis has started.
42afb13afcSAdam Balogh //
43afb13afcSAdam Balogh // To handle any of these three different representations stored in an SVal we
44afb13afcSAdam Balogh // use setter and getters functions which separate the three cases. To store
45afb13afcSAdam Balogh // them we use a pointer union of symbol and memory region.
46afb13afcSAdam Balogh //
47afb13afcSAdam Balogh // The checker works the following way: We record the begin and the
48afb13afcSAdam Balogh // past-end iterator for all containers whenever their `.begin()` and `.end()`
49afb13afcSAdam Balogh // are called. Since the Constraint Manager cannot handle such SVals we need
50afb13afcSAdam Balogh // to take over its role. We post-check equality and non-equality comparisons
51afb13afcSAdam Balogh // and record that the two sides are equal if we are in the 'equal' branch
52afb13afcSAdam Balogh // (true-branch for `==` and false-branch for `!=`).
53afb13afcSAdam Balogh //
54afb13afcSAdam Balogh // In case of type-I or type-II iterators we get a concrete integer as a result
55afb13afcSAdam Balogh // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
56afb13afcSAdam Balogh // this latter case we record the symbol and reload it in evalAssume() and do
57afb13afcSAdam Balogh // the propagation there. We also handle (maybe double) negated comparisons
58afb13afcSAdam Balogh // which are represented in the form of (x == 0 or x != 0) where x is the
59afb13afcSAdam Balogh // comparison itself.
60afb13afcSAdam Balogh //
61afb13afcSAdam Balogh // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
62afb13afcSAdam Balogh // we only use expressions of the format S, S+n or S-n for iterator positions
63afb13afcSAdam Balogh // where S is a conjured symbol and n is an unsigned concrete integer. When
64afb13afcSAdam Balogh // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
65afb13afcSAdam Balogh // a constraint which we later retrieve when doing an actual comparison.
66afb13afcSAdam Balogh
67afb13afcSAdam Balogh #include "clang/AST/DeclTemplate.h"
680b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
69afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
70afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/Checker.h"
710b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
72afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
73afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
74afb13afcSAdam Balogh #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
75afb13afcSAdam Balogh
76afb13afcSAdam Balogh #include "Iterator.h"
77afb13afcSAdam Balogh
78afb13afcSAdam Balogh #include <utility>
79afb13afcSAdam Balogh
80afb13afcSAdam Balogh using namespace clang;
81afb13afcSAdam Balogh using namespace ento;
82afb13afcSAdam Balogh using namespace iterator;
83afb13afcSAdam Balogh
84afb13afcSAdam Balogh namespace {
85afb13afcSAdam Balogh
86afb13afcSAdam Balogh class IteratorModeling
879e63b190SAdam Balogh : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
889e63b190SAdam Balogh check::PostStmt<BinaryOperator>,
899e63b190SAdam Balogh check::PostStmt<MaterializeTemporaryExpr>,
90afb13afcSAdam Balogh check::Bind, check::LiveSymbols, check::DeadSymbols> {
91afb13afcSAdam Balogh
9260bad941SAdam Balogh using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
9360bad941SAdam Balogh SVal, SVal, SVal) const;
9460bad941SAdam Balogh
9560bad941SAdam Balogh void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
9660bad941SAdam Balogh OverloadedOperatorKind Op) const;
9760bad941SAdam Balogh void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call,
9860bad941SAdam Balogh const Expr *OrigExpr,
9960bad941SAdam Balogh const AdvanceFn *Handler) const;
10060bad941SAdam Balogh
101855d21a0SAdam Balogh void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
102afb13afcSAdam Balogh const SVal &LVal, const SVal &RVal,
103afb13afcSAdam Balogh OverloadedOperatorKind Op) const;
104afb13afcSAdam Balogh void processComparison(CheckerContext &C, ProgramStateRef State,
105afb13afcSAdam Balogh SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
106afb13afcSAdam Balogh OverloadedOperatorKind Op) const;
107afb13afcSAdam Balogh void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
108afb13afcSAdam Balogh bool Postfix) const;
109afb13afcSAdam Balogh void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
110afb13afcSAdam Balogh bool Postfix) const;
111afb13afcSAdam Balogh void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
112afb13afcSAdam Balogh OverloadedOperatorKind Op, const SVal &RetVal,
113141cb8a1SEndre Fülöp const SVal &Iterator, const SVal &Amount) const;
1149e63b190SAdam Balogh void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
1159e63b190SAdam Balogh OverloadedOperatorKind OK, SVal Offset) const;
11660bad941SAdam Balogh void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
11760bad941SAdam Balogh SVal Amount) const;
11860bad941SAdam Balogh void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
11960bad941SAdam Balogh SVal Amount) const;
12060bad941SAdam Balogh void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
12160bad941SAdam Balogh SVal Amount) const;
122afb13afcSAdam Balogh void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
123afb13afcSAdam Balogh const MemRegion *Cont) const;
12460bad941SAdam Balogh bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
1256e9c5894SAdam Balogh void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
1266e9c5894SAdam Balogh const char *Sep) const override;
1276e9c5894SAdam Balogh
12860bad941SAdam Balogh // std::advance, std::prev & std::next
12960bad941SAdam Balogh CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
13060bad941SAdam Balogh // template<class InputIt, class Distance>
13160bad941SAdam Balogh // void advance(InputIt& it, Distance n);
13260bad941SAdam Balogh {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance},
13360bad941SAdam Balogh
13460bad941SAdam Balogh // template<class BidirIt>
13560bad941SAdam Balogh // BidirIt prev(
13660bad941SAdam Balogh // BidirIt it,
13760bad941SAdam Balogh // typename std::iterator_traits<BidirIt>::difference_type n = 1);
13860bad941SAdam Balogh {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev},
13960bad941SAdam Balogh
14060bad941SAdam Balogh // template<class ForwardIt>
14160bad941SAdam Balogh // ForwardIt next(
14260bad941SAdam Balogh // ForwardIt it,
14360bad941SAdam Balogh // typename std::iterator_traits<ForwardIt>::difference_type n = 1);
14460bad941SAdam Balogh {{{"std", "next"}, 2}, &IteratorModeling::handleNext},
14560bad941SAdam Balogh };
14660bad941SAdam Balogh
147afb13afcSAdam Balogh public:
14860bad941SAdam Balogh IteratorModeling() = default;
149afb13afcSAdam Balogh
150afb13afcSAdam Balogh void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
151afb13afcSAdam Balogh void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
1529e63b190SAdam Balogh void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const;
1539e63b190SAdam Balogh void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const;
154afb13afcSAdam Balogh void checkPostStmt(const MaterializeTemporaryExpr *MTE,
155afb13afcSAdam Balogh CheckerContext &C) const;
156afb13afcSAdam Balogh void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
157afb13afcSAdam Balogh void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
158afb13afcSAdam Balogh };
159afb13afcSAdam Balogh
160afb13afcSAdam Balogh bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
1619e63b190SAdam Balogh bool isSimpleComparisonOperator(BinaryOperatorKind OK);
162afb13afcSAdam Balogh ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
163afb13afcSAdam Balogh ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
164afb13afcSAdam Balogh SymbolRef Sym2, bool Equal);
165afb13afcSAdam Balogh bool isBoundThroughLazyCompoundVal(const Environment &Env,
166afb13afcSAdam Balogh const MemRegion *Reg);
16760bad941SAdam Balogh const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
168afb13afcSAdam Balogh
169afb13afcSAdam Balogh } // namespace
170afb13afcSAdam Balogh
checkPostCall(const CallEvent & Call,CheckerContext & C) const171afb13afcSAdam Balogh void IteratorModeling::checkPostCall(const CallEvent &Call,
172afb13afcSAdam Balogh CheckerContext &C) const {
173afb13afcSAdam Balogh // Record new iterator positions and iterator position changes
174afb13afcSAdam Balogh const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
175afb13afcSAdam Balogh if (!Func)
176afb13afcSAdam Balogh return;
177afb13afcSAdam Balogh
178afb13afcSAdam Balogh if (Func->isOverloadedOperator()) {
179afb13afcSAdam Balogh const auto Op = Func->getOverloadedOperator();
18060bad941SAdam Balogh handleOverloadedOperator(C, Call, Op);
18160bad941SAdam Balogh return;
18260bad941SAdam Balogh }
18360bad941SAdam Balogh
184afb13afcSAdam Balogh const auto *OrigExpr = Call.getOriginExpr();
185afb13afcSAdam Balogh if (!OrigExpr)
186afb13afcSAdam Balogh return;
187afb13afcSAdam Balogh
18860bad941SAdam Balogh const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call);
18960bad941SAdam Balogh if (Handler) {
19060bad941SAdam Balogh handleAdvanceLikeFunction(C, Call, OrigExpr, Handler);
191afb13afcSAdam Balogh return;
192afb13afcSAdam Balogh }
193afb13afcSAdam Balogh
1949a08a3faSAdam Balogh if (!isIteratorType(Call.getResultType()))
195afb13afcSAdam Balogh return;
196afb13afcSAdam Balogh
197afb13afcSAdam Balogh auto State = C.getState();
198afb13afcSAdam Balogh
199afb13afcSAdam Balogh // Already bound to container?
200afb13afcSAdam Balogh if (getIteratorPosition(State, Call.getReturnValue()))
201afb13afcSAdam Balogh return;
202afb13afcSAdam Balogh
203afb13afcSAdam Balogh // Copy-like and move constructors
204afb13afcSAdam Balogh if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
205afb13afcSAdam Balogh if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
206afb13afcSAdam Balogh State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
207afb13afcSAdam Balogh if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
208afb13afcSAdam Balogh State = removeIteratorPosition(State, Call.getArgSVal(0));
209afb13afcSAdam Balogh }
210afb13afcSAdam Balogh C.addTransition(State);
211afb13afcSAdam Balogh return;
212afb13afcSAdam Balogh }
213afb13afcSAdam Balogh }
214afb13afcSAdam Balogh
215afb13afcSAdam Balogh // Assumption: if return value is an iterator which is not yet bound to a
2169e63b190SAdam Balogh // container, then look for the first iterator argument of the
2179e63b190SAdam Balogh // same type as the return value and bind the return value to
2189e63b190SAdam Balogh // the same container. This approach works for STL algorithms.
219afb13afcSAdam Balogh // FIXME: Add a more conservative mode
220afb13afcSAdam Balogh for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
2219e63b190SAdam Balogh if (isIteratorType(Call.getArgExpr(i)->getType()) &&
2229e63b190SAdam Balogh Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
2239e63b190SAdam Balogh C.getASTContext()).getTypePtr() ==
2249e63b190SAdam Balogh Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) {
225afb13afcSAdam Balogh if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
226afb13afcSAdam Balogh assignToContainer(C, OrigExpr, Call.getReturnValue(),
227afb13afcSAdam Balogh Pos->getContainer());
228afb13afcSAdam Balogh return;
229afb13afcSAdam Balogh }
230afb13afcSAdam Balogh }
231afb13afcSAdam Balogh }
232afb13afcSAdam Balogh }
233afb13afcSAdam Balogh
checkBind(SVal Loc,SVal Val,const Stmt * S,CheckerContext & C) const234afb13afcSAdam Balogh void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
235afb13afcSAdam Balogh CheckerContext &C) const {
236afb13afcSAdam Balogh auto State = C.getState();
237afb13afcSAdam Balogh const auto *Pos = getIteratorPosition(State, Val);
238afb13afcSAdam Balogh if (Pos) {
239afb13afcSAdam Balogh State = setIteratorPosition(State, Loc, *Pos);
240afb13afcSAdam Balogh C.addTransition(State);
241afb13afcSAdam Balogh } else {
242afb13afcSAdam Balogh const auto *OldPos = getIteratorPosition(State, Loc);
243afb13afcSAdam Balogh if (OldPos) {
244afb13afcSAdam Balogh State = removeIteratorPosition(State, Loc);
245afb13afcSAdam Balogh C.addTransition(State);
246afb13afcSAdam Balogh }
247afb13afcSAdam Balogh }
248afb13afcSAdam Balogh }
249afb13afcSAdam Balogh
checkPostStmt(const UnaryOperator * UO,CheckerContext & C) const2509e63b190SAdam Balogh void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
2519e63b190SAdam Balogh CheckerContext &C) const {
2529e63b190SAdam Balogh UnaryOperatorKind OK = UO->getOpcode();
2539e63b190SAdam Balogh if (!isIncrementOperator(OK) && !isDecrementOperator(OK))
2549e63b190SAdam Balogh return;
2559e63b190SAdam Balogh
2569e63b190SAdam Balogh auto &SVB = C.getSValBuilder();
2579e63b190SAdam Balogh handlePtrIncrOrDecr(C, UO->getSubExpr(),
2589e63b190SAdam Balogh isIncrementOperator(OK) ? OO_Plus : OO_Minus,
2599e63b190SAdam Balogh SVB.makeArrayIndex(1));
2609e63b190SAdam Balogh }
2619e63b190SAdam Balogh
checkPostStmt(const BinaryOperator * BO,CheckerContext & C) const2629e63b190SAdam Balogh void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
2639e63b190SAdam Balogh CheckerContext &C) const {
264141cb8a1SEndre Fülöp const ProgramStateRef State = C.getState();
265141cb8a1SEndre Fülöp const BinaryOperatorKind OK = BO->getOpcode();
266141cb8a1SEndre Fülöp const Expr *const LHS = BO->getLHS();
267141cb8a1SEndre Fülöp const Expr *const RHS = BO->getRHS();
268141cb8a1SEndre Fülöp const SVal LVal = State->getSVal(LHS, C.getLocationContext());
269141cb8a1SEndre Fülöp const SVal RVal = State->getSVal(RHS, C.getLocationContext());
2709e63b190SAdam Balogh
2719e63b190SAdam Balogh if (isSimpleComparisonOperator(BO->getOpcode())) {
2729e63b190SAdam Balogh SVal Result = State->getSVal(BO, C.getLocationContext());
2739e63b190SAdam Balogh handleComparison(C, BO, Result, LVal, RVal,
2749e63b190SAdam Balogh BinaryOperator::getOverloadedOperator(OK));
2759e63b190SAdam Balogh } else if (isRandomIncrOrDecrOperator(OK)) {
276141cb8a1SEndre Fülöp // In case of operator+ the iterator can be either on the LHS (eg.: it + 1),
277141cb8a1SEndre Fülöp // or on the RHS (eg.: 1 + it). Both cases are modeled.
278141cb8a1SEndre Fülöp const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType();
279141cb8a1SEndre Fülöp const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS;
280141cb8a1SEndre Fülöp const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS;
281141cb8a1SEndre Fülöp
282141cb8a1SEndre Fülöp // The non-iterator side must have an integral or enumeration type.
283141cb8a1SEndre Fülöp if (!AmountExpr->getType()->isIntegralOrEnumerationType())
284a59d4ae4SAdam Balogh return;
285141cb8a1SEndre Fülöp const SVal &AmountVal = IsIterOnLHS ? RVal : LVal;
286141cb8a1SEndre Fülöp handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
287141cb8a1SEndre Fülöp AmountVal);
2889e63b190SAdam Balogh }
2899e63b190SAdam Balogh }
2909e63b190SAdam Balogh
checkPostStmt(const MaterializeTemporaryExpr * MTE,CheckerContext & C) const291afb13afcSAdam Balogh void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
292afb13afcSAdam Balogh CheckerContext &C) const {
293afb13afcSAdam Balogh /* Transfer iterator state to temporary objects */
294afb13afcSAdam Balogh auto State = C.getState();
295afb13afcSAdam Balogh const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
296afb13afcSAdam Balogh if (!Pos)
297afb13afcSAdam Balogh return;
298afb13afcSAdam Balogh State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
299afb13afcSAdam Balogh C.addTransition(State);
300afb13afcSAdam Balogh }
301afb13afcSAdam Balogh
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const302afb13afcSAdam Balogh void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
303afb13afcSAdam Balogh SymbolReaper &SR) const {
3049a08a3faSAdam Balogh // Keep symbolic expressions of iterator positions alive
305afb13afcSAdam Balogh auto RegionMap = State->get<IteratorRegionMap>();
30670d592d6SMark de Wever for (const auto &Reg : RegionMap) {
307afb13afcSAdam Balogh const auto Offset = Reg.second.getOffset();
308afb13afcSAdam Balogh for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
309afb13afcSAdam Balogh if (isa<SymbolData>(*i))
310afb13afcSAdam Balogh SR.markLive(*i);
311afb13afcSAdam Balogh }
312afb13afcSAdam Balogh
313afb13afcSAdam Balogh auto SymbolMap = State->get<IteratorSymbolMap>();
31470d592d6SMark de Wever for (const auto &Sym : SymbolMap) {
315afb13afcSAdam Balogh const auto Offset = Sym.second.getOffset();
316afb13afcSAdam Balogh for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
317afb13afcSAdam Balogh if (isa<SymbolData>(*i))
318afb13afcSAdam Balogh SR.markLive(*i);
319afb13afcSAdam Balogh }
320afb13afcSAdam Balogh
321afb13afcSAdam Balogh }
322afb13afcSAdam Balogh
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const323afb13afcSAdam Balogh void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
324afb13afcSAdam Balogh CheckerContext &C) const {
325afb13afcSAdam Balogh // Cleanup
326afb13afcSAdam Balogh auto State = C.getState();
327afb13afcSAdam Balogh
328afb13afcSAdam Balogh auto RegionMap = State->get<IteratorRegionMap>();
32970d592d6SMark de Wever for (const auto &Reg : RegionMap) {
330afb13afcSAdam Balogh if (!SR.isLiveRegion(Reg.first)) {
331afb13afcSAdam Balogh // The region behind the `LazyCompoundVal` is often cleaned up before
332afb13afcSAdam Balogh // the `LazyCompoundVal` itself. If there are iterator positions keyed
333afb13afcSAdam Balogh // by these regions their cleanup must be deferred.
334afb13afcSAdam Balogh if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
335afb13afcSAdam Balogh State = State->remove<IteratorRegionMap>(Reg.first);
336afb13afcSAdam Balogh }
337afb13afcSAdam Balogh }
338afb13afcSAdam Balogh }
339afb13afcSAdam Balogh
340afb13afcSAdam Balogh auto SymbolMap = State->get<IteratorSymbolMap>();
34170d592d6SMark de Wever for (const auto &Sym : SymbolMap) {
342afb13afcSAdam Balogh if (!SR.isLive(Sym.first)) {
343afb13afcSAdam Balogh State = State->remove<IteratorSymbolMap>(Sym.first);
344afb13afcSAdam Balogh }
345afb13afcSAdam Balogh }
346afb13afcSAdam Balogh
347afb13afcSAdam Balogh C.addTransition(State);
348afb13afcSAdam Balogh }
349afb13afcSAdam Balogh
35060bad941SAdam Balogh void
handleOverloadedOperator(CheckerContext & C,const CallEvent & Call,OverloadedOperatorKind Op) const35160bad941SAdam Balogh IteratorModeling::handleOverloadedOperator(CheckerContext &C,
35260bad941SAdam Balogh const CallEvent &Call,
35360bad941SAdam Balogh OverloadedOperatorKind Op) const {
35460bad941SAdam Balogh if (isSimpleComparisonOperator(Op)) {
35560bad941SAdam Balogh const auto *OrigExpr = Call.getOriginExpr();
35660bad941SAdam Balogh if (!OrigExpr)
35760bad941SAdam Balogh return;
35860bad941SAdam Balogh
35960bad941SAdam Balogh if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
36060bad941SAdam Balogh handleComparison(C, OrigExpr, Call.getReturnValue(),
36160bad941SAdam Balogh InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
36260bad941SAdam Balogh return;
36360bad941SAdam Balogh }
36460bad941SAdam Balogh
36560bad941SAdam Balogh handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
36660bad941SAdam Balogh Call.getArgSVal(1), Op);
36760bad941SAdam Balogh return;
36860bad941SAdam Balogh } else if (isRandomIncrOrDecrOperator(Op)) {
36960bad941SAdam Balogh const auto *OrigExpr = Call.getOriginExpr();
37060bad941SAdam Balogh if (!OrigExpr)
37160bad941SAdam Balogh return;
37260bad941SAdam Balogh
37360bad941SAdam Balogh if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
37460bad941SAdam Balogh if (Call.getNumArgs() >= 1 &&
37560bad941SAdam Balogh Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
37660bad941SAdam Balogh handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
37760bad941SAdam Balogh InstCall->getCXXThisVal(), Call.getArgSVal(0));
37860bad941SAdam Balogh return;
37960bad941SAdam Balogh }
380141cb8a1SEndre Fülöp } else if (Call.getNumArgs() >= 2) {
381141cb8a1SEndre Fülöp const Expr *FirstArg = Call.getArgExpr(0);
382141cb8a1SEndre Fülöp const Expr *SecondArg = Call.getArgExpr(1);
383141cb8a1SEndre Fülöp const QualType FirstType = FirstArg->getType();
384141cb8a1SEndre Fülöp const QualType SecondType = SecondArg->getType();
385141cb8a1SEndre Fülöp
386141cb8a1SEndre Fülöp if (FirstType->isIntegralOrEnumerationType() ||
387141cb8a1SEndre Fülöp SecondType->isIntegralOrEnumerationType()) {
388141cb8a1SEndre Fülöp // In case of operator+ the iterator can be either on the LHS (eg.:
389141cb8a1SEndre Fülöp // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled.
390141cb8a1SEndre Fülöp const bool IsIterFirst = FirstType->isStructureOrClassType();
391141cb8a1SEndre Fülöp const SVal FirstArg = Call.getArgSVal(0);
392141cb8a1SEndre Fülöp const SVal SecondArg = Call.getArgSVal(1);
393141cb8a1SEndre Fülöp const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg;
394141cb8a1SEndre Fülöp const SVal &Amount = IsIterFirst ? SecondArg : FirstArg;
395141cb8a1SEndre Fülöp
39660bad941SAdam Balogh handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
397141cb8a1SEndre Fülöp Iterator, Amount);
39860bad941SAdam Balogh return;
39960bad941SAdam Balogh }
40060bad941SAdam Balogh }
40160bad941SAdam Balogh } else if (isIncrementOperator(Op)) {
40260bad941SAdam Balogh if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
40360bad941SAdam Balogh handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
40460bad941SAdam Balogh Call.getNumArgs());
40560bad941SAdam Balogh return;
40660bad941SAdam Balogh }
40760bad941SAdam Balogh
40860bad941SAdam Balogh handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
40960bad941SAdam Balogh Call.getNumArgs());
41060bad941SAdam Balogh return;
41160bad941SAdam Balogh } else if (isDecrementOperator(Op)) {
41260bad941SAdam Balogh if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
41360bad941SAdam Balogh handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
41460bad941SAdam Balogh Call.getNumArgs());
41560bad941SAdam Balogh return;
41660bad941SAdam Balogh }
41760bad941SAdam Balogh
41860bad941SAdam Balogh handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
41960bad941SAdam Balogh Call.getNumArgs());
42060bad941SAdam Balogh return;
42160bad941SAdam Balogh }
42260bad941SAdam Balogh }
42360bad941SAdam Balogh
42460bad941SAdam Balogh void
handleAdvanceLikeFunction(CheckerContext & C,const CallEvent & Call,const Expr * OrigExpr,const AdvanceFn * Handler) const42560bad941SAdam Balogh IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
42660bad941SAdam Balogh const CallEvent &Call,
42760bad941SAdam Balogh const Expr *OrigExpr,
42860bad941SAdam Balogh const AdvanceFn *Handler) const {
42960bad941SAdam Balogh if (!C.wasInlined) {
43060bad941SAdam Balogh (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
43160bad941SAdam Balogh Call.getArgSVal(0), Call.getArgSVal(1));
43260bad941SAdam Balogh return;
43360bad941SAdam Balogh }
43460bad941SAdam Balogh
43560bad941SAdam Balogh // If std::advance() was inlined, but a non-standard function it calls inside
43660bad941SAdam Balogh // was not, then we have to model it explicitly
43760bad941SAdam Balogh const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
43860bad941SAdam Balogh if (IdInfo) {
43960bad941SAdam Balogh if (IdInfo->getName() == "advance") {
44060bad941SAdam Balogh if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
44160bad941SAdam Balogh (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
44260bad941SAdam Balogh Call.getArgSVal(0), Call.getArgSVal(1));
44360bad941SAdam Balogh }
44460bad941SAdam Balogh }
44560bad941SAdam Balogh }
44660bad941SAdam Balogh }
44760bad941SAdam Balogh
handleComparison(CheckerContext & C,const Expr * CE,SVal RetVal,const SVal & LVal,const SVal & RVal,OverloadedOperatorKind Op) const448afb13afcSAdam Balogh void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
449855d21a0SAdam Balogh SVal RetVal, const SVal &LVal,
450afb13afcSAdam Balogh const SVal &RVal,
451afb13afcSAdam Balogh OverloadedOperatorKind Op) const {
452afb13afcSAdam Balogh // Record the operands and the operator of the comparison for the next
453afb13afcSAdam Balogh // evalAssume, if the result is a symbolic expression. If it is a concrete
454afb13afcSAdam Balogh // value (only one branch is possible), then transfer the state between
455afb13afcSAdam Balogh // the operands according to the operator and the result
456afb13afcSAdam Balogh auto State = C.getState();
457afb13afcSAdam Balogh const auto *LPos = getIteratorPosition(State, LVal);
458afb13afcSAdam Balogh const auto *RPos = getIteratorPosition(State, RVal);
459afb13afcSAdam Balogh const MemRegion *Cont = nullptr;
460afb13afcSAdam Balogh if (LPos) {
461afb13afcSAdam Balogh Cont = LPos->getContainer();
462afb13afcSAdam Balogh } else if (RPos) {
463afb13afcSAdam Balogh Cont = RPos->getContainer();
464afb13afcSAdam Balogh }
465afb13afcSAdam Balogh if (!Cont)
466afb13afcSAdam Balogh return;
467afb13afcSAdam Balogh
468ba8cda98SDenys Petrov // At least one of the iterators has recorded positions. If one of them does
469afb13afcSAdam Balogh // not then create a new symbol for the offset.
470afb13afcSAdam Balogh SymbolRef Sym;
471afb13afcSAdam Balogh if (!LPos || !RPos) {
472afb13afcSAdam Balogh auto &SymMgr = C.getSymbolManager();
473afb13afcSAdam Balogh Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
474afb13afcSAdam Balogh C.getASTContext().LongTy, C.blockCount());
475afb13afcSAdam Balogh State = assumeNoOverflow(State, Sym, 4);
476afb13afcSAdam Balogh }
477afb13afcSAdam Balogh
478afb13afcSAdam Balogh if (!LPos) {
479afb13afcSAdam Balogh State = setIteratorPosition(State, LVal,
480afb13afcSAdam Balogh IteratorPosition::getPosition(Cont, Sym));
481afb13afcSAdam Balogh LPos = getIteratorPosition(State, LVal);
482afb13afcSAdam Balogh } else if (!RPos) {
483afb13afcSAdam Balogh State = setIteratorPosition(State, RVal,
484afb13afcSAdam Balogh IteratorPosition::getPosition(Cont, Sym));
485afb13afcSAdam Balogh RPos = getIteratorPosition(State, RVal);
486afb13afcSAdam Balogh }
487afb13afcSAdam Balogh
488a59d4ae4SAdam Balogh // If the value for which we just tried to set a new iterator position is
489a59d4ae4SAdam Balogh // an `SVal`for which no iterator position can be set then the setting was
490a59d4ae4SAdam Balogh // unsuccessful. We cannot handle the comparison in this case.
491a59d4ae4SAdam Balogh if (!LPos || !RPos)
492a59d4ae4SAdam Balogh return;
493a59d4ae4SAdam Balogh
494ba8cda98SDenys Petrov // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
495855d21a0SAdam Balogh // instead.
496855d21a0SAdam Balogh if (RetVal.isUnknown()) {
497855d21a0SAdam Balogh auto &SymMgr = C.getSymbolManager();
498855d21a0SAdam Balogh auto *LCtx = C.getLocationContext();
499855d21a0SAdam Balogh RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
500855d21a0SAdam Balogh CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
501855d21a0SAdam Balogh State = State->BindExpr(CE, LCtx, RetVal);
502855d21a0SAdam Balogh }
503855d21a0SAdam Balogh
504afb13afcSAdam Balogh processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
505afb13afcSAdam Balogh }
506afb13afcSAdam Balogh
processComparison(CheckerContext & C,ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,const SVal & RetVal,OverloadedOperatorKind Op) const507afb13afcSAdam Balogh void IteratorModeling::processComparison(CheckerContext &C,
508afb13afcSAdam Balogh ProgramStateRef State, SymbolRef Sym1,
509afb13afcSAdam Balogh SymbolRef Sym2, const SVal &RetVal,
510afb13afcSAdam Balogh OverloadedOperatorKind Op) const {
511afb13afcSAdam Balogh if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
512afb13afcSAdam Balogh if ((State = relateSymbols(State, Sym1, Sym2,
513afb13afcSAdam Balogh (Op == OO_EqualEqual) ==
514afb13afcSAdam Balogh (TruthVal->getValue() != 0)))) {
515afb13afcSAdam Balogh C.addTransition(State);
516afb13afcSAdam Balogh } else {
517afb13afcSAdam Balogh C.generateSink(State, C.getPredecessor());
518afb13afcSAdam Balogh }
519afb13afcSAdam Balogh return;
520afb13afcSAdam Balogh }
521afb13afcSAdam Balogh
522afb13afcSAdam Balogh const auto ConditionVal = RetVal.getAs<DefinedSVal>();
523afb13afcSAdam Balogh if (!ConditionVal)
524afb13afcSAdam Balogh return;
525afb13afcSAdam Balogh
526afb13afcSAdam Balogh if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
527afb13afcSAdam Balogh StateTrue = StateTrue->assume(*ConditionVal, true);
528afb13afcSAdam Balogh C.addTransition(StateTrue);
529afb13afcSAdam Balogh }
530afb13afcSAdam Balogh
531afb13afcSAdam Balogh if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
532afb13afcSAdam Balogh StateFalse = StateFalse->assume(*ConditionVal, false);
533afb13afcSAdam Balogh C.addTransition(StateFalse);
534afb13afcSAdam Balogh }
535afb13afcSAdam Balogh }
536afb13afcSAdam Balogh
handleIncrement(CheckerContext & C,const SVal & RetVal,const SVal & Iter,bool Postfix) const537afb13afcSAdam Balogh void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
538afb13afcSAdam Balogh const SVal &Iter, bool Postfix) const {
539afb13afcSAdam Balogh // Increment the symbolic expressions which represents the position of the
540afb13afcSAdam Balogh // iterator
541afb13afcSAdam Balogh auto State = C.getState();
542afb13afcSAdam Balogh auto &BVF = C.getSymbolManager().getBasicVals();
543afb13afcSAdam Balogh
544afb13afcSAdam Balogh const auto *Pos = getIteratorPosition(State, Iter);
545afb13afcSAdam Balogh if (!Pos)
546afb13afcSAdam Balogh return;
547afb13afcSAdam Balogh
548afb13afcSAdam Balogh auto NewState =
549afb13afcSAdam Balogh advancePosition(State, Iter, OO_Plus,
550afb13afcSAdam Balogh nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
551afb13afcSAdam Balogh assert(NewState &&
552afb13afcSAdam Balogh "Advancing position by concrete int should always be successful");
553afb13afcSAdam Balogh
554afb13afcSAdam Balogh const auto *NewPos = getIteratorPosition(NewState, Iter);
555afb13afcSAdam Balogh assert(NewPos &&
556afb13afcSAdam Balogh "Iterator should have position after successful advancement");
557afb13afcSAdam Balogh
558afb13afcSAdam Balogh State = setIteratorPosition(State, Iter, *NewPos);
559afb13afcSAdam Balogh State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
560afb13afcSAdam Balogh C.addTransition(State);
561afb13afcSAdam Balogh }
562afb13afcSAdam Balogh
handleDecrement(CheckerContext & C,const SVal & RetVal,const SVal & Iter,bool Postfix) const563afb13afcSAdam Balogh void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
564afb13afcSAdam Balogh const SVal &Iter, bool Postfix) const {
565afb13afcSAdam Balogh // Decrement the symbolic expressions which represents the position of the
566afb13afcSAdam Balogh // iterator
567afb13afcSAdam Balogh auto State = C.getState();
568afb13afcSAdam Balogh auto &BVF = C.getSymbolManager().getBasicVals();
569afb13afcSAdam Balogh
570afb13afcSAdam Balogh const auto *Pos = getIteratorPosition(State, Iter);
571afb13afcSAdam Balogh if (!Pos)
572afb13afcSAdam Balogh return;
573afb13afcSAdam Balogh
574afb13afcSAdam Balogh auto NewState =
575afb13afcSAdam Balogh advancePosition(State, Iter, OO_Minus,
576afb13afcSAdam Balogh nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
577afb13afcSAdam Balogh assert(NewState &&
578afb13afcSAdam Balogh "Advancing position by concrete int should always be successful");
579afb13afcSAdam Balogh
580afb13afcSAdam Balogh const auto *NewPos = getIteratorPosition(NewState, Iter);
581afb13afcSAdam Balogh assert(NewPos &&
582afb13afcSAdam Balogh "Iterator should have position after successful advancement");
583afb13afcSAdam Balogh
584afb13afcSAdam Balogh State = setIteratorPosition(State, Iter, *NewPos);
585afb13afcSAdam Balogh State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
586afb13afcSAdam Balogh C.addTransition(State);
587afb13afcSAdam Balogh }
588afb13afcSAdam Balogh
handleRandomIncrOrDecr(CheckerContext & C,const Expr * CE,OverloadedOperatorKind Op,const SVal & RetVal,const SVal & Iterator,const SVal & Amount) const589141cb8a1SEndre Fülöp void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
590afb13afcSAdam Balogh OverloadedOperatorKind Op,
591afb13afcSAdam Balogh const SVal &RetVal,
592141cb8a1SEndre Fülöp const SVal &Iterator,
593141cb8a1SEndre Fülöp const SVal &Amount) const {
594afb13afcSAdam Balogh // Increment or decrement the symbolic expressions which represents the
595afb13afcSAdam Balogh // position of the iterator
596afb13afcSAdam Balogh auto State = C.getState();
597afb13afcSAdam Balogh
598141cb8a1SEndre Fülöp const auto *Pos = getIteratorPosition(State, Iterator);
599afb13afcSAdam Balogh if (!Pos)
600afb13afcSAdam Balogh return;
601afb13afcSAdam Balogh
602141cb8a1SEndre Fülöp const auto *Value = &Amount;
603141cb8a1SEndre Fülöp SVal Val;
604141cb8a1SEndre Fülöp if (auto LocAmount = Amount.getAs<Loc>()) {
605141cb8a1SEndre Fülöp Val = State->getRawSVal(*LocAmount);
606141cb8a1SEndre Fülöp Value = &Val;
607afb13afcSAdam Balogh }
608afb13afcSAdam Balogh
609141cb8a1SEndre Fülöp const auto &TgtVal =
610141cb8a1SEndre Fülöp (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
611afb13afcSAdam Balogh
612ea563daaSAdam Balogh // `AdvancedState` is a state where the position of `LHS` is advanced. We
613ea563daaSAdam Balogh // only need this state to retrieve the new position, but we do not want
614ea563daaSAdam Balogh // to change the position of `LHS` (in every case).
615141cb8a1SEndre Fülöp auto AdvancedState = advancePosition(State, Iterator, Op, *Value);
616ea563daaSAdam Balogh if (AdvancedState) {
617141cb8a1SEndre Fülöp const auto *NewPos = getIteratorPosition(AdvancedState, Iterator);
618afb13afcSAdam Balogh assert(NewPos &&
619afb13afcSAdam Balogh "Iterator should have position after successful advancement");
620afb13afcSAdam Balogh
621ea563daaSAdam Balogh State = setIteratorPosition(State, TgtVal, *NewPos);
622afb13afcSAdam Balogh C.addTransition(State);
623afb13afcSAdam Balogh } else {
624afb13afcSAdam Balogh assignToContainer(C, CE, TgtVal, Pos->getContainer());
625afb13afcSAdam Balogh }
626afb13afcSAdam Balogh }
627afb13afcSAdam Balogh
handlePtrIncrOrDecr(CheckerContext & C,const Expr * Iterator,OverloadedOperatorKind OK,SVal Offset) const6289e63b190SAdam Balogh void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
6299e63b190SAdam Balogh const Expr *Iterator,
6309e63b190SAdam Balogh OverloadedOperatorKind OK,
6319e63b190SAdam Balogh SVal Offset) const {
632*96ccb690SBalazs Benics if (!isa<DefinedSVal>(Offset))
633a59d4ae4SAdam Balogh return;
634a59d4ae4SAdam Balogh
6359e63b190SAdam Balogh QualType PtrType = Iterator->getType();
6369e63b190SAdam Balogh if (!PtrType->isPointerType())
6379e63b190SAdam Balogh return;
6389e63b190SAdam Balogh QualType ElementType = PtrType->getPointeeType();
6399e63b190SAdam Balogh
6409e63b190SAdam Balogh ProgramStateRef State = C.getState();
6419e63b190SAdam Balogh SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
6429e63b190SAdam Balogh
6439e63b190SAdam Balogh const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
6449e63b190SAdam Balogh if (!OldPos)
6459e63b190SAdam Balogh return;
6469e63b190SAdam Balogh
6479e63b190SAdam Balogh SVal NewVal;
648a59d4ae4SAdam Balogh if (OK == OO_Plus || OK == OO_PlusEqual) {
6499e63b190SAdam Balogh NewVal = State->getLValue(ElementType, Offset, OldVal);
650a59d4ae4SAdam Balogh } else {
651a59d4ae4SAdam Balogh auto &SVB = C.getSValBuilder();
652a59d4ae4SAdam Balogh SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>());
6539e63b190SAdam Balogh NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
6549e63b190SAdam Balogh }
6559e63b190SAdam Balogh
6569e63b190SAdam Balogh // `AdvancedState` is a state where the position of `Old` is advanced. We
6579e63b190SAdam Balogh // only need this state to retrieve the new position, but we do not want
6589e63b190SAdam Balogh // ever to change the position of `OldVal`.
6599e63b190SAdam Balogh auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
6609e63b190SAdam Balogh if (AdvancedState) {
6619e63b190SAdam Balogh const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
6629e63b190SAdam Balogh assert(NewPos &&
6639e63b190SAdam Balogh "Iterator should have position after successful advancement");
6649e63b190SAdam Balogh
6659e63b190SAdam Balogh ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
6669e63b190SAdam Balogh C.addTransition(NewState);
6679e63b190SAdam Balogh } else {
6689e63b190SAdam Balogh assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
6699e63b190SAdam Balogh }
6709e63b190SAdam Balogh }
6719e63b190SAdam Balogh
handleAdvance(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const67260bad941SAdam Balogh void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
67360bad941SAdam Balogh SVal RetVal, SVal Iter,
67460bad941SAdam Balogh SVal Amount) const {
67560bad941SAdam Balogh handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
67660bad941SAdam Balogh }
67760bad941SAdam Balogh
handlePrev(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const67860bad941SAdam Balogh void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
67960bad941SAdam Balogh SVal RetVal, SVal Iter, SVal Amount) const {
68060bad941SAdam Balogh handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
68160bad941SAdam Balogh }
68260bad941SAdam Balogh
handleNext(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const68360bad941SAdam Balogh void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
68460bad941SAdam Balogh SVal RetVal, SVal Iter, SVal Amount) const {
68560bad941SAdam Balogh handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
68660bad941SAdam Balogh }
68760bad941SAdam Balogh
assignToContainer(CheckerContext & C,const Expr * CE,const SVal & RetVal,const MemRegion * Cont) const688afb13afcSAdam Balogh void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
689afb13afcSAdam Balogh const SVal &RetVal,
690afb13afcSAdam Balogh const MemRegion *Cont) const {
691afb13afcSAdam Balogh Cont = Cont->getMostDerivedObjectRegion();
692afb13afcSAdam Balogh
693afb13afcSAdam Balogh auto State = C.getState();
694b198f16eSAdam Balogh const auto *LCtx = C.getLocationContext();
695b198f16eSAdam Balogh State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
696b198f16eSAdam Balogh
697afb13afcSAdam Balogh C.addTransition(State);
698afb13afcSAdam Balogh }
699afb13afcSAdam Balogh
noChangeInAdvance(CheckerContext & C,SVal Iter,const Expr * CE) const70060bad941SAdam Balogh bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
70160bad941SAdam Balogh const Expr *CE) const {
70260bad941SAdam Balogh // Compare the iterator position before and after the call. (To be called
70360bad941SAdam Balogh // from `checkPostCall()`.)
70460bad941SAdam Balogh const auto StateAfter = C.getState();
70560bad941SAdam Balogh
70660bad941SAdam Balogh const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
70760bad941SAdam Balogh // If we have no position after the call of `std::advance`, then we are not
70860bad941SAdam Balogh // interested. (Modeling of an inlined `std::advance()` should not remove the
70960bad941SAdam Balogh // position in any case.)
71060bad941SAdam Balogh if (!PosAfter)
71160bad941SAdam Balogh return false;
71260bad941SAdam Balogh
71360bad941SAdam Balogh const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
71460bad941SAdam Balogh assert(N && "Any call should have a `CallEnter` node.");
71560bad941SAdam Balogh
71660bad941SAdam Balogh const auto StateBefore = N->getState();
71760bad941SAdam Balogh const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
718a59d4ae4SAdam Balogh // FIXME: `std::advance()` should not create a new iterator position but
719a59d4ae4SAdam Balogh // change existing ones. However, in case of iterators implemented as
720a59d4ae4SAdam Balogh // pointers the handling of parameters in `std::advance()`-like
721a59d4ae4SAdam Balogh // functions is still incomplete which may result in cases where
722a59d4ae4SAdam Balogh // the new position is assigned to the wrong pointer. This causes
723a59d4ae4SAdam Balogh // crash if we use an assertion here.
724a59d4ae4SAdam Balogh if (!PosBefore)
725a59d4ae4SAdam Balogh return false;
72660bad941SAdam Balogh
72760bad941SAdam Balogh return PosBefore->getOffset() == PosAfter->getOffset();
72860bad941SAdam Balogh }
72960bad941SAdam Balogh
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const7306e9c5894SAdam Balogh void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
7316e9c5894SAdam Balogh const char *NL, const char *Sep) const {
7326e9c5894SAdam Balogh auto SymbolMap = State->get<IteratorSymbolMap>();
7336e9c5894SAdam Balogh auto RegionMap = State->get<IteratorRegionMap>();
734ea563daaSAdam Balogh // Use a counter to add newlines before every line except the first one.
735ea563daaSAdam Balogh unsigned Count = 0;
7366e9c5894SAdam Balogh
7376e9c5894SAdam Balogh if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
7386e9c5894SAdam Balogh Out << Sep << "Iterator Positions :" << NL;
739b6d9e976SMark de Wever for (const auto &Sym : SymbolMap) {
740ea563daaSAdam Balogh if (Count++)
741ea563daaSAdam Balogh Out << NL;
742ea563daaSAdam Balogh
7436e9c5894SAdam Balogh Sym.first->dumpToStream(Out);
7446e9c5894SAdam Balogh Out << " : ";
7456e9c5894SAdam Balogh const auto Pos = Sym.second;
7466e9c5894SAdam Balogh Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
7476e9c5894SAdam Balogh Pos.getContainer()->dumpToStream(Out);
7486e9c5894SAdam Balogh Out<<" ; Offset == ";
7496e9c5894SAdam Balogh Pos.getOffset()->dumpToStream(Out);
7506e9c5894SAdam Balogh }
7516e9c5894SAdam Balogh
752b6d9e976SMark de Wever for (const auto &Reg : RegionMap) {
753ea563daaSAdam Balogh if (Count++)
754ea563daaSAdam Balogh Out << NL;
755ea563daaSAdam Balogh
7566e9c5894SAdam Balogh Reg.first->dumpToStream(Out);
7576e9c5894SAdam Balogh Out << " : ";
7586e9c5894SAdam Balogh const auto Pos = Reg.second;
7596e9c5894SAdam Balogh Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
7606e9c5894SAdam Balogh Pos.getContainer()->dumpToStream(Out);
7616e9c5894SAdam Balogh Out<<" ; Offset == ";
7626e9c5894SAdam Balogh Pos.getOffset()->dumpToStream(Out);
7636e9c5894SAdam Balogh }
7646e9c5894SAdam Balogh }
7656e9c5894SAdam Balogh }
7666e9c5894SAdam Balogh
767afb13afcSAdam Balogh namespace {
768afb13afcSAdam Balogh
isSimpleComparisonOperator(OverloadedOperatorKind OK)769afb13afcSAdam Balogh bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
770afb13afcSAdam Balogh return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
771afb13afcSAdam Balogh }
772afb13afcSAdam Balogh
isSimpleComparisonOperator(BinaryOperatorKind OK)7739e63b190SAdam Balogh bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
7749e63b190SAdam Balogh return OK == BO_EQ || OK == BO_NE;
7759e63b190SAdam Balogh }
7769e63b190SAdam Balogh
removeIteratorPosition(ProgramStateRef State,const SVal & Val)777afb13afcSAdam Balogh ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
778afb13afcSAdam Balogh if (auto Reg = Val.getAsRegion()) {
779afb13afcSAdam Balogh Reg = Reg->getMostDerivedObjectRegion();
780afb13afcSAdam Balogh return State->remove<IteratorRegionMap>(Reg);
781afb13afcSAdam Balogh } else if (const auto Sym = Val.getAsSymbol()) {
782afb13afcSAdam Balogh return State->remove<IteratorSymbolMap>(Sym);
783afb13afcSAdam Balogh } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
784afb13afcSAdam Balogh return State->remove<IteratorRegionMap>(LCVal->getRegion());
785afb13afcSAdam Balogh }
786afb13afcSAdam Balogh return nullptr;
787afb13afcSAdam Balogh }
788afb13afcSAdam Balogh
relateSymbols(ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,bool Equal)789afb13afcSAdam Balogh ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
790afb13afcSAdam Balogh SymbolRef Sym2, bool Equal) {
791afb13afcSAdam Balogh auto &SVB = State->getStateManager().getSValBuilder();
792afb13afcSAdam Balogh
793afb13afcSAdam Balogh // FIXME: This code should be reworked as follows:
794afb13afcSAdam Balogh // 1. Subtract the operands using evalBinOp().
795afb13afcSAdam Balogh // 2. Assume that the result doesn't overflow.
796afb13afcSAdam Balogh // 3. Compare the result to 0.
797afb13afcSAdam Balogh // 4. Assume the result of the comparison.
798afb13afcSAdam Balogh const auto comparison =
799afb13afcSAdam Balogh SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
800afb13afcSAdam Balogh nonloc::SymbolVal(Sym2), SVB.getConditionType());
801afb13afcSAdam Balogh
802*96ccb690SBalazs Benics assert(isa<DefinedSVal>(comparison) &&
803afb13afcSAdam Balogh "Symbol comparison must be a `DefinedSVal`");
804afb13afcSAdam Balogh
805afb13afcSAdam Balogh auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
806afb13afcSAdam Balogh if (!NewState)
807afb13afcSAdam Balogh return nullptr;
808afb13afcSAdam Balogh
809afb13afcSAdam Balogh if (const auto CompSym = comparison.getAsSymbol()) {
810afb13afcSAdam Balogh assert(isa<SymIntExpr>(CompSym) &&
811afb13afcSAdam Balogh "Symbol comparison must be a `SymIntExpr`");
812afb13afcSAdam Balogh assert(BinaryOperator::isComparisonOp(
813afb13afcSAdam Balogh cast<SymIntExpr>(CompSym)->getOpcode()) &&
814afb13afcSAdam Balogh "Symbol comparison must be a comparison");
815afb13afcSAdam Balogh return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
816afb13afcSAdam Balogh }
817afb13afcSAdam Balogh
818afb13afcSAdam Balogh return NewState;
819afb13afcSAdam Balogh }
820afb13afcSAdam Balogh
isBoundThroughLazyCompoundVal(const Environment & Env,const MemRegion * Reg)821afb13afcSAdam Balogh bool isBoundThroughLazyCompoundVal(const Environment &Env,
822afb13afcSAdam Balogh const MemRegion *Reg) {
82370d592d6SMark de Wever for (const auto &Binding : Env) {
824afb13afcSAdam Balogh if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
825afb13afcSAdam Balogh if (LCVal->getRegion() == Reg)
826afb13afcSAdam Balogh return true;
827afb13afcSAdam Balogh }
828afb13afcSAdam Balogh }
829afb13afcSAdam Balogh
830afb13afcSAdam Balogh return false;
831afb13afcSAdam Balogh }
832afb13afcSAdam Balogh
findCallEnter(const ExplodedNode * Node,const Expr * Call)83360bad941SAdam Balogh const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
83460bad941SAdam Balogh while (Node) {
83560bad941SAdam Balogh ProgramPoint PP = Node->getLocation();
83660bad941SAdam Balogh if (auto Enter = PP.getAs<CallEnter>()) {
83760bad941SAdam Balogh if (Enter->getCallExpr() == Call)
83860bad941SAdam Balogh break;
83960bad941SAdam Balogh }
84060bad941SAdam Balogh
84160bad941SAdam Balogh Node = Node->getFirstPred();
84260bad941SAdam Balogh }
84360bad941SAdam Balogh
84460bad941SAdam Balogh return Node;
84560bad941SAdam Balogh }
84660bad941SAdam Balogh
847afb13afcSAdam Balogh } // namespace
848afb13afcSAdam Balogh
registerIteratorModeling(CheckerManager & mgr)849afb13afcSAdam Balogh void ento::registerIteratorModeling(CheckerManager &mgr) {
850afb13afcSAdam Balogh mgr.registerChecker<IteratorModeling>();
851afb13afcSAdam Balogh }
852afb13afcSAdam Balogh
shouldRegisterIteratorModeling(const CheckerManager & mgr)853bda3dd0dSKirstóf Umann bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
854afb13afcSAdam Balogh return true;
855afb13afcSAdam Balogh }
856