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