1e5dd7070Spatrick //===-- IteratorModeling.cpp --------------------------------------*- C++ -*--//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9ec727ea7Spatrick // Defines a modeling-checker for modeling STL iterator-like iterators.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick //
13e5dd7070Spatrick // In the code, iterator can be represented as a:
14e5dd7070Spatrick // * type-I: typedef-ed pointer. Operations over such iterator, such as
15e5dd7070Spatrick // comparisons or increments, are modeled straightforwardly by the
16e5dd7070Spatrick // analyzer.
17e5dd7070Spatrick // * type-II: structure with its method bodies available. Operations over such
18e5dd7070Spatrick // iterator are inlined by the analyzer, and results of modeling
19e5dd7070Spatrick // these operations are exposing implementation details of the
20e5dd7070Spatrick // iterators, which is not necessarily helping.
21e5dd7070Spatrick // * type-III: completely opaque structure. Operations over such iterator are
22e5dd7070Spatrick // modeled conservatively, producing conjured symbols everywhere.
23e5dd7070Spatrick //
24e5dd7070Spatrick // To handle all these types in a common way we introduce a structure called
25e5dd7070Spatrick // IteratorPosition which is an abstraction of the position the iterator
26e5dd7070Spatrick // represents using symbolic expressions. The checker handles all the
27e5dd7070Spatrick // operations on this structure.
28e5dd7070Spatrick //
29e5dd7070Spatrick // Additionally, depending on the circumstances, operators of types II and III
30e5dd7070Spatrick // can be represented as:
31e5dd7070Spatrick // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
32e5dd7070Spatrick // from conservatively evaluated methods such as
33e5dd7070Spatrick // `.begin()`.
34e5dd7070Spatrick // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
35e5dd7070Spatrick // variables or temporaries, when the iterator object is
36e5dd7070Spatrick // currently treated as an lvalue.
37e5dd7070Spatrick // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
38e5dd7070Spatrick // iterator object is treated as an rvalue taken of a
39e5dd7070Spatrick // particular lvalue, eg. a copy of "type-a" iterator
40e5dd7070Spatrick // object, or an iterator that existed before the
41e5dd7070Spatrick // analysis has started.
42e5dd7070Spatrick //
43e5dd7070Spatrick // To handle any of these three different representations stored in an SVal we
44e5dd7070Spatrick // use setter and getters functions which separate the three cases. To store
45e5dd7070Spatrick // them we use a pointer union of symbol and memory region.
46e5dd7070Spatrick //
47e5dd7070Spatrick // The checker works the following way: We record the begin and the
48e5dd7070Spatrick // past-end iterator for all containers whenever their `.begin()` and `.end()`
49e5dd7070Spatrick // are called. Since the Constraint Manager cannot handle such SVals we need
50e5dd7070Spatrick // to take over its role. We post-check equality and non-equality comparisons
51e5dd7070Spatrick // and record that the two sides are equal if we are in the 'equal' branch
52e5dd7070Spatrick // (true-branch for `==` and false-branch for `!=`).
53e5dd7070Spatrick //
54e5dd7070Spatrick // In case of type-I or type-II iterators we get a concrete integer as a result
55e5dd7070Spatrick // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
56e5dd7070Spatrick // this latter case we record the symbol and reload it in evalAssume() and do
57e5dd7070Spatrick // the propagation there. We also handle (maybe double) negated comparisons
58e5dd7070Spatrick // which are represented in the form of (x == 0 or x != 0) where x is the
59e5dd7070Spatrick // comparison itself.
60e5dd7070Spatrick //
61e5dd7070Spatrick // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
62e5dd7070Spatrick // we only use expressions of the format S, S+n or S-n for iterator positions
63e5dd7070Spatrick // where S is a conjured symbol and n is an unsigned concrete integer. When
64e5dd7070Spatrick // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
65e5dd7070Spatrick // a constraint which we later retrieve when doing an actual comparison.
66e5dd7070Spatrick
67e5dd7070Spatrick #include "clang/AST/DeclTemplate.h"
68*12c85518Srobert #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
69e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
70e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
71*12c85518Srobert #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
72e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
73e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
74e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
75e5dd7070Spatrick
76e5dd7070Spatrick #include "Iterator.h"
77e5dd7070Spatrick
78e5dd7070Spatrick #include <utility>
79e5dd7070Spatrick
80e5dd7070Spatrick using namespace clang;
81e5dd7070Spatrick using namespace ento;
82e5dd7070Spatrick using namespace iterator;
83e5dd7070Spatrick
84e5dd7070Spatrick namespace {
85e5dd7070Spatrick
86e5dd7070Spatrick class IteratorModeling
87ec727ea7Spatrick : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
88ec727ea7Spatrick check::PostStmt<BinaryOperator>,
89ec727ea7Spatrick check::PostStmt<MaterializeTemporaryExpr>,
90e5dd7070Spatrick check::Bind, check::LiveSymbols, check::DeadSymbols> {
91e5dd7070Spatrick
92ec727ea7Spatrick using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
93ec727ea7Spatrick SVal, SVal, SVal) const;
94ec727ea7Spatrick
95ec727ea7Spatrick void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
96ec727ea7Spatrick OverloadedOperatorKind Op) const;
97ec727ea7Spatrick void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call,
98ec727ea7Spatrick const Expr *OrigExpr,
99ec727ea7Spatrick const AdvanceFn *Handler) const;
100ec727ea7Spatrick
101e5dd7070Spatrick void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
102e5dd7070Spatrick const SVal &LVal, const SVal &RVal,
103e5dd7070Spatrick OverloadedOperatorKind Op) const;
104e5dd7070Spatrick void processComparison(CheckerContext &C, ProgramStateRef State,
105e5dd7070Spatrick SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
106e5dd7070Spatrick OverloadedOperatorKind Op) const;
107e5dd7070Spatrick void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
108e5dd7070Spatrick bool Postfix) const;
109e5dd7070Spatrick void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
110e5dd7070Spatrick bool Postfix) const;
111e5dd7070Spatrick void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
112e5dd7070Spatrick OverloadedOperatorKind Op, const SVal &RetVal,
113a9ac8606Spatrick const SVal &Iterator, const SVal &Amount) const;
114ec727ea7Spatrick void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
115ec727ea7Spatrick OverloadedOperatorKind OK, SVal Offset) const;
116ec727ea7Spatrick void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
117ec727ea7Spatrick SVal Amount) const;
118ec727ea7Spatrick void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
119ec727ea7Spatrick SVal Amount) const;
120ec727ea7Spatrick void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
121ec727ea7Spatrick SVal Amount) const;
122e5dd7070Spatrick void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
123e5dd7070Spatrick const MemRegion *Cont) const;
124ec727ea7Spatrick bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
125e5dd7070Spatrick void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
126e5dd7070Spatrick const char *Sep) const override;
127e5dd7070Spatrick
128ec727ea7Spatrick // std::advance, std::prev & std::next
129ec727ea7Spatrick CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
130ec727ea7Spatrick // template<class InputIt, class Distance>
131ec727ea7Spatrick // void advance(InputIt& it, Distance n);
132ec727ea7Spatrick {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance},
133ec727ea7Spatrick
134ec727ea7Spatrick // template<class BidirIt>
135ec727ea7Spatrick // BidirIt prev(
136ec727ea7Spatrick // BidirIt it,
137ec727ea7Spatrick // typename std::iterator_traits<BidirIt>::difference_type n = 1);
138ec727ea7Spatrick {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev},
139ec727ea7Spatrick
140ec727ea7Spatrick // template<class ForwardIt>
141ec727ea7Spatrick // ForwardIt next(
142ec727ea7Spatrick // ForwardIt it,
143ec727ea7Spatrick // typename std::iterator_traits<ForwardIt>::difference_type n = 1);
144ec727ea7Spatrick {{{"std", "next"}, 2}, &IteratorModeling::handleNext},
145ec727ea7Spatrick };
146ec727ea7Spatrick
147e5dd7070Spatrick public:
148ec727ea7Spatrick IteratorModeling() = default;
149e5dd7070Spatrick
150e5dd7070Spatrick void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
151e5dd7070Spatrick void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
152ec727ea7Spatrick void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const;
153ec727ea7Spatrick void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const;
154e5dd7070Spatrick void checkPostStmt(const MaterializeTemporaryExpr *MTE,
155e5dd7070Spatrick CheckerContext &C) const;
156e5dd7070Spatrick void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
157e5dd7070Spatrick void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
158e5dd7070Spatrick };
159e5dd7070Spatrick
160e5dd7070Spatrick bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
161ec727ea7Spatrick bool isSimpleComparisonOperator(BinaryOperatorKind OK);
162e5dd7070Spatrick ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
163e5dd7070Spatrick ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
164e5dd7070Spatrick SymbolRef Sym2, bool Equal);
165e5dd7070Spatrick bool isBoundThroughLazyCompoundVal(const Environment &Env,
166e5dd7070Spatrick const MemRegion *Reg);
167ec727ea7Spatrick const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
168e5dd7070Spatrick
169e5dd7070Spatrick } // namespace
170e5dd7070Spatrick
checkPostCall(const CallEvent & Call,CheckerContext & C) const171e5dd7070Spatrick void IteratorModeling::checkPostCall(const CallEvent &Call,
172e5dd7070Spatrick CheckerContext &C) const {
173e5dd7070Spatrick // Record new iterator positions and iterator position changes
174e5dd7070Spatrick const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
175e5dd7070Spatrick if (!Func)
176e5dd7070Spatrick return;
177e5dd7070Spatrick
178e5dd7070Spatrick if (Func->isOverloadedOperator()) {
179e5dd7070Spatrick const auto Op = Func->getOverloadedOperator();
180ec727ea7Spatrick handleOverloadedOperator(C, Call, Op);
181e5dd7070Spatrick return;
182e5dd7070Spatrick }
183e5dd7070Spatrick
184e5dd7070Spatrick const auto *OrigExpr = Call.getOriginExpr();
185e5dd7070Spatrick if (!OrigExpr)
186e5dd7070Spatrick return;
187e5dd7070Spatrick
188ec727ea7Spatrick const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call);
189ec727ea7Spatrick if (Handler) {
190ec727ea7Spatrick handleAdvanceLikeFunction(C, Call, OrigExpr, Handler);
191ec727ea7Spatrick return;
192ec727ea7Spatrick }
193ec727ea7Spatrick
194e5dd7070Spatrick if (!isIteratorType(Call.getResultType()))
195e5dd7070Spatrick return;
196e5dd7070Spatrick
197e5dd7070Spatrick auto State = C.getState();
198e5dd7070Spatrick
199e5dd7070Spatrick // Already bound to container?
200e5dd7070Spatrick if (getIteratorPosition(State, Call.getReturnValue()))
201e5dd7070Spatrick return;
202e5dd7070Spatrick
203e5dd7070Spatrick // Copy-like and move constructors
204e5dd7070Spatrick if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
205e5dd7070Spatrick if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
206e5dd7070Spatrick State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
207e5dd7070Spatrick if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
208e5dd7070Spatrick State = removeIteratorPosition(State, Call.getArgSVal(0));
209e5dd7070Spatrick }
210e5dd7070Spatrick C.addTransition(State);
211e5dd7070Spatrick return;
212e5dd7070Spatrick }
213e5dd7070Spatrick }
214e5dd7070Spatrick
215e5dd7070Spatrick // Assumption: if return value is an iterator which is not yet bound to a
216ec727ea7Spatrick // container, then look for the first iterator argument of the
217ec727ea7Spatrick // same type as the return value and bind the return value to
218ec727ea7Spatrick // the same container. This approach works for STL algorithms.
219e5dd7070Spatrick // FIXME: Add a more conservative mode
220e5dd7070Spatrick for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
221ec727ea7Spatrick if (isIteratorType(Call.getArgExpr(i)->getType()) &&
222ec727ea7Spatrick Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
223ec727ea7Spatrick C.getASTContext()).getTypePtr() ==
224ec727ea7Spatrick Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) {
225e5dd7070Spatrick if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
226e5dd7070Spatrick assignToContainer(C, OrigExpr, Call.getReturnValue(),
227e5dd7070Spatrick Pos->getContainer());
228e5dd7070Spatrick return;
229e5dd7070Spatrick }
230e5dd7070Spatrick }
231e5dd7070Spatrick }
232e5dd7070Spatrick }
233e5dd7070Spatrick
checkBind(SVal Loc,SVal Val,const Stmt * S,CheckerContext & C) const234e5dd7070Spatrick void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
235e5dd7070Spatrick CheckerContext &C) const {
236e5dd7070Spatrick auto State = C.getState();
237e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, Val);
238e5dd7070Spatrick if (Pos) {
239e5dd7070Spatrick State = setIteratorPosition(State, Loc, *Pos);
240e5dd7070Spatrick C.addTransition(State);
241e5dd7070Spatrick } else {
242e5dd7070Spatrick const auto *OldPos = getIteratorPosition(State, Loc);
243e5dd7070Spatrick if (OldPos) {
244e5dd7070Spatrick State = removeIteratorPosition(State, Loc);
245e5dd7070Spatrick C.addTransition(State);
246e5dd7070Spatrick }
247e5dd7070Spatrick }
248e5dd7070Spatrick }
249e5dd7070Spatrick
checkPostStmt(const UnaryOperator * UO,CheckerContext & C) const250ec727ea7Spatrick void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
251ec727ea7Spatrick CheckerContext &C) const {
252ec727ea7Spatrick UnaryOperatorKind OK = UO->getOpcode();
253ec727ea7Spatrick if (!isIncrementOperator(OK) && !isDecrementOperator(OK))
254ec727ea7Spatrick return;
255ec727ea7Spatrick
256ec727ea7Spatrick auto &SVB = C.getSValBuilder();
257ec727ea7Spatrick handlePtrIncrOrDecr(C, UO->getSubExpr(),
258ec727ea7Spatrick isIncrementOperator(OK) ? OO_Plus : OO_Minus,
259ec727ea7Spatrick SVB.makeArrayIndex(1));
260ec727ea7Spatrick }
261ec727ea7Spatrick
checkPostStmt(const BinaryOperator * BO,CheckerContext & C) const262ec727ea7Spatrick void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
263ec727ea7Spatrick CheckerContext &C) const {
264a9ac8606Spatrick const ProgramStateRef State = C.getState();
265a9ac8606Spatrick const BinaryOperatorKind OK = BO->getOpcode();
266a9ac8606Spatrick const Expr *const LHS = BO->getLHS();
267a9ac8606Spatrick const Expr *const RHS = BO->getRHS();
268a9ac8606Spatrick const SVal LVal = State->getSVal(LHS, C.getLocationContext());
269a9ac8606Spatrick const SVal RVal = State->getSVal(RHS, C.getLocationContext());
270ec727ea7Spatrick
271ec727ea7Spatrick if (isSimpleComparisonOperator(BO->getOpcode())) {
272ec727ea7Spatrick SVal Result = State->getSVal(BO, C.getLocationContext());
273ec727ea7Spatrick handleComparison(C, BO, Result, LVal, RVal,
274ec727ea7Spatrick BinaryOperator::getOverloadedOperator(OK));
275ec727ea7Spatrick } else if (isRandomIncrOrDecrOperator(OK)) {
276a9ac8606Spatrick // In case of operator+ the iterator can be either on the LHS (eg.: it + 1),
277a9ac8606Spatrick // or on the RHS (eg.: 1 + it). Both cases are modeled.
278a9ac8606Spatrick const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType();
279a9ac8606Spatrick const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS;
280a9ac8606Spatrick const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS;
281a9ac8606Spatrick
282a9ac8606Spatrick // The non-iterator side must have an integral or enumeration type.
283a9ac8606Spatrick if (!AmountExpr->getType()->isIntegralOrEnumerationType())
284a9ac8606Spatrick return;
285a9ac8606Spatrick const SVal &AmountVal = IsIterOnLHS ? RVal : LVal;
286a9ac8606Spatrick handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
287a9ac8606Spatrick AmountVal);
288ec727ea7Spatrick }
289ec727ea7Spatrick }
290ec727ea7Spatrick
checkPostStmt(const MaterializeTemporaryExpr * MTE,CheckerContext & C) const291e5dd7070Spatrick void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
292e5dd7070Spatrick CheckerContext &C) const {
293e5dd7070Spatrick /* Transfer iterator state to temporary objects */
294e5dd7070Spatrick auto State = C.getState();
295e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
296e5dd7070Spatrick if (!Pos)
297e5dd7070Spatrick return;
298e5dd7070Spatrick State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
299e5dd7070Spatrick C.addTransition(State);
300e5dd7070Spatrick }
301e5dd7070Spatrick
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const302e5dd7070Spatrick void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
303e5dd7070Spatrick SymbolReaper &SR) const {
304ec727ea7Spatrick // Keep symbolic expressions of iterator positions alive
305e5dd7070Spatrick auto RegionMap = State->get<IteratorRegionMap>();
306e5dd7070Spatrick for (const auto &Reg : RegionMap) {
307e5dd7070Spatrick const auto Offset = Reg.second.getOffset();
308e5dd7070Spatrick for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
309e5dd7070Spatrick if (isa<SymbolData>(*i))
310e5dd7070Spatrick SR.markLive(*i);
311e5dd7070Spatrick }
312e5dd7070Spatrick
313e5dd7070Spatrick auto SymbolMap = State->get<IteratorSymbolMap>();
314e5dd7070Spatrick for (const auto &Sym : SymbolMap) {
315e5dd7070Spatrick const auto Offset = Sym.second.getOffset();
316e5dd7070Spatrick for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
317e5dd7070Spatrick if (isa<SymbolData>(*i))
318e5dd7070Spatrick SR.markLive(*i);
319e5dd7070Spatrick }
320e5dd7070Spatrick
321e5dd7070Spatrick }
322e5dd7070Spatrick
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const323e5dd7070Spatrick void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
324e5dd7070Spatrick CheckerContext &C) const {
325e5dd7070Spatrick // Cleanup
326e5dd7070Spatrick auto State = C.getState();
327e5dd7070Spatrick
328e5dd7070Spatrick auto RegionMap = State->get<IteratorRegionMap>();
329e5dd7070Spatrick for (const auto &Reg : RegionMap) {
330e5dd7070Spatrick if (!SR.isLiveRegion(Reg.first)) {
331e5dd7070Spatrick // The region behind the `LazyCompoundVal` is often cleaned up before
332e5dd7070Spatrick // the `LazyCompoundVal` itself. If there are iterator positions keyed
333e5dd7070Spatrick // by these regions their cleanup must be deferred.
334e5dd7070Spatrick if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
335e5dd7070Spatrick State = State->remove<IteratorRegionMap>(Reg.first);
336e5dd7070Spatrick }
337e5dd7070Spatrick }
338e5dd7070Spatrick }
339e5dd7070Spatrick
340e5dd7070Spatrick auto SymbolMap = State->get<IteratorSymbolMap>();
341e5dd7070Spatrick for (const auto &Sym : SymbolMap) {
342e5dd7070Spatrick if (!SR.isLive(Sym.first)) {
343e5dd7070Spatrick State = State->remove<IteratorSymbolMap>(Sym.first);
344e5dd7070Spatrick }
345e5dd7070Spatrick }
346e5dd7070Spatrick
347ec727ea7Spatrick C.addTransition(State);
348e5dd7070Spatrick }
349ec727ea7Spatrick
350ec727ea7Spatrick void
handleOverloadedOperator(CheckerContext & C,const CallEvent & Call,OverloadedOperatorKind Op) const351ec727ea7Spatrick IteratorModeling::handleOverloadedOperator(CheckerContext &C,
352ec727ea7Spatrick const CallEvent &Call,
353ec727ea7Spatrick OverloadedOperatorKind Op) const {
354ec727ea7Spatrick if (isSimpleComparisonOperator(Op)) {
355ec727ea7Spatrick const auto *OrigExpr = Call.getOriginExpr();
356ec727ea7Spatrick if (!OrigExpr)
357ec727ea7Spatrick return;
358ec727ea7Spatrick
359ec727ea7Spatrick if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
360ec727ea7Spatrick handleComparison(C, OrigExpr, Call.getReturnValue(),
361ec727ea7Spatrick InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
362ec727ea7Spatrick return;
363ec727ea7Spatrick }
364ec727ea7Spatrick
365ec727ea7Spatrick handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
366ec727ea7Spatrick Call.getArgSVal(1), Op);
367ec727ea7Spatrick return;
368ec727ea7Spatrick } else if (isRandomIncrOrDecrOperator(Op)) {
369ec727ea7Spatrick const auto *OrigExpr = Call.getOriginExpr();
370ec727ea7Spatrick if (!OrigExpr)
371ec727ea7Spatrick return;
372ec727ea7Spatrick
373ec727ea7Spatrick if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
374ec727ea7Spatrick if (Call.getNumArgs() >= 1 &&
375ec727ea7Spatrick Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
376ec727ea7Spatrick handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
377ec727ea7Spatrick InstCall->getCXXThisVal(), Call.getArgSVal(0));
378ec727ea7Spatrick return;
379ec727ea7Spatrick }
380a9ac8606Spatrick } else if (Call.getNumArgs() >= 2) {
381a9ac8606Spatrick const Expr *FirstArg = Call.getArgExpr(0);
382a9ac8606Spatrick const Expr *SecondArg = Call.getArgExpr(1);
383a9ac8606Spatrick const QualType FirstType = FirstArg->getType();
384a9ac8606Spatrick const QualType SecondType = SecondArg->getType();
385a9ac8606Spatrick
386a9ac8606Spatrick if (FirstType->isIntegralOrEnumerationType() ||
387a9ac8606Spatrick SecondType->isIntegralOrEnumerationType()) {
388a9ac8606Spatrick // In case of operator+ the iterator can be either on the LHS (eg.:
389a9ac8606Spatrick // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled.
390a9ac8606Spatrick const bool IsIterFirst = FirstType->isStructureOrClassType();
391a9ac8606Spatrick const SVal FirstArg = Call.getArgSVal(0);
392a9ac8606Spatrick const SVal SecondArg = Call.getArgSVal(1);
393a9ac8606Spatrick const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg;
394a9ac8606Spatrick const SVal &Amount = IsIterFirst ? SecondArg : FirstArg;
395a9ac8606Spatrick
396ec727ea7Spatrick handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
397a9ac8606Spatrick Iterator, Amount);
398ec727ea7Spatrick return;
399ec727ea7Spatrick }
400ec727ea7Spatrick }
401ec727ea7Spatrick } else if (isIncrementOperator(Op)) {
402ec727ea7Spatrick if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
403ec727ea7Spatrick handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
404ec727ea7Spatrick Call.getNumArgs());
405ec727ea7Spatrick return;
406ec727ea7Spatrick }
407ec727ea7Spatrick
408ec727ea7Spatrick handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
409ec727ea7Spatrick Call.getNumArgs());
410ec727ea7Spatrick return;
411ec727ea7Spatrick } else if (isDecrementOperator(Op)) {
412ec727ea7Spatrick if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
413ec727ea7Spatrick handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
414ec727ea7Spatrick Call.getNumArgs());
415ec727ea7Spatrick return;
416ec727ea7Spatrick }
417ec727ea7Spatrick
418ec727ea7Spatrick handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
419ec727ea7Spatrick Call.getNumArgs());
420ec727ea7Spatrick return;
421e5dd7070Spatrick }
422e5dd7070Spatrick }
423e5dd7070Spatrick
424ec727ea7Spatrick void
handleAdvanceLikeFunction(CheckerContext & C,const CallEvent & Call,const Expr * OrigExpr,const AdvanceFn * Handler) const425ec727ea7Spatrick IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
426ec727ea7Spatrick const CallEvent &Call,
427ec727ea7Spatrick const Expr *OrigExpr,
428ec727ea7Spatrick const AdvanceFn *Handler) const {
429ec727ea7Spatrick if (!C.wasInlined) {
430ec727ea7Spatrick (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
431ec727ea7Spatrick Call.getArgSVal(0), Call.getArgSVal(1));
432ec727ea7Spatrick return;
433ec727ea7Spatrick }
434ec727ea7Spatrick
435ec727ea7Spatrick // If std::advance() was inlined, but a non-standard function it calls inside
436ec727ea7Spatrick // was not, then we have to model it explicitly
437ec727ea7Spatrick const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
438ec727ea7Spatrick if (IdInfo) {
439ec727ea7Spatrick if (IdInfo->getName() == "advance") {
440ec727ea7Spatrick if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
441ec727ea7Spatrick (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
442ec727ea7Spatrick Call.getArgSVal(0), Call.getArgSVal(1));
443ec727ea7Spatrick }
444ec727ea7Spatrick }
445ec727ea7Spatrick }
446e5dd7070Spatrick }
447e5dd7070Spatrick
handleComparison(CheckerContext & C,const Expr * CE,SVal RetVal,const SVal & LVal,const SVal & RVal,OverloadedOperatorKind Op) const448e5dd7070Spatrick void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
449e5dd7070Spatrick SVal RetVal, const SVal &LVal,
450e5dd7070Spatrick const SVal &RVal,
451e5dd7070Spatrick OverloadedOperatorKind Op) const {
452e5dd7070Spatrick // Record the operands and the operator of the comparison for the next
453e5dd7070Spatrick // evalAssume, if the result is a symbolic expression. If it is a concrete
454e5dd7070Spatrick // value (only one branch is possible), then transfer the state between
455e5dd7070Spatrick // the operands according to the operator and the result
456e5dd7070Spatrick auto State = C.getState();
457e5dd7070Spatrick const auto *LPos = getIteratorPosition(State, LVal);
458e5dd7070Spatrick const auto *RPos = getIteratorPosition(State, RVal);
459e5dd7070Spatrick const MemRegion *Cont = nullptr;
460e5dd7070Spatrick if (LPos) {
461e5dd7070Spatrick Cont = LPos->getContainer();
462e5dd7070Spatrick } else if (RPos) {
463e5dd7070Spatrick Cont = RPos->getContainer();
464e5dd7070Spatrick }
465e5dd7070Spatrick if (!Cont)
466e5dd7070Spatrick return;
467e5dd7070Spatrick
468ec727ea7Spatrick // At least one of the iterators has recorded positions. If one of them does
469e5dd7070Spatrick // not then create a new symbol for the offset.
470e5dd7070Spatrick SymbolRef Sym;
471e5dd7070Spatrick if (!LPos || !RPos) {
472e5dd7070Spatrick auto &SymMgr = C.getSymbolManager();
473e5dd7070Spatrick Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
474e5dd7070Spatrick C.getASTContext().LongTy, C.blockCount());
475e5dd7070Spatrick State = assumeNoOverflow(State, Sym, 4);
476e5dd7070Spatrick }
477e5dd7070Spatrick
478e5dd7070Spatrick if (!LPos) {
479e5dd7070Spatrick State = setIteratorPosition(State, LVal,
480e5dd7070Spatrick IteratorPosition::getPosition(Cont, Sym));
481e5dd7070Spatrick LPos = getIteratorPosition(State, LVal);
482e5dd7070Spatrick } else if (!RPos) {
483e5dd7070Spatrick State = setIteratorPosition(State, RVal,
484e5dd7070Spatrick IteratorPosition::getPosition(Cont, Sym));
485e5dd7070Spatrick RPos = getIteratorPosition(State, RVal);
486e5dd7070Spatrick }
487e5dd7070Spatrick
488a9ac8606Spatrick // If the value for which we just tried to set a new iterator position is
489a9ac8606Spatrick // an `SVal`for which no iterator position can be set then the setting was
490a9ac8606Spatrick // unsuccessful. We cannot handle the comparison in this case.
491a9ac8606Spatrick if (!LPos || !RPos)
492a9ac8606Spatrick return;
493a9ac8606Spatrick
494ec727ea7Spatrick // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
495e5dd7070Spatrick // instead.
496e5dd7070Spatrick if (RetVal.isUnknown()) {
497e5dd7070Spatrick auto &SymMgr = C.getSymbolManager();
498e5dd7070Spatrick auto *LCtx = C.getLocationContext();
499e5dd7070Spatrick RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
500e5dd7070Spatrick CE, LCtx, C.getASTContext().BoolTy, C.blockCount()));
501e5dd7070Spatrick State = State->BindExpr(CE, LCtx, RetVal);
502e5dd7070Spatrick }
503e5dd7070Spatrick
504e5dd7070Spatrick processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
505e5dd7070Spatrick }
506e5dd7070Spatrick
processComparison(CheckerContext & C,ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,const SVal & RetVal,OverloadedOperatorKind Op) const507e5dd7070Spatrick void IteratorModeling::processComparison(CheckerContext &C,
508e5dd7070Spatrick ProgramStateRef State, SymbolRef Sym1,
509e5dd7070Spatrick SymbolRef Sym2, const SVal &RetVal,
510e5dd7070Spatrick OverloadedOperatorKind Op) const {
511e5dd7070Spatrick if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
512e5dd7070Spatrick if ((State = relateSymbols(State, Sym1, Sym2,
513e5dd7070Spatrick (Op == OO_EqualEqual) ==
514e5dd7070Spatrick (TruthVal->getValue() != 0)))) {
515e5dd7070Spatrick C.addTransition(State);
516e5dd7070Spatrick } else {
517e5dd7070Spatrick C.generateSink(State, C.getPredecessor());
518e5dd7070Spatrick }
519e5dd7070Spatrick return;
520e5dd7070Spatrick }
521e5dd7070Spatrick
522e5dd7070Spatrick const auto ConditionVal = RetVal.getAs<DefinedSVal>();
523e5dd7070Spatrick if (!ConditionVal)
524e5dd7070Spatrick return;
525e5dd7070Spatrick
526e5dd7070Spatrick if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
527e5dd7070Spatrick StateTrue = StateTrue->assume(*ConditionVal, true);
528e5dd7070Spatrick C.addTransition(StateTrue);
529e5dd7070Spatrick }
530e5dd7070Spatrick
531e5dd7070Spatrick if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
532e5dd7070Spatrick StateFalse = StateFalse->assume(*ConditionVal, false);
533e5dd7070Spatrick C.addTransition(StateFalse);
534e5dd7070Spatrick }
535e5dd7070Spatrick }
536e5dd7070Spatrick
handleIncrement(CheckerContext & C,const SVal & RetVal,const SVal & Iter,bool Postfix) const537e5dd7070Spatrick void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
538e5dd7070Spatrick const SVal &Iter, bool Postfix) const {
539e5dd7070Spatrick // Increment the symbolic expressions which represents the position of the
540e5dd7070Spatrick // iterator
541e5dd7070Spatrick auto State = C.getState();
542e5dd7070Spatrick auto &BVF = C.getSymbolManager().getBasicVals();
543e5dd7070Spatrick
544e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, Iter);
545e5dd7070Spatrick if (!Pos)
546e5dd7070Spatrick return;
547e5dd7070Spatrick
548e5dd7070Spatrick auto NewState =
549e5dd7070Spatrick advancePosition(State, Iter, OO_Plus,
550e5dd7070Spatrick nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
551e5dd7070Spatrick assert(NewState &&
552e5dd7070Spatrick "Advancing position by concrete int should always be successful");
553e5dd7070Spatrick
554e5dd7070Spatrick const auto *NewPos = getIteratorPosition(NewState, Iter);
555e5dd7070Spatrick assert(NewPos &&
556e5dd7070Spatrick "Iterator should have position after successful advancement");
557e5dd7070Spatrick
558e5dd7070Spatrick State = setIteratorPosition(State, Iter, *NewPos);
559e5dd7070Spatrick State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
560e5dd7070Spatrick C.addTransition(State);
561e5dd7070Spatrick }
562e5dd7070Spatrick
handleDecrement(CheckerContext & C,const SVal & RetVal,const SVal & Iter,bool Postfix) const563e5dd7070Spatrick void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
564e5dd7070Spatrick const SVal &Iter, bool Postfix) const {
565e5dd7070Spatrick // Decrement the symbolic expressions which represents the position of the
566e5dd7070Spatrick // iterator
567e5dd7070Spatrick auto State = C.getState();
568e5dd7070Spatrick auto &BVF = C.getSymbolManager().getBasicVals();
569e5dd7070Spatrick
570e5dd7070Spatrick const auto *Pos = getIteratorPosition(State, Iter);
571e5dd7070Spatrick if (!Pos)
572e5dd7070Spatrick return;
573e5dd7070Spatrick
574e5dd7070Spatrick auto NewState =
575e5dd7070Spatrick advancePosition(State, Iter, OO_Minus,
576e5dd7070Spatrick nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
577e5dd7070Spatrick assert(NewState &&
578e5dd7070Spatrick "Advancing position by concrete int should always be successful");
579e5dd7070Spatrick
580e5dd7070Spatrick const auto *NewPos = getIteratorPosition(NewState, Iter);
581e5dd7070Spatrick assert(NewPos &&
582e5dd7070Spatrick "Iterator should have position after successful advancement");
583e5dd7070Spatrick
584e5dd7070Spatrick State = setIteratorPosition(State, Iter, *NewPos);
585e5dd7070Spatrick State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
586e5dd7070Spatrick C.addTransition(State);
587e5dd7070Spatrick }
588e5dd7070Spatrick
handleRandomIncrOrDecr(CheckerContext & C,const Expr * CE,OverloadedOperatorKind Op,const SVal & RetVal,const SVal & Iterator,const SVal & Amount) const589a9ac8606Spatrick void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
590e5dd7070Spatrick OverloadedOperatorKind Op,
591e5dd7070Spatrick const SVal &RetVal,
592a9ac8606Spatrick const SVal &Iterator,
593a9ac8606Spatrick const SVal &Amount) const {
594e5dd7070Spatrick // Increment or decrement the symbolic expressions which represents the
595e5dd7070Spatrick // position of the iterator
596e5dd7070Spatrick auto State = C.getState();
597e5dd7070Spatrick
598a9ac8606Spatrick const auto *Pos = getIteratorPosition(State, Iterator);
599e5dd7070Spatrick if (!Pos)
600e5dd7070Spatrick return;
601e5dd7070Spatrick
602a9ac8606Spatrick const auto *Value = &Amount;
603a9ac8606Spatrick SVal Val;
604a9ac8606Spatrick if (auto LocAmount = Amount.getAs<Loc>()) {
605a9ac8606Spatrick Val = State->getRawSVal(*LocAmount);
606a9ac8606Spatrick Value = &Val;
607e5dd7070Spatrick }
608e5dd7070Spatrick
609a9ac8606Spatrick const auto &TgtVal =
610a9ac8606Spatrick (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
611e5dd7070Spatrick
612ec727ea7Spatrick // `AdvancedState` is a state where the position of `LHS` is advanced. We
613ec727ea7Spatrick // only need this state to retrieve the new position, but we do not want
614ec727ea7Spatrick // to change the position of `LHS` (in every case).
615a9ac8606Spatrick auto AdvancedState = advancePosition(State, Iterator, Op, *Value);
616ec727ea7Spatrick if (AdvancedState) {
617a9ac8606Spatrick const auto *NewPos = getIteratorPosition(AdvancedState, Iterator);
618e5dd7070Spatrick assert(NewPos &&
619e5dd7070Spatrick "Iterator should have position after successful advancement");
620e5dd7070Spatrick
621ec727ea7Spatrick State = setIteratorPosition(State, TgtVal, *NewPos);
622e5dd7070Spatrick C.addTransition(State);
623e5dd7070Spatrick } else {
624e5dd7070Spatrick assignToContainer(C, CE, TgtVal, Pos->getContainer());
625e5dd7070Spatrick }
626e5dd7070Spatrick }
627e5dd7070Spatrick
handlePtrIncrOrDecr(CheckerContext & C,const Expr * Iterator,OverloadedOperatorKind OK,SVal Offset) const628ec727ea7Spatrick void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
629ec727ea7Spatrick const Expr *Iterator,
630ec727ea7Spatrick OverloadedOperatorKind OK,
631ec727ea7Spatrick SVal Offset) const {
632*12c85518Srobert if (!isa<DefinedSVal>(Offset))
633a9ac8606Spatrick return;
634a9ac8606Spatrick
635ec727ea7Spatrick QualType PtrType = Iterator->getType();
636ec727ea7Spatrick if (!PtrType->isPointerType())
637ec727ea7Spatrick return;
638ec727ea7Spatrick QualType ElementType = PtrType->getPointeeType();
639ec727ea7Spatrick
640ec727ea7Spatrick ProgramStateRef State = C.getState();
641ec727ea7Spatrick SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
642ec727ea7Spatrick
643ec727ea7Spatrick const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
644ec727ea7Spatrick if (!OldPos)
645e5dd7070Spatrick return;
646e5dd7070Spatrick
647ec727ea7Spatrick SVal NewVal;
648a9ac8606Spatrick if (OK == OO_Plus || OK == OO_PlusEqual) {
649ec727ea7Spatrick NewVal = State->getLValue(ElementType, Offset, OldVal);
650a9ac8606Spatrick } else {
651a9ac8606Spatrick auto &SVB = C.getSValBuilder();
652a9ac8606Spatrick SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>());
653ec727ea7Spatrick NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
654e5dd7070Spatrick }
655e5dd7070Spatrick
656ec727ea7Spatrick // `AdvancedState` is a state where the position of `Old` is advanced. We
657ec727ea7Spatrick // only need this state to retrieve the new position, but we do not want
658ec727ea7Spatrick // ever to change the position of `OldVal`.
659ec727ea7Spatrick auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
660ec727ea7Spatrick if (AdvancedState) {
661ec727ea7Spatrick const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
662ec727ea7Spatrick assert(NewPos &&
663ec727ea7Spatrick "Iterator should have position after successful advancement");
664e5dd7070Spatrick
665ec727ea7Spatrick ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
666ec727ea7Spatrick C.addTransition(NewState);
667ec727ea7Spatrick } else {
668ec727ea7Spatrick assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
669e5dd7070Spatrick }
670ec727ea7Spatrick }
671ec727ea7Spatrick
handleAdvance(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const672ec727ea7Spatrick void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
673ec727ea7Spatrick SVal RetVal, SVal Iter,
674ec727ea7Spatrick SVal Amount) const {
675ec727ea7Spatrick handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
676ec727ea7Spatrick }
677ec727ea7Spatrick
handlePrev(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const678ec727ea7Spatrick void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
679ec727ea7Spatrick SVal RetVal, SVal Iter, SVal Amount) const {
680ec727ea7Spatrick handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
681ec727ea7Spatrick }
682ec727ea7Spatrick
handleNext(CheckerContext & C,const Expr * CE,SVal RetVal,SVal Iter,SVal Amount) const683ec727ea7Spatrick void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
684ec727ea7Spatrick SVal RetVal, SVal Iter, SVal Amount) const {
685ec727ea7Spatrick handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
686e5dd7070Spatrick }
687e5dd7070Spatrick
assignToContainer(CheckerContext & C,const Expr * CE,const SVal & RetVal,const MemRegion * Cont) const688e5dd7070Spatrick void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
689e5dd7070Spatrick const SVal &RetVal,
690e5dd7070Spatrick const MemRegion *Cont) const {
691e5dd7070Spatrick Cont = Cont->getMostDerivedObjectRegion();
692e5dd7070Spatrick
693e5dd7070Spatrick auto State = C.getState();
694ec727ea7Spatrick const auto *LCtx = C.getLocationContext();
695ec727ea7Spatrick State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
696ec727ea7Spatrick
697e5dd7070Spatrick C.addTransition(State);
698e5dd7070Spatrick }
699e5dd7070Spatrick
noChangeInAdvance(CheckerContext & C,SVal Iter,const Expr * CE) const700ec727ea7Spatrick bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
701ec727ea7Spatrick const Expr *CE) const {
702ec727ea7Spatrick // Compare the iterator position before and after the call. (To be called
703ec727ea7Spatrick // from `checkPostCall()`.)
704ec727ea7Spatrick const auto StateAfter = C.getState();
705e5dd7070Spatrick
706ec727ea7Spatrick const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
707ec727ea7Spatrick // If we have no position after the call of `std::advance`, then we are not
708ec727ea7Spatrick // interested. (Modeling of an inlined `std::advance()` should not remove the
709ec727ea7Spatrick // position in any case.)
710ec727ea7Spatrick if (!PosAfter)
711ec727ea7Spatrick return false;
712e5dd7070Spatrick
713ec727ea7Spatrick const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
714ec727ea7Spatrick assert(N && "Any call should have a `CallEnter` node.");
715e5dd7070Spatrick
716ec727ea7Spatrick const auto StateBefore = N->getState();
717ec727ea7Spatrick const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
718a9ac8606Spatrick // FIXME: `std::advance()` should not create a new iterator position but
719a9ac8606Spatrick // change existing ones. However, in case of iterators implemented as
720a9ac8606Spatrick // pointers the handling of parameters in `std::advance()`-like
721a9ac8606Spatrick // functions is still incomplete which may result in cases where
722a9ac8606Spatrick // the new position is assigned to the wrong pointer. This causes
723a9ac8606Spatrick // crash if we use an assertion here.
724a9ac8606Spatrick if (!PosBefore)
725a9ac8606Spatrick return false;
726e5dd7070Spatrick
727ec727ea7Spatrick return PosBefore->getOffset() == PosAfter->getOffset();
728e5dd7070Spatrick }
729e5dd7070Spatrick
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const730e5dd7070Spatrick void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
731e5dd7070Spatrick const char *NL, const char *Sep) const {
732e5dd7070Spatrick auto SymbolMap = State->get<IteratorSymbolMap>();
733e5dd7070Spatrick auto RegionMap = State->get<IteratorRegionMap>();
734ec727ea7Spatrick // Use a counter to add newlines before every line except the first one.
735ec727ea7Spatrick unsigned Count = 0;
736e5dd7070Spatrick
737e5dd7070Spatrick if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
738e5dd7070Spatrick Out << Sep << "Iterator Positions :" << NL;
739e5dd7070Spatrick for (const auto &Sym : SymbolMap) {
740ec727ea7Spatrick if (Count++)
741ec727ea7Spatrick Out << NL;
742ec727ea7Spatrick
743e5dd7070Spatrick Sym.first->dumpToStream(Out);
744e5dd7070Spatrick Out << " : ";
745e5dd7070Spatrick const auto Pos = Sym.second;
746e5dd7070Spatrick Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
747e5dd7070Spatrick Pos.getContainer()->dumpToStream(Out);
748e5dd7070Spatrick Out<<" ; Offset == ";
749e5dd7070Spatrick Pos.getOffset()->dumpToStream(Out);
750e5dd7070Spatrick }
751e5dd7070Spatrick
752e5dd7070Spatrick for (const auto &Reg : RegionMap) {
753ec727ea7Spatrick if (Count++)
754ec727ea7Spatrick Out << NL;
755ec727ea7Spatrick
756e5dd7070Spatrick Reg.first->dumpToStream(Out);
757e5dd7070Spatrick Out << " : ";
758e5dd7070Spatrick const auto Pos = Reg.second;
759e5dd7070Spatrick Out << (Pos.isValid() ? "Valid" : "Invalid") << " ; Container == ";
760e5dd7070Spatrick Pos.getContainer()->dumpToStream(Out);
761e5dd7070Spatrick Out<<" ; Offset == ";
762e5dd7070Spatrick Pos.getOffset()->dumpToStream(Out);
763e5dd7070Spatrick }
764e5dd7070Spatrick }
765e5dd7070Spatrick }
766e5dd7070Spatrick
767e5dd7070Spatrick namespace {
768e5dd7070Spatrick
isSimpleComparisonOperator(OverloadedOperatorKind OK)769e5dd7070Spatrick bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
770e5dd7070Spatrick return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
771e5dd7070Spatrick }
772e5dd7070Spatrick
isSimpleComparisonOperator(BinaryOperatorKind OK)773ec727ea7Spatrick bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
774ec727ea7Spatrick return OK == BO_EQ || OK == BO_NE;
775e5dd7070Spatrick }
776e5dd7070Spatrick
removeIteratorPosition(ProgramStateRef State,const SVal & Val)777e5dd7070Spatrick ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
778e5dd7070Spatrick if (auto Reg = Val.getAsRegion()) {
779e5dd7070Spatrick Reg = Reg->getMostDerivedObjectRegion();
780e5dd7070Spatrick return State->remove<IteratorRegionMap>(Reg);
781e5dd7070Spatrick } else if (const auto Sym = Val.getAsSymbol()) {
782e5dd7070Spatrick return State->remove<IteratorSymbolMap>(Sym);
783e5dd7070Spatrick } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
784e5dd7070Spatrick return State->remove<IteratorRegionMap>(LCVal->getRegion());
785e5dd7070Spatrick }
786e5dd7070Spatrick return nullptr;
787e5dd7070Spatrick }
788e5dd7070Spatrick
relateSymbols(ProgramStateRef State,SymbolRef Sym1,SymbolRef Sym2,bool Equal)789e5dd7070Spatrick ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
790e5dd7070Spatrick SymbolRef Sym2, bool Equal) {
791e5dd7070Spatrick auto &SVB = State->getStateManager().getSValBuilder();
792e5dd7070Spatrick
793e5dd7070Spatrick // FIXME: This code should be reworked as follows:
794e5dd7070Spatrick // 1. Subtract the operands using evalBinOp().
795e5dd7070Spatrick // 2. Assume that the result doesn't overflow.
796e5dd7070Spatrick // 3. Compare the result to 0.
797e5dd7070Spatrick // 4. Assume the result of the comparison.
798e5dd7070Spatrick const auto comparison =
799e5dd7070Spatrick SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
800e5dd7070Spatrick nonloc::SymbolVal(Sym2), SVB.getConditionType());
801e5dd7070Spatrick
802*12c85518Srobert assert(isa<DefinedSVal>(comparison) &&
803e5dd7070Spatrick "Symbol comparison must be a `DefinedSVal`");
804e5dd7070Spatrick
805e5dd7070Spatrick auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
806e5dd7070Spatrick if (!NewState)
807e5dd7070Spatrick return nullptr;
808e5dd7070Spatrick
809e5dd7070Spatrick if (const auto CompSym = comparison.getAsSymbol()) {
810e5dd7070Spatrick assert(isa<SymIntExpr>(CompSym) &&
811e5dd7070Spatrick "Symbol comparison must be a `SymIntExpr`");
812e5dd7070Spatrick assert(BinaryOperator::isComparisonOp(
813e5dd7070Spatrick cast<SymIntExpr>(CompSym)->getOpcode()) &&
814e5dd7070Spatrick "Symbol comparison must be a comparison");
815e5dd7070Spatrick return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
816e5dd7070Spatrick }
817e5dd7070Spatrick
818e5dd7070Spatrick return NewState;
819e5dd7070Spatrick }
820e5dd7070Spatrick
isBoundThroughLazyCompoundVal(const Environment & Env,const MemRegion * Reg)821e5dd7070Spatrick bool isBoundThroughLazyCompoundVal(const Environment &Env,
822e5dd7070Spatrick const MemRegion *Reg) {
823e5dd7070Spatrick for (const auto &Binding : Env) {
824e5dd7070Spatrick if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
825e5dd7070Spatrick if (LCVal->getRegion() == Reg)
826e5dd7070Spatrick return true;
827e5dd7070Spatrick }
828e5dd7070Spatrick }
829e5dd7070Spatrick
830e5dd7070Spatrick return false;
831e5dd7070Spatrick }
832e5dd7070Spatrick
findCallEnter(const ExplodedNode * Node,const Expr * Call)833ec727ea7Spatrick const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
834ec727ea7Spatrick while (Node) {
835ec727ea7Spatrick ProgramPoint PP = Node->getLocation();
836ec727ea7Spatrick if (auto Enter = PP.getAs<CallEnter>()) {
837ec727ea7Spatrick if (Enter->getCallExpr() == Call)
838ec727ea7Spatrick break;
839e5dd7070Spatrick }
840e5dd7070Spatrick
841ec727ea7Spatrick Node = Node->getFirstPred();
842e5dd7070Spatrick }
843e5dd7070Spatrick
844ec727ea7Spatrick return Node;
845e5dd7070Spatrick }
846e5dd7070Spatrick
847e5dd7070Spatrick } // namespace
848e5dd7070Spatrick
registerIteratorModeling(CheckerManager & mgr)849e5dd7070Spatrick void ento::registerIteratorModeling(CheckerManager &mgr) {
850e5dd7070Spatrick mgr.registerChecker<IteratorModeling>();
851e5dd7070Spatrick }
852e5dd7070Spatrick
shouldRegisterIteratorModeling(const CheckerManager & mgr)853ec727ea7Spatrick bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
854e5dd7070Spatrick return true;
855e5dd7070Spatrick }
856