blob: 7ed11146397e500f62ff76ca370cf9f71f106960 [file] [log] [blame]
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Artem Dergachev8fa639e2017-05-29 15:03:20 +00006//
7//===----------------------------------------------------------------------===//
8//
9// Defines a checker for using iterators outside their range (past end). Usage
10// means here dereferencing, incrementing etc.
11//
12//===----------------------------------------------------------------------===//
13//
14// In the code, iterator can be represented as a:
15// * type-I: typedef-ed pointer. Operations over such iterator, such as
16// comparisons or increments, are modeled straightforwardly by the
17// analyzer.
18// * type-II: structure with its method bodies available. Operations over such
19// iterator are inlined by the analyzer, and results of modeling
20// these operations are exposing implementation details of the
21// iterators, which is not necessarily helping.
22// * type-III: completely opaque structure. Operations over such iterator are
23// modeled conservatively, producing conjured symbols everywhere.
24//
25// To handle all these types in a common way we introduce a structure called
26// IteratorPosition which is an abstraction of the position the iterator
27// represents using symbolic expressions. The checker handles all the
28// operations on this structure.
29//
30// Additionally, depending on the circumstances, operators of types II and III
31// can be represented as:
32// * type-IIa, type-IIIa: conjured structure symbols - when returned by value
33// from conservatively evaluated methods such as
34// `.begin()`.
35// * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
36// variables or temporaries, when the iterator object is
37// currently treated as an lvalue.
38// * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
39// iterator object is treated as an rvalue taken of a
40// particular lvalue, eg. a copy of "type-a" iterator
41// object, or an iterator that existed before the
42// analysis has started.
43//
44// To handle any of these three different representations stored in an SVal we
45// use setter and getters functions which separate the three cases. To store
46// them we use a pointer union of symbol and memory region.
47//
Adam Baloghb03ed5e2018-06-28 10:58:53 +000048// The checker works the following way: We record the begin and the
49// past-end iterator for all containers whenever their `.begin()` and `.end()`
50// are called. Since the Constraint Manager cannot handle such SVals we need
51// to take over its role. We post-check equality and non-equality comparisons
52// and record that the two sides are equal if we are in the 'equal' branch
53// (true-branch for `==` and false-branch for `!=`).
Artem Dergachev8fa639e2017-05-29 15:03:20 +000054//
55// In case of type-I or type-II iterators we get a concrete integer as a result
56// of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
57// this latter case we record the symbol and reload it in evalAssume() and do
58// the propagation there. We also handle (maybe double) negated comparisons
Adam Baloghb03ed5e2018-06-28 10:58:53 +000059// which are represented in the form of (x == 0 or x != 0) where x is the
Artem Dergachev8fa639e2017-05-29 15:03:20 +000060// comparison itself.
Adam Baloghb03ed5e2018-06-28 10:58:53 +000061//
62// Since `SimpleConstraintManager` cannot handle complex symbolic expressions
63// we only use expressions of the format S, S+n or S-n for iterator positions
64// where S is a conjured symbol and n is an unsigned concrete integer. When
65// making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
66// a constraint which we later retrieve when doing an actual comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +000067
Kristof Umann76a21502018-12-15 16:23:51 +000068#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
Adam Balogh2cfbe932018-08-28 08:41:15 +000069#include "clang/AST/DeclTemplate.h"
Artem Dergachev8fa639e2017-05-29 15:03:20 +000070#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
71#include "clang/StaticAnalyzer/Core/Checker.h"
72#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
73#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Csaba Dabise4bf4562019-08-22 00:36:42 +000074#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
Artem Dergachev8fa639e2017-05-29 15:03:20 +000075
Adam Balogh2cfbe932018-08-28 08:41:15 +000076#include <utility>
77
Artem Dergachev8fa639e2017-05-29 15:03:20 +000078using namespace clang;
79using namespace ento;
80
81namespace {
82
83// Abstract position of an iterator. This helps to handle all three kinds
84// of operators in a common way by using a symbolic position.
85struct IteratorPosition {
86private:
87
88 // Container the iterator belongs to
89 const MemRegion *Cont;
90
Adam Balogh2cfbe932018-08-28 08:41:15 +000091 // Whether iterator is valid
92 const bool Valid;
93
Artem Dergachev8fa639e2017-05-29 15:03:20 +000094 // Abstract offset
Adam Baloghb03ed5e2018-06-28 10:58:53 +000095 const SymbolRef Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +000096
Adam Balogh2cfbe932018-08-28 08:41:15 +000097 IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
98 : Cont(C), Valid(V), Offset(Of) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +000099
100public:
101 const MemRegion *getContainer() const { return Cont; }
Adam Balogh2cfbe932018-08-28 08:41:15 +0000102 bool isValid() const { return Valid; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000103 SymbolRef getOffset() const { return Offset; }
104
Adam Balogh2cfbe932018-08-28 08:41:15 +0000105 IteratorPosition invalidate() const {
106 return IteratorPosition(Cont, false, Offset);
107 }
108
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000109 static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000110 return IteratorPosition(C, true, Of);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000111 }
112
113 IteratorPosition setTo(SymbolRef NewOf) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000114 return IteratorPosition(Cont, Valid, NewOf);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000115 }
116
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000117 IteratorPosition reAssign(const MemRegion *NewCont) const {
118 return IteratorPosition(NewCont, Valid, Offset);
119 }
120
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000121 bool operator==(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000122 return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000123 }
124
125 bool operator!=(const IteratorPosition &X) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000126 return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000127 }
128
129 void Profile(llvm::FoldingSetNodeID &ID) const {
130 ID.AddPointer(Cont);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000131 ID.AddInteger(Valid);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000132 ID.Add(Offset);
133 }
134};
135
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000136// Structure to record the symbolic begin and end position of a container
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000137struct ContainerData {
138private:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000139 const SymbolRef Begin, End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000140
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000141 ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000142
143public:
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000144 static ContainerData fromBegin(SymbolRef B) {
145 return ContainerData(B, nullptr);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000146 }
147
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000148 static ContainerData fromEnd(SymbolRef E) {
149 return ContainerData(nullptr, E);
150 }
151
152 SymbolRef getBegin() const { return Begin; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000153 SymbolRef getEnd() const { return End; }
154
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000155 ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
156
157 ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000158
159 bool operator==(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000160 return Begin == X.Begin && End == X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000161 }
162
163 bool operator!=(const ContainerData &X) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000164 return Begin != X.Begin || End != X.End;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000165 }
166
167 void Profile(llvm::FoldingSetNodeID &ID) const {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000168 ID.Add(Begin);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000169 ID.Add(End);
170 }
171};
172
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000173class IteratorChecker
174 : public Checker<check::PreCall, check::PostCall,
Adam Balogh2e7cb342018-09-10 09:07:47 +0000175 check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
Adam Balogh0f88cae2019-11-08 08:56:31 +0100176 check::LiveSymbols, check::DeadSymbols, eval::Call> {
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000177
178 std::unique_ptr<BugType> OutOfRangeBugType;
Adam Balogh21583b72018-09-10 09:03:22 +0000179 std::unique_ptr<BugType> MismatchedBugType;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000180 std::unique_ptr<BugType> InvalidatedBugType;
Adam Balogh0f88cae2019-11-08 08:56:31 +0100181 std::unique_ptr<BugType> DebugMsgBugType;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000182
Adam Balogh54976e72019-04-23 07:15:55 +0000183 void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
184 const SVal &LVal, const SVal &RVal,
185 OverloadedOperatorKind Op) const;
186 void processComparison(CheckerContext &C, ProgramStateRef State,
187 SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
188 OverloadedOperatorKind Op) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000189 void verifyAccess(CheckerContext &C, const SVal &Val) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000190 void verifyDereference(CheckerContext &C, const SVal &Val) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000191 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
192 bool Postfix) const;
193 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
194 bool Postfix) const;
195 void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
196 const SVal &RetVal, const SVal &LHS,
197 const SVal &RHS) const;
198 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
199 const SVal &Cont) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000200 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
201 const SVal &Cont) const;
202 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
203 const MemRegion *Cont) const;
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000204 void handleAssign(CheckerContext &C, const SVal &Cont,
205 const Expr *CE = nullptr,
206 const SVal &OldCont = UndefinedVal()) const;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000207 void handleClear(CheckerContext &C, const SVal &Cont) const;
Adam Balogh9a48ba62018-09-10 09:06:31 +0000208 void handlePushBack(CheckerContext &C, const SVal &Cont) const;
209 void handlePopBack(CheckerContext &C, const SVal &Cont) const;
210 void handlePushFront(CheckerContext &C, const SVal &Cont) const;
211 void handlePopFront(CheckerContext &C, const SVal &Cont) const;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000212 void handleInsert(CheckerContext &C, const SVal &Iter) const;
213 void handleErase(CheckerContext &C, const SVal &Iter) const;
214 void handleErase(CheckerContext &C, const SVal &Iter1,
215 const SVal &Iter2) const;
216 void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
217 void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
218 const SVal &Iter2) const;
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000219 void verifyIncrement(CheckerContext &C, const SVal &Iter) const;
220 void verifyDecrement(CheckerContext &C, const SVal &Iter) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000221 void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000222 const SVal &LHS, const SVal &RHS) const;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000223 void verifyMatch(CheckerContext &C, const SVal &Iter,
224 const MemRegion *Cont) const;
Adam Balogh21583b72018-09-10 09:03:22 +0000225 void verifyMatch(CheckerContext &C, const SVal &Iter1,
226 const SVal &Iter2) const;
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000227 IteratorPosition advancePosition(CheckerContext &C, OverloadedOperatorKind Op,
228 const IteratorPosition &Pos,
229 const SVal &Distance) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000230 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
231 CheckerContext &C, ExplodedNode *ErrNode) const;
Adam Balogh21583b72018-09-10 09:03:22 +0000232 void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
233 const SVal &Val2, CheckerContext &C,
234 ExplodedNode *ErrNode) const;
Adam Balogh3659f7a2018-09-10 09:05:31 +0000235 void reportMismatchedBug(const StringRef &Message, const SVal &Val,
236 const MemRegion *Reg, CheckerContext &C,
237 ExplodedNode *ErrNode) const;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000238 void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
239 CheckerContext &C, ExplodedNode *ErrNode) const;
Adam Balogh0f88cae2019-11-08 08:56:31 +0100240 template <typename Getter>
241 void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
242 Getter get) const;
243 void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
244 void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
245 template <typename Getter>
246 void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
247 Getter get, SVal Default) const;
248 void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
249 void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
250 void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
251 ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000252
Adam Balogh0f88cae2019-11-08 08:56:31 +0100253 typedef void (IteratorChecker::*FnCheck)(const CallExpr *,
254 CheckerContext &) const;
255
256 CallDescriptionMap<FnCheck> Callbacks = {
257 {{0, "clang_analyzer_container_begin", 1},
258 &IteratorChecker::analyzerContainerBegin},
259 {{0, "clang_analyzer_container_end", 1},
260 &IteratorChecker::analyzerContainerEnd},
261 {{0, "clang_analyzer_iterator_position", 1},
262 &IteratorChecker::analyzerIteratorPosition},
263 {{0, "clang_analyzer_iterator_container", 1},
264 &IteratorChecker::analyzerIteratorContainer},
265 {{0, "clang_analyzer_iterator_validity", 1},
266 &IteratorChecker::analyzerIteratorValidity},
267 };
268
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000269public:
270 IteratorChecker();
271
272 enum CheckKind {
273 CK_IteratorRangeChecker,
Adam Balogh21583b72018-09-10 09:03:22 +0000274 CK_MismatchedIteratorChecker,
Adam Balogh2cfbe932018-08-28 08:41:15 +0000275 CK_InvalidatedIteratorChecker,
Adam Balogh0f88cae2019-11-08 08:56:31 +0100276 CK_DebugIteratorModeling,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000277 CK_NumCheckKinds
278 };
279
280 DefaultBool ChecksEnabled[CK_NumCheckKinds];
Kristof Umann72649422019-09-12 19:09:24 +0000281 CheckerNameRef CheckNames[CK_NumCheckKinds];
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000282
283 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
284 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000285 void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
286 void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
287 void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000288 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
289 CheckerContext &C) const;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000290 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000291 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
Adam Balogh0f88cae2019-11-08 08:56:31 +0100292 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000293};
294} // namespace
295
296REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
297REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
298 IteratorPosition)
299
300REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
301
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000302namespace {
303
304bool isIteratorType(const QualType &Type);
305bool isIterator(const CXXRecordDecl *CRD);
Adam Balogh3659f7a2018-09-10 09:05:31 +0000306bool isComparisonOperator(OverloadedOperatorKind OK);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000307bool isBeginCall(const FunctionDecl *Func);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000308bool isEndCall(const FunctionDecl *Func);
Adam Balogh2e7cb342018-09-10 09:07:47 +0000309bool isAssignCall(const FunctionDecl *Func);
310bool isClearCall(const FunctionDecl *Func);
Adam Balogh9a48ba62018-09-10 09:06:31 +0000311bool isPushBackCall(const FunctionDecl *Func);
312bool isEmplaceBackCall(const FunctionDecl *Func);
313bool isPopBackCall(const FunctionDecl *Func);
314bool isPushFrontCall(const FunctionDecl *Func);
315bool isEmplaceFrontCall(const FunctionDecl *Func);
316bool isPopFrontCall(const FunctionDecl *Func);
Adam Balogh2e7cb342018-09-10 09:07:47 +0000317bool isInsertCall(const FunctionDecl *Func);
318bool isEraseCall(const FunctionDecl *Func);
319bool isEraseAfterCall(const FunctionDecl *Func);
320bool isEmplaceCall(const FunctionDecl *Func);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000321bool isAssignmentOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000322bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000323bool isAccessOperator(OverloadedOperatorKind OK);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000324bool isDereferenceOperator(OverloadedOperatorKind OK);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000325bool isIncrementOperator(OverloadedOperatorKind OK);
326bool isDecrementOperator(OverloadedOperatorKind OK);
327bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
Adam Balogh9a48ba62018-09-10 09:06:31 +0000328bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
329bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
330bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000331SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000332SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000333ProgramStateRef createContainerBegin(ProgramStateRef State,
Adam Balogh33160c42019-05-20 11:04:27 +0000334 const MemRegion *Cont, const Expr *E,
335 QualType T, const LocationContext *LCtx,
336 unsigned BlockCount);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000337ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
Adam Balogh33160c42019-05-20 11:04:27 +0000338 const Expr *E, QualType T,
339 const LocationContext *LCtx,
340 unsigned BlockCount);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000341const IteratorPosition *getIteratorPosition(ProgramStateRef State,
342 const SVal &Val);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000343ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
344 const IteratorPosition &Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000345ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
Adam Balogh54976e72019-04-23 07:15:55 +0000346ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
347 long Scale);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000348ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
349 const MemRegion *Cont);
Adam Balogh2e7cb342018-09-10 09:07:47 +0000350ProgramStateRef
351invalidateAllIteratorPositionsExcept(ProgramStateRef State,
352 const MemRegion *Cont, SymbolRef Offset,
353 BinaryOperator::Opcode Opc);
Adam Balogh9a48ba62018-09-10 09:06:31 +0000354ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
355 SymbolRef Offset,
356 BinaryOperator::Opcode Opc);
Adam Balogh2e7cb342018-09-10 09:07:47 +0000357ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
358 SymbolRef Offset1,
359 BinaryOperator::Opcode Opc1,
360 SymbolRef Offset2,
361 BinaryOperator::Opcode Opc2);
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000362ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
363 const MemRegion *Cont,
364 const MemRegion *NewCont);
365ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
366 const MemRegion *Cont,
367 const MemRegion *NewCont,
368 SymbolRef Offset,
369 BinaryOperator::Opcode Opc);
370ProgramStateRef rebaseSymbolInIteratorPositionsIf(
371 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
372 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
Adam Balogh54976e72019-04-23 07:15:55 +0000373ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
374 SymbolRef Sym2, bool Equal);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000375const ContainerData *getContainerData(ProgramStateRef State,
376 const MemRegion *Cont);
377ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
378 const ContainerData &CData);
Adam Balogha6921202018-07-30 08:52:21 +0000379bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
Adam Balogh21583b72018-09-10 09:03:22 +0000380bool isBoundThroughLazyCompoundVal(const Environment &Env,
381 const MemRegion *Reg);
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000382bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
383bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
384bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000385bool isZero(ProgramStateRef State, const NonLoc &Val);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000386} // namespace
387
388IteratorChecker::IteratorChecker() {
389 OutOfRangeBugType.reset(
Adam Balogh12f5c7f2019-08-29 09:35:47 +0000390 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
Adam Balogh21583b72018-09-10 09:03:22 +0000391 MismatchedBugType.reset(
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000392 new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
393 /*SuppressOnSink=*/true));
Adam Balogh2cfbe932018-08-28 08:41:15 +0000394 InvalidatedBugType.reset(
Adam Balogh12f5c7f2019-08-29 09:35:47 +0000395 new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
Adam Balogh0f88cae2019-11-08 08:56:31 +0100396 DebugMsgBugType.reset(
397 new BugType(this, "Checking analyzer assumptions", "debug",
398 /*SuppressOnSink=*/true));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000399}
400
401void IteratorChecker::checkPreCall(const CallEvent &Call,
402 CheckerContext &C) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000403 // Check for out of range access or access of invalidated position and
404 // iterator mismatches
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000405 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
406 if (!Func)
407 return;
408
409 if (Func->isOverloadedOperator()) {
Adam Balogh2cfbe932018-08-28 08:41:15 +0000410 if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
411 isAccessOperator(Func->getOverloadedOperator())) {
412 // Check for any kind of access of invalidated iterator positions
413 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
414 verifyAccess(C, InstCall->getCXXThisVal());
415 } else {
416 verifyAccess(C, Call.getArgSVal(0));
417 }
418 }
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000419 if (ChecksEnabled[CK_IteratorRangeChecker]) {
420 if (isIncrementOperator(Func->getOverloadedOperator())) {
421 // Check for out-of-range incrementions
422 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
423 verifyIncrement(C, InstCall->getCXXThisVal());
424 } else {
425 if (Call.getNumArgs() >= 1) {
426 verifyIncrement(C, Call.getArgSVal(0));
427 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000428 }
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000429 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
430 // Check for out-of-range decrementions
431 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
432 verifyDecrement(C, InstCall->getCXXThisVal());
433 } else {
434 if (Call.getNumArgs() >= 1) {
435 verifyDecrement(C, Call.getArgSVal(0));
436 }
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000437 }
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000438 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
439 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
440 // Check for out-of-range incrementions and decrementions
Adam Balogh8557f172019-08-05 06:45:41 +0000441 if (Call.getNumArgs() >= 1 &&
442 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000443 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
444 InstCall->getCXXThisVal(),
445 Call.getArgSVal(0));
446 }
447 } else {
Adam Balogh8557f172019-08-05 06:45:41 +0000448 if (Call.getNumArgs() >= 2 &&
449 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000450 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
451 Call.getArgSVal(0), Call.getArgSVal(1));
452 }
453 }
454 } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
455 // Check for dereference of out-of-range iterators
456 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
457 verifyDereference(C, InstCall->getCXXThisVal());
458 } else {
459 verifyDereference(C, Call.getArgSVal(0));
460 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000461 }
Adam Balogh3659f7a2018-09-10 09:05:31 +0000462 } else if (ChecksEnabled[CK_MismatchedIteratorChecker] &&
463 isComparisonOperator(Func->getOverloadedOperator())) {
464 // Check for comparisons of iterators of different containers
465 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
466 if (Call.getNumArgs() < 1)
467 return;
468
469 if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
470 !isIteratorType(Call.getArgExpr(0)->getType()))
471 return;
472
473 verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
474 } else {
475 if (Call.getNumArgs() < 2)
476 return;
477
478 if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
479 !isIteratorType(Call.getArgExpr(1)->getType()))
480 return;
481
482 verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
483 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000484 }
Adam Balogh2e7cb342018-09-10 09:07:47 +0000485 } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
486 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
487 return;
488
489 const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
490 if (!ContReg)
491 return;
492 // Check for erase, insert and emplace using iterator of another container
493 if (isEraseCall(Func) || isEraseAfterCall(Func)) {
494 verifyMatch(C, Call.getArgSVal(0),
495 InstCall->getCXXThisVal().getAsRegion());
496 if (Call.getNumArgs() == 2) {
497 verifyMatch(C, Call.getArgSVal(1),
498 InstCall->getCXXThisVal().getAsRegion());
499 }
500 } else if (isInsertCall(Func)) {
501 verifyMatch(C, Call.getArgSVal(0),
502 InstCall->getCXXThisVal().getAsRegion());
503 if (Call.getNumArgs() == 3 &&
504 isIteratorType(Call.getArgExpr(1)->getType()) &&
505 isIteratorType(Call.getArgExpr(2)->getType())) {
506 verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
507 }
508 } else if (isEmplaceCall(Func)) {
509 verifyMatch(C, Call.getArgSVal(0),
510 InstCall->getCXXThisVal().getAsRegion());
511 }
Adam Balogh3659f7a2018-09-10 09:05:31 +0000512 } else if (isa<CXXConstructorCall>(&Call)) {
513 // Check match of first-last iterator pair in a constructor of a container
514 if (Call.getNumArgs() < 2)
515 return;
516
517 const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
518 if (Ctr->getNumParams() < 2)
519 return;
520
521 if (Ctr->getParamDecl(0)->getName() != "first" ||
522 Ctr->getParamDecl(1)->getName() != "last")
523 return;
524
525 if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
526 !isIteratorType(Call.getArgExpr(1)->getType()))
527 return;
528
529 verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
530 } else {
Adam Balogh21583b72018-09-10 09:03:22 +0000531 // The main purpose of iterators is to abstract away from different
532 // containers and provide a (maybe limited) uniform access to them.
533 // This implies that any correctly written template function that
534 // works on multiple containers using iterators takes different
535 // template parameters for different containers. So we can safely
536 // assume that passing iterators of different containers as arguments
537 // whose type replaces the same template parameter is a bug.
538 //
539 // Example:
540 // template<typename I1, typename I2>
541 // void f(I1 first1, I1 last1, I2 first2, I2 last2);
542 //
543 // In this case the first two arguments to f() must be iterators must belong
544 // to the same container and the last to also to the same container but
Raphael Isemannb23ccec2018-12-10 12:37:46 +0000545 // not necessarily to the same as the first two.
Adam Balogh21583b72018-09-10 09:03:22 +0000546
547 if (!ChecksEnabled[CK_MismatchedIteratorChecker])
548 return;
549
550 const auto *Templ = Func->getPrimaryTemplate();
551 if (!Templ)
552 return;
553
554 const auto *TParams = Templ->getTemplateParameters();
555 const auto *TArgs = Func->getTemplateSpecializationArgs();
556
557 // Iterate over all the template parameters
558 for (size_t I = 0; I < TParams->size(); ++I) {
559 const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
560 if (!TPDecl)
561 continue;
562
563 if (TPDecl->isParameterPack())
564 continue;
565
566 const auto TAType = TArgs->get(I).getAsType();
567 if (!isIteratorType(TAType))
568 continue;
569
570 SVal LHS = UndefinedVal();
571
572 // For every template parameter which is an iterator type in the
573 // instantiation look for all functions' parameters' type by it and
574 // check whether they belong to the same container
575 for (auto J = 0U; J < Func->getNumParams(); ++J) {
576 const auto *Param = Func->getParamDecl(J);
577 const auto *ParamType =
578 Param->getType()->getAs<SubstTemplateTypeParmType>();
579 if (!ParamType ||
580 ParamType->getReplacedParameter()->getDecl() != TPDecl)
581 continue;
582 if (LHS.isUndef()) {
583 LHS = Call.getArgSVal(J);
584 } else {
585 verifyMatch(C, LHS, Call.getArgSVal(J));
586 }
587 }
588 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000589 }
590}
591
592void IteratorChecker::checkPostCall(const CallEvent &Call,
593 CheckerContext &C) const {
594 // Record new iterator positions and iterator position changes
595 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
596 if (!Func)
597 return;
598
599 if (Func->isOverloadedOperator()) {
600 const auto Op = Func->getOverloadedOperator();
Adam Balogh2cfbe932018-08-28 08:41:15 +0000601 if (isAssignmentOperator(Op)) {
Artem Dergachev630f7da2019-08-28 18:44:38 +0000602 // Overloaded 'operator=' must be a non-static member function.
603 const auto *InstCall = cast<CXXInstanceCall>(&Call);
Adam Baloghd538b702019-04-26 07:30:07 +0000604 if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000605 handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
606 Call.getArgSVal(0));
Adam Baloghd538b702019-04-26 07:30:07 +0000607 return;
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000608 }
Adam Baloghd538b702019-04-26 07:30:07 +0000609
610 handleAssign(C, InstCall->getCXXThisVal());
611 return;
Adam Balogh2cfbe932018-08-28 08:41:15 +0000612 } else if (isSimpleComparisonOperator(Op)) {
Adam Balogh54976e72019-04-23 07:15:55 +0000613 const auto *OrigExpr = Call.getOriginExpr();
614 if (!OrigExpr)
615 return;
616
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000617 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Balogh54976e72019-04-23 07:15:55 +0000618 handleComparison(C, OrigExpr, Call.getReturnValue(),
619 InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
Adam Baloghd538b702019-04-26 07:30:07 +0000620 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000621 }
Adam Baloghd538b702019-04-26 07:30:07 +0000622
623 handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
624 Call.getArgSVal(1), Op);
625 return;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000626 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
627 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Balogh8557f172019-08-05 06:45:41 +0000628 if (Call.getNumArgs() >= 1 &&
629 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000630 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
631 Call.getReturnValue(),
632 InstCall->getCXXThisVal(), Call.getArgSVal(0));
Adam Baloghd538b702019-04-26 07:30:07 +0000633 return;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000634 }
635 } else {
Adam Balogh8557f172019-08-05 06:45:41 +0000636 if (Call.getNumArgs() >= 2 &&
637 Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000638 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
639 Call.getReturnValue(), Call.getArgSVal(0),
640 Call.getArgSVal(1));
Adam Baloghd538b702019-04-26 07:30:07 +0000641 return;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000642 }
643 }
644 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
645 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
646 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
647 Call.getNumArgs());
Adam Baloghd538b702019-04-26 07:30:07 +0000648 return;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000649 }
Adam Baloghd538b702019-04-26 07:30:07 +0000650
651 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
652 Call.getNumArgs());
653 return;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000654 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
655 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
656 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
657 Call.getNumArgs());
Adam Baloghd538b702019-04-26 07:30:07 +0000658 return;
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000659 }
Adam Baloghd538b702019-04-26 07:30:07 +0000660
661 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
662 Call.getNumArgs());
663 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000664 }
665 } else {
Adam Balogh9a48ba62018-09-10 09:06:31 +0000666 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Balogh2e7cb342018-09-10 09:07:47 +0000667 if (isAssignCall(Func)) {
668 handleAssign(C, InstCall->getCXXThisVal());
Adam Baloghd538b702019-04-26 07:30:07 +0000669 return;
670 }
671
672 if (isClearCall(Func)) {
Adam Balogh2e7cb342018-09-10 09:07:47 +0000673 handleClear(C, InstCall->getCXXThisVal());
Adam Baloghd538b702019-04-26 07:30:07 +0000674 return;
675 }
676
677 if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
Adam Balogh9a48ba62018-09-10 09:06:31 +0000678 handlePushBack(C, InstCall->getCXXThisVal());
Adam Baloghd538b702019-04-26 07:30:07 +0000679 return;
680 }
681
682 if (isPopBackCall(Func)) {
Adam Balogh9a48ba62018-09-10 09:06:31 +0000683 handlePopBack(C, InstCall->getCXXThisVal());
Adam Baloghd538b702019-04-26 07:30:07 +0000684 return;
685 }
686
687 if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
Adam Balogh9a48ba62018-09-10 09:06:31 +0000688 handlePushFront(C, InstCall->getCXXThisVal());
Adam Baloghd538b702019-04-26 07:30:07 +0000689 return;
690 }
691
692 if (isPopFrontCall(Func)) {
Adam Balogh9a48ba62018-09-10 09:06:31 +0000693 handlePopFront(C, InstCall->getCXXThisVal());
Adam Baloghd538b702019-04-26 07:30:07 +0000694 return;
695 }
696
697 if (isInsertCall(Func) || isEmplaceCall(Func)) {
Adam Balogh2e7cb342018-09-10 09:07:47 +0000698 handleInsert(C, Call.getArgSVal(0));
Adam Baloghd538b702019-04-26 07:30:07 +0000699 return;
700 }
701
702 if (isEraseCall(Func)) {
Adam Balogh2e7cb342018-09-10 09:07:47 +0000703 if (Call.getNumArgs() == 1) {
704 handleErase(C, Call.getArgSVal(0));
Adam Baloghd538b702019-04-26 07:30:07 +0000705 return;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000706 }
Adam Baloghd538b702019-04-26 07:30:07 +0000707
708 if (Call.getNumArgs() == 2) {
709 handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
710 return;
711 }
712 }
713
714 if (isEraseAfterCall(Func)) {
Adam Balogh2e7cb342018-09-10 09:07:47 +0000715 if (Call.getNumArgs() == 1) {
716 handleEraseAfter(C, Call.getArgSVal(0));
Adam Baloghd538b702019-04-26 07:30:07 +0000717 return;
718 }
719
720 if (Call.getNumArgs() == 2) {
Adam Balogh2e7cb342018-09-10 09:07:47 +0000721 handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
Adam Baloghd538b702019-04-26 07:30:07 +0000722 return;
Adam Balogh2e7cb342018-09-10 09:07:47 +0000723 }
Adam Balogh9a48ba62018-09-10 09:06:31 +0000724 }
725 }
726
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000727 const auto *OrigExpr = Call.getOriginExpr();
728 if (!OrigExpr)
729 return;
730
731 if (!isIteratorType(Call.getResultType()))
732 return;
733
734 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000735
736 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000737 if (isBeginCall(Func)) {
738 handleBegin(C, OrigExpr, Call.getReturnValue(),
739 InstCall->getCXXThisVal());
740 return;
741 }
Adam Baloghd538b702019-04-26 07:30:07 +0000742
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000743 if (isEndCall(Func)) {
744 handleEnd(C, OrigExpr, Call.getReturnValue(),
745 InstCall->getCXXThisVal());
746 return;
747 }
748 }
749
Adam Balogh6b23b1a2018-09-10 09:04:27 +0000750 // Already bound to container?
751 if (getIteratorPosition(State, Call.getReturnValue()))
752 return;
753
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000754 // Copy-like and move constructors
755 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
756 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
757 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
758 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
759 State = removeIteratorPosition(State, Call.getArgSVal(0));
760 }
761 C.addTransition(State);
762 return;
763 }
764 }
765
766 // Assumption: if return value is an iterator which is not yet bound to a
767 // container, then look for the first iterator argument, and
768 // bind the return value to the same container. This approach
769 // works for STL algorithms.
770 // FIXME: Add a more conservative mode
771 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
772 if (isIteratorType(Call.getArgExpr(i)->getType())) {
773 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
774 assignToContainer(C, OrigExpr, Call.getReturnValue(),
775 Pos->getContainer());
776 return;
777 }
778 }
779 }
780 }
781}
782
Adam Balogh2e7cb342018-09-10 09:07:47 +0000783void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
784 CheckerContext &C) const {
785 auto State = C.getState();
786 const auto *Pos = getIteratorPosition(State, Val);
787 if (Pos) {
788 State = setIteratorPosition(State, Loc, *Pos);
789 C.addTransition(State);
790 } else {
791 const auto *OldPos = getIteratorPosition(State, Loc);
792 if (OldPos) {
793 State = removeIteratorPosition(State, Loc);
794 C.addTransition(State);
795 }
796 }
797}
798
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000799void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
800 CheckerContext &C) const {
801 /* Transfer iterator state to temporary objects */
802 auto State = C.getState();
Tyker08ea1ee2019-11-16 17:04:34 +0100803 const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000804 if (!Pos)
805 return;
George Karpenkovd703ec92018-01-17 20:27:29 +0000806 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000807 C.addTransition(State);
808}
809
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000810void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
811 SymbolReaper &SR) const {
812 // Keep symbolic expressions of iterator positions, container begins and ends
813 // alive
814 auto RegionMap = State->get<IteratorRegionMap>();
815 for (const auto Reg : RegionMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000816 const auto Offset = Reg.second.getOffset();
817 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
818 if (isa<SymbolData>(*i))
819 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000820 }
821
822 auto SymbolMap = State->get<IteratorSymbolMap>();
823 for (const auto Sym : SymbolMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000824 const auto Offset = Sym.second.getOffset();
825 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
826 if (isa<SymbolData>(*i))
827 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000828 }
829
830 auto ContMap = State->get<ContainerMap>();
831 for (const auto Cont : ContMap) {
832 const auto CData = Cont.second;
833 if (CData.getBegin()) {
834 SR.markLive(CData.getBegin());
Adam Balogh9a48ba62018-09-10 09:06:31 +0000835 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
836 SR.markLive(SIE->getLHS());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000837 }
838 if (CData.getEnd()) {
839 SR.markLive(CData.getEnd());
Adam Balogh9a48ba62018-09-10 09:06:31 +0000840 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
841 SR.markLive(SIE->getLHS());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000842 }
843 }
844}
845
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000846void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
847 CheckerContext &C) const {
848 // Cleanup
849 auto State = C.getState();
850
851 auto RegionMap = State->get<IteratorRegionMap>();
852 for (const auto Reg : RegionMap) {
853 if (!SR.isLiveRegion(Reg.first)) {
Adam Balogh21583b72018-09-10 09:03:22 +0000854 // The region behind the `LazyCompoundVal` is often cleaned up before
855 // the `LazyCompoundVal` itself. If there are iterator positions keyed
856 // by these regions their cleanup must be deferred.
857 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
858 State = State->remove<IteratorRegionMap>(Reg.first);
859 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000860 }
861 }
862
863 auto SymbolMap = State->get<IteratorSymbolMap>();
864 for (const auto Sym : SymbolMap) {
865 if (!SR.isLive(Sym.first)) {
866 State = State->remove<IteratorSymbolMap>(Sym.first);
867 }
868 }
869
870 auto ContMap = State->get<ContainerMap>();
871 for (const auto Cont : ContMap) {
872 if (!SR.isLiveRegion(Cont.first)) {
Adam Balogha6921202018-07-30 08:52:21 +0000873 // We must keep the container data while it has live iterators to be able
874 // to compare them to the begin and the end of the container.
875 if (!hasLiveIterators(State, Cont.first)) {
876 State = State->remove<ContainerMap>(Cont.first);
877 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000878 }
879 }
880
Reka Kovacse48ea892018-07-30 16:14:59 +0000881 C.addTransition(State);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000882}
883
Adam Balogh54976e72019-04-23 07:15:55 +0000884void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE,
885 const SVal &RetVal, const SVal &LVal,
886 const SVal &RVal,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000887 OverloadedOperatorKind Op) const {
888 // Record the operands and the operator of the comparison for the next
889 // evalAssume, if the result is a symbolic expression. If it is a concrete
890 // value (only one branch is possible), then transfer the state between
891 // the operands according to the operator and the result
Adam Balogh54976e72019-04-23 07:15:55 +0000892 auto State = C.getState();
893 const auto *LPos = getIteratorPosition(State, LVal);
894 const auto *RPos = getIteratorPosition(State, RVal);
895 const MemRegion *Cont = nullptr;
896 if (LPos) {
897 Cont = LPos->getContainer();
898 } else if (RPos) {
899 Cont = RPos->getContainer();
900 }
901 if (!Cont)
902 return;
903
904 // At least one of the iterators have recorded positions. If one of them has
905 // not then create a new symbol for the offset.
906 SymbolRef Sym;
907 if (!LPos || !RPos) {
908 auto &SymMgr = C.getSymbolManager();
Adam Baloghd2e2e202019-04-23 11:18:50 +0000909 Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
Adam Balogh54976e72019-04-23 07:15:55 +0000910 C.getASTContext().LongTy, C.blockCount());
911 State = assumeNoOverflow(State, Sym, 4);
912 }
913
914 if (!LPos) {
915 State = setIteratorPosition(State, LVal,
916 IteratorPosition::getPosition(Cont, Sym));
917 LPos = getIteratorPosition(State, LVal);
918 } else if (!RPos) {
919 State = setIteratorPosition(State, RVal,
920 IteratorPosition::getPosition(Cont, Sym));
921 RPos = getIteratorPosition(State, RVal);
922 }
923
924 processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
925}
926
927void IteratorChecker::processComparison(CheckerContext &C,
928 ProgramStateRef State, SymbolRef Sym1,
929 SymbolRef Sym2, const SVal &RetVal,
930 OverloadedOperatorKind Op) const {
931 if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
Adam Balogh8f882702019-04-23 07:45:10 +0000932 if ((State = relateSymbols(State, Sym1, Sym2,
Adam Balogh54976e72019-04-23 07:15:55 +0000933 (Op == OO_EqualEqual) ==
Adam Balogh8f882702019-04-23 07:45:10 +0000934 (TruthVal->getValue() != 0)))) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000935 C.addTransition(State);
936 } else {
937 C.generateSink(State, C.getPredecessor());
938 }
Adam Balogh54976e72019-04-23 07:15:55 +0000939 return;
940 }
941
942 const auto ConditionVal = RetVal.getAs<DefinedSVal>();
943 if (!ConditionVal)
944 return;
945
946 if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
947 StateTrue = StateTrue->assume(*ConditionVal, true);
948 C.addTransition(StateTrue);
949 }
950
951 if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
952 StateFalse = StateFalse->assume(*ConditionVal, false);
953 C.addTransition(StateFalse);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000954 }
955}
956
957void IteratorChecker::verifyDereference(CheckerContext &C,
958 const SVal &Val) const {
959 auto State = C.getState();
960 const auto *Pos = getIteratorPosition(State, Val);
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000961 if (Pos && isPastTheEnd(State, *Pos)) {
Adam Balogh12f5c7f2019-08-29 09:35:47 +0000962 auto *N = C.generateErrorNode(State);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000963 if (!N)
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000964 return;
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000965 reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
966 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000967 }
968}
969
Adam Balogh2cfbe932018-08-28 08:41:15 +0000970void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
971 auto State = C.getState();
972 const auto *Pos = getIteratorPosition(State, Val);
973 if (Pos && !Pos->isValid()) {
Adam Balogh12f5c7f2019-08-29 09:35:47 +0000974 auto *N = C.generateErrorNode(State);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000975 if (!N) {
976 return;
977 }
978 reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
979 }
980}
981
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000982void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
983 const SVal &Iter, bool Postfix) const {
984 // Increment the symbolic expressions which represents the position of the
985 // iterator
986 auto State = C.getState();
987 const auto *Pos = getIteratorPosition(State, Iter);
988 if (Pos) {
989 auto &SymMgr = C.getSymbolManager();
990 auto &BVF = SymMgr.getBasicVals();
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000991 const auto NewPos =
992 advancePosition(C, OO_Plus, *Pos,
993 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000994 State = setIteratorPosition(State, Iter, NewPos);
995 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
996 C.addTransition(State);
997 }
998}
999
1000void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
1001 const SVal &Iter, bool Postfix) const {
1002 // Decrement the symbolic expressions which represents the position of the
1003 // iterator
1004 auto State = C.getState();
1005 const auto *Pos = getIteratorPosition(State, Iter);
1006 if (Pos) {
1007 auto &SymMgr = C.getSymbolManager();
1008 auto &BVF = SymMgr.getBasicVals();
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001009 const auto NewPos =
1010 advancePosition(C, OO_Minus, *Pos,
1011 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001012 State = setIteratorPosition(State, Iter, NewPos);
1013 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
1014 C.addTransition(State);
1015 }
1016}
1017
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001018void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
1019 OverloadedOperatorKind Op,
1020 const SVal &RetVal,
1021 const SVal &LHS,
1022 const SVal &RHS) const {
1023 // Increment or decrement the symbolic expressions which represents the
1024 // position of the iterator
1025 auto State = C.getState();
1026 const auto *Pos = getIteratorPosition(State, LHS);
1027 if (!Pos)
1028 return;
1029
1030 const auto *value = &RHS;
1031 if (auto loc = RHS.getAs<Loc>()) {
1032 const auto val = State->getRawSVal(*loc);
1033 value = &val;
1034 }
1035
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001036 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001037 State =
1038 setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value));
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001039 C.addTransition(State);
1040}
1041
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001042void IteratorChecker::verifyIncrement(CheckerContext &C,
1043 const SVal &Iter) const {
1044 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1045 verifyRandomIncrOrDecr(C, OO_Plus, Iter,
1046 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
1047}
1048
1049void IteratorChecker::verifyDecrement(CheckerContext &C,
1050 const SVal &Iter) const {
1051 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1052 verifyRandomIncrOrDecr(C, OO_Minus, Iter,
1053 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
1054}
1055
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001056void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
1057 OverloadedOperatorKind Op,
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001058 const SVal &LHS,
1059 const SVal &RHS) const {
1060 auto State = C.getState();
1061
1062 // If the iterator is initially inside its range, then the operation is valid
1063 const auto *Pos = getIteratorPosition(State, LHS);
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001064 if (!Pos)
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001065 return;
1066
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001067 auto Value = RHS;
1068 if (auto ValAsLoc = RHS.getAs<Loc>()) {
1069 Value = State->getRawSVal(*ValAsLoc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001070 }
1071
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001072 if (Value.isUnknown())
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001073 return;
1074
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001075 // Incremention or decremention by 0 is never a bug.
1076 if (isZero(State, Value.castAs<NonLoc>()))
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001077 return;
1078
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001079 // The result may be the past-end iterator of the container, but any other
1080 // out of range position is undefined behaviour
1081 if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) {
Adam Balogh12f5c7f2019-08-29 09:35:47 +00001082 auto *N = C.generateErrorNode(State);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001083 if (!N)
1084 return;
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001085 reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
1086 C, N);
1087 }
1088 if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
Adam Balogh12f5c7f2019-08-29 09:35:47 +00001089 auto *N = C.generateErrorNode(State);
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001090 if (!N)
1091 return;
1092 reportOutOfRangeBug("Iterator incremented behind the past-the-end "
1093 "iterator.", LHS, C, N);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001094 }
1095}
1096
Adam Balogh2e7cb342018-09-10 09:07:47 +00001097void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
1098 const MemRegion *Cont) const {
1099 // Verify match between a container and the container of an iterator
Adam Balogh42d241f2018-12-04 10:22:28 +00001100 Cont = Cont->getMostDerivedObjectRegion();
Adam Balogh2e7cb342018-09-10 09:07:47 +00001101
Adam Baloghd7033052019-03-13 13:55:11 +00001102 if (const auto *ContSym = Cont->getSymbolicBase()) {
1103 if (isa<SymbolConjured>(ContSym->getSymbol()))
1104 return;
1105 }
1106
Adam Balogh2e7cb342018-09-10 09:07:47 +00001107 auto State = C.getState();
1108 const auto *Pos = getIteratorPosition(State, Iter);
Adam Baloghd7033052019-03-13 13:55:11 +00001109 if (!Pos)
1110 return;
1111
1112 const auto *IterCont = Pos->getContainer();
1113
1114 // Skip symbolic regions based on conjured symbols. Two conjured symbols
1115 // may or may not be the same. For example, the same function can return
1116 // the same or a different container but we get different conjured symbols
1117 // for each call. This may cause false positives so omit them from the check.
1118 if (const auto *ContSym = IterCont->getSymbolicBase()) {
1119 if (isa<SymbolConjured>(ContSym->getSymbol()))
1120 return;
1121 }
1122
1123 if (IterCont != Cont) {
Adam Balogh2e7cb342018-09-10 09:07:47 +00001124 auto *N = C.generateNonFatalErrorNode(State);
1125 if (!N) {
1126 return;
1127 }
Adam Baloghd7033052019-03-13 13:55:11 +00001128 reportMismatchedBug("Container accessed using foreign iterator argument.",
1129 Iter, Cont, C, N);
Adam Balogh2e7cb342018-09-10 09:07:47 +00001130 }
1131}
1132
Adam Balogh21583b72018-09-10 09:03:22 +00001133void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
1134 const SVal &Iter2) const {
1135 // Verify match between the containers of two iterators
1136 auto State = C.getState();
1137 const auto *Pos1 = getIteratorPosition(State, Iter1);
Adam Baloghd7033052019-03-13 13:55:11 +00001138 if (!Pos1)
1139 return;
1140
1141 const auto *IterCont1 = Pos1->getContainer();
1142
1143 // Skip symbolic regions based on conjured symbols. Two conjured symbols
1144 // may or may not be the same. For example, the same function can return
1145 // the same or a different container but we get different conjured symbols
1146 // for each call. This may cause false positives so omit them from the check.
1147 if (const auto *ContSym = IterCont1->getSymbolicBase()) {
1148 if (isa<SymbolConjured>(ContSym->getSymbol()))
1149 return;
1150 }
1151
Adam Balogh21583b72018-09-10 09:03:22 +00001152 const auto *Pos2 = getIteratorPosition(State, Iter2);
Adam Baloghd7033052019-03-13 13:55:11 +00001153 if (!Pos2)
1154 return;
1155
1156 const auto *IterCont2 = Pos2->getContainer();
1157 if (const auto *ContSym = IterCont2->getSymbolicBase()) {
1158 if (isa<SymbolConjured>(ContSym->getSymbol()))
1159 return;
1160 }
1161
1162 if (IterCont1 != IterCont2) {
Adam Balogh21583b72018-09-10 09:03:22 +00001163 auto *N = C.generateNonFatalErrorNode(State);
1164 if (!N)
1165 return;
1166 reportMismatchedBug("Iterators of different containers used where the "
1167 "same container is expected.", Iter1, Iter2, C, N);
1168 }
1169}
1170
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001171void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
1172 const SVal &RetVal, const SVal &Cont) const {
1173 const auto *ContReg = Cont.getAsRegion();
1174 if (!ContReg)
1175 return;
1176
Adam Balogh42d241f2018-12-04 10:22:28 +00001177 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001178
1179 // If the container already has a begin symbol then use it. Otherwise first
1180 // create a new one.
1181 auto State = C.getState();
1182 auto BeginSym = getContainerBegin(State, ContReg);
1183 if (!BeginSym) {
Adam Balogh33160c42019-05-20 11:04:27 +00001184 State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
1185 C.getLocationContext(), C.blockCount());
1186 BeginSym = getContainerBegin(State, ContReg);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001187 }
1188 State = setIteratorPosition(State, RetVal,
1189 IteratorPosition::getPosition(ContReg, BeginSym));
1190 C.addTransition(State);
1191}
1192
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001193void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
1194 const SVal &RetVal, const SVal &Cont) const {
1195 const auto *ContReg = Cont.getAsRegion();
1196 if (!ContReg)
1197 return;
1198
Adam Balogh42d241f2018-12-04 10:22:28 +00001199 ContReg = ContReg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001200
1201 // If the container already has an end symbol then use it. Otherwise first
1202 // create a new one.
1203 auto State = C.getState();
1204 auto EndSym = getContainerEnd(State, ContReg);
1205 if (!EndSym) {
Adam Balogh33160c42019-05-20 11:04:27 +00001206 State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
1207 C.getLocationContext(), C.blockCount());
1208 EndSym = getContainerEnd(State, ContReg);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001209 }
1210 State = setIteratorPosition(State, RetVal,
1211 IteratorPosition::getPosition(ContReg, EndSym));
1212 C.addTransition(State);
1213}
1214
1215void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
1216 const SVal &RetVal,
1217 const MemRegion *Cont) const {
Adam Balogh42d241f2018-12-04 10:22:28 +00001218 Cont = Cont->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001219
1220 auto State = C.getState();
1221 auto &SymMgr = C.getSymbolManager();
1222 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
1223 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001224 State = assumeNoOverflow(State, Sym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001225 State = setIteratorPosition(State, RetVal,
1226 IteratorPosition::getPosition(Cont, Sym));
1227 C.addTransition(State);
1228}
1229
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001230void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
1231 const Expr *CE, const SVal &OldCont) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +00001232 const auto *ContReg = Cont.getAsRegion();
1233 if (!ContReg)
1234 return;
1235
Adam Balogh42d241f2018-12-04 10:22:28 +00001236 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh2cfbe932018-08-28 08:41:15 +00001237
1238 // Assignment of a new value to a container always invalidates all its
1239 // iterators
1240 auto State = C.getState();
1241 const auto CData = getContainerData(State, ContReg);
1242 if (CData) {
1243 State = invalidateAllIteratorPositions(State, ContReg);
1244 }
1245
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001246 // In case of move, iterators of the old container (except the past-end
1247 // iterators) remain valid but refer to the new container
1248 if (!OldCont.isUndef()) {
1249 const auto *OldContReg = OldCont.getAsRegion();
1250 if (OldContReg) {
Adam Balogh42d241f2018-12-04 10:22:28 +00001251 OldContReg = OldContReg->getMostDerivedObjectRegion();
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001252 const auto OldCData = getContainerData(State, OldContReg);
1253 if (OldCData) {
1254 if (const auto OldEndSym = OldCData->getEnd()) {
Raphael Isemannb23ccec2018-12-10 12:37:46 +00001255 // If we already assigned an "end" symbol to the old container, then
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001256 // first reassign all iterator positions to the new container which
1257 // are not past the container (thus not greater or equal to the
1258 // current "end" symbol).
1259 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
1260 OldEndSym, BO_GE);
1261 auto &SymMgr = C.getSymbolManager();
1262 auto &SVB = C.getSValBuilder();
1263 // Then generate and assign a new "end" symbol for the new container.
1264 auto NewEndSym =
1265 SymMgr.conjureSymbol(CE, C.getLocationContext(),
1266 C.getASTContext().LongTy, C.blockCount());
1267 State = assumeNoOverflow(State, NewEndSym, 4);
1268 if (CData) {
1269 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
1270 } else {
1271 State = setContainerData(State, ContReg,
1272 ContainerData::fromEnd(NewEndSym));
1273 }
1274 // Finally, replace the old "end" symbol in the already reassigned
1275 // iterator positions with the new "end" symbol.
1276 State = rebaseSymbolInIteratorPositionsIf(
1277 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
1278 } else {
1279 // There was no "end" symbol assigned yet to the old container,
1280 // so reassign all iterator positions to the new container.
1281 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1282 }
1283 if (const auto OldBeginSym = OldCData->getBegin()) {
1284 // If we already assigned a "begin" symbol to the old container, then
1285 // assign it to the new container and remove it from the old one.
1286 if (CData) {
1287 State =
1288 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
1289 } else {
1290 State = setContainerData(State, ContReg,
1291 ContainerData::fromBegin(OldBeginSym));
1292 }
1293 State =
1294 setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
1295 }
1296 } else {
1297 // There was neither "begin" nor "end" symbol assigned yet to the old
1298 // container, so reassign all iterator positions to the new container.
1299 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1300 }
1301 }
1302 }
Adam Balogh2cfbe932018-08-28 08:41:15 +00001303 C.addTransition(State);
1304}
1305
Adam Balogh2e7cb342018-09-10 09:07:47 +00001306void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
1307 const auto *ContReg = Cont.getAsRegion();
1308 if (!ContReg)
1309 return;
1310
Adam Balogh42d241f2018-12-04 10:22:28 +00001311 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh2e7cb342018-09-10 09:07:47 +00001312
1313 // The clear() operation invalidates all the iterators, except the past-end
1314 // iterators of list-like containers
1315 auto State = C.getState();
1316 if (!hasSubscriptOperator(State, ContReg) ||
1317 !backModifiable(State, ContReg)) {
1318 const auto CData = getContainerData(State, ContReg);
1319 if (CData) {
1320 if (const auto EndSym = CData->getEnd()) {
1321 State =
1322 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
1323 C.addTransition(State);
1324 return;
1325 }
1326 }
1327 }
1328 State = invalidateAllIteratorPositions(State, ContReg);
1329 C.addTransition(State);
1330}
1331
Adam Balogh9a48ba62018-09-10 09:06:31 +00001332void IteratorChecker::handlePushBack(CheckerContext &C,
1333 const SVal &Cont) const {
1334 const auto *ContReg = Cont.getAsRegion();
1335 if (!ContReg)
1336 return;
1337
Adam Balogh42d241f2018-12-04 10:22:28 +00001338 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001339
1340 // For deque-like containers invalidate all iterator positions
1341 auto State = C.getState();
1342 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
1343 State = invalidateAllIteratorPositions(State, ContReg);
1344 C.addTransition(State);
1345 return;
1346 }
1347
1348 const auto CData = getContainerData(State, ContReg);
1349 if (!CData)
1350 return;
1351
1352 // For vector-like containers invalidate the past-end iterator positions
1353 if (const auto EndSym = CData->getEnd()) {
1354 if (hasSubscriptOperator(State, ContReg)) {
1355 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1356 }
1357 auto &SymMgr = C.getSymbolManager();
1358 auto &BVF = SymMgr.getBasicVals();
1359 auto &SVB = C.getSValBuilder();
1360 const auto newEndSym =
1361 SVB.evalBinOp(State, BO_Add,
1362 nonloc::SymbolVal(EndSym),
1363 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1364 SymMgr.getType(EndSym)).getAsSymbol();
1365 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1366 }
1367 C.addTransition(State);
1368}
1369
1370void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
1371 const auto *ContReg = Cont.getAsRegion();
1372 if (!ContReg)
1373 return;
1374
Adam Balogh42d241f2018-12-04 10:22:28 +00001375 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001376
1377 auto State = C.getState();
1378 const auto CData = getContainerData(State, ContReg);
1379 if (!CData)
1380 return;
1381
1382 if (const auto EndSym = CData->getEnd()) {
1383 auto &SymMgr = C.getSymbolManager();
1384 auto &BVF = SymMgr.getBasicVals();
1385 auto &SVB = C.getSValBuilder();
1386 const auto BackSym =
1387 SVB.evalBinOp(State, BO_Sub,
1388 nonloc::SymbolVal(EndSym),
1389 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1390 SymMgr.getType(EndSym)).getAsSymbol();
1391 // For vector-like and deque-like containers invalidate the last and the
1392 // past-end iterator positions. For list-like containers only invalidate
1393 // the last position
1394 if (hasSubscriptOperator(State, ContReg) &&
1395 backModifiable(State, ContReg)) {
1396 State = invalidateIteratorPositions(State, BackSym, BO_GE);
1397 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
1398 } else {
1399 State = invalidateIteratorPositions(State, BackSym, BO_EQ);
1400 }
1401 auto newEndSym = BackSym;
1402 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1403 C.addTransition(State);
1404 }
1405}
1406
1407void IteratorChecker::handlePushFront(CheckerContext &C,
1408 const SVal &Cont) const {
1409 const auto *ContReg = Cont.getAsRegion();
1410 if (!ContReg)
1411 return;
1412
Adam Balogh42d241f2018-12-04 10:22:28 +00001413 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001414
1415 // For deque-like containers invalidate all iterator positions
1416 auto State = C.getState();
1417 if (hasSubscriptOperator(State, ContReg)) {
1418 State = invalidateAllIteratorPositions(State, ContReg);
1419 C.addTransition(State);
1420 } else {
1421 const auto CData = getContainerData(State, ContReg);
1422 if (!CData)
1423 return;
1424
1425 if (const auto BeginSym = CData->getBegin()) {
1426 auto &SymMgr = C.getSymbolManager();
1427 auto &BVF = SymMgr.getBasicVals();
1428 auto &SVB = C.getSValBuilder();
1429 const auto newBeginSym =
1430 SVB.evalBinOp(State, BO_Sub,
1431 nonloc::SymbolVal(BeginSym),
1432 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1433 SymMgr.getType(BeginSym)).getAsSymbol();
1434 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1435 C.addTransition(State);
1436 }
1437 }
1438}
1439
1440void IteratorChecker::handlePopFront(CheckerContext &C,
1441 const SVal &Cont) const {
1442 const auto *ContReg = Cont.getAsRegion();
1443 if (!ContReg)
1444 return;
1445
Adam Balogh42d241f2018-12-04 10:22:28 +00001446 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001447
1448 auto State = C.getState();
1449 const auto CData = getContainerData(State, ContReg);
1450 if (!CData)
1451 return;
1452
1453 // For deque-like containers invalidate all iterator positions. For list-like
1454 // iterators only invalidate the first position
1455 if (const auto BeginSym = CData->getBegin()) {
1456 if (hasSubscriptOperator(State, ContReg)) {
1457 State = invalidateIteratorPositions(State, BeginSym, BO_LE);
1458 } else {
1459 State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
1460 }
1461 auto &SymMgr = C.getSymbolManager();
1462 auto &BVF = SymMgr.getBasicVals();
1463 auto &SVB = C.getSValBuilder();
1464 const auto newBeginSym =
1465 SVB.evalBinOp(State, BO_Add,
1466 nonloc::SymbolVal(BeginSym),
1467 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1468 SymMgr.getType(BeginSym)).getAsSymbol();
1469 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1470 C.addTransition(State);
1471 }
1472}
1473
Adam Balogh2e7cb342018-09-10 09:07:47 +00001474void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
1475 auto State = C.getState();
1476 const auto *Pos = getIteratorPosition(State, Iter);
1477 if (!Pos)
1478 return;
1479
1480 // For deque-like containers invalidate all iterator positions. For
1481 // vector-like containers invalidate iterator positions after the insertion.
1482 const auto *Cont = Pos->getContainer();
1483 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1484 if (frontModifiable(State, Cont)) {
1485 State = invalidateAllIteratorPositions(State, Cont);
1486 } else {
1487 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1488 }
1489 if (const auto *CData = getContainerData(State, Cont)) {
1490 if (const auto EndSym = CData->getEnd()) {
1491 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1492 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1493 }
1494 }
1495 C.addTransition(State);
1496 }
1497}
1498
1499void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
1500 auto State = C.getState();
1501 const auto *Pos = getIteratorPosition(State, Iter);
1502 if (!Pos)
1503 return;
1504
1505 // For deque-like containers invalidate all iterator positions. For
1506 // vector-like containers invalidate iterator positions at and after the
1507 // deletion. For list-like containers only invalidate the deleted position.
1508 const auto *Cont = Pos->getContainer();
1509 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1510 if (frontModifiable(State, Cont)) {
1511 State = invalidateAllIteratorPositions(State, Cont);
1512 } else {
1513 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1514 }
1515 if (const auto *CData = getContainerData(State, Cont)) {
1516 if (const auto EndSym = CData->getEnd()) {
1517 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1518 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1519 }
1520 }
1521 } else {
1522 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
1523 }
1524 C.addTransition(State);
1525}
1526
1527void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
1528 const SVal &Iter2) const {
1529 auto State = C.getState();
1530 const auto *Pos1 = getIteratorPosition(State, Iter1);
1531 const auto *Pos2 = getIteratorPosition(State, Iter2);
1532 if (!Pos1 || !Pos2)
1533 return;
1534
1535 // For deque-like containers invalidate all iterator positions. For
1536 // vector-like containers invalidate iterator positions at and after the
1537 // deletion range. For list-like containers only invalidate the deleted
1538 // position range [first..last].
1539 const auto *Cont = Pos1->getContainer();
1540 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1541 if (frontModifiable(State, Cont)) {
1542 State = invalidateAllIteratorPositions(State, Cont);
1543 } else {
1544 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
1545 }
1546 if (const auto *CData = getContainerData(State, Cont)) {
1547 if (const auto EndSym = CData->getEnd()) {
1548 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1549 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1550 }
1551 }
1552 } else {
1553 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
1554 Pos2->getOffset(), BO_LT);
1555 }
1556 C.addTransition(State);
1557}
1558
1559void IteratorChecker::handleEraseAfter(CheckerContext &C,
1560 const SVal &Iter) const {
1561 auto State = C.getState();
1562 const auto *Pos = getIteratorPosition(State, Iter);
1563 if (!Pos)
1564 return;
1565
1566 // Invalidate the deleted iterator position, which is the position of the
1567 // parameter plus one.
1568 auto &SymMgr = C.getSymbolManager();
1569 auto &BVF = SymMgr.getBasicVals();
1570 auto &SVB = C.getSValBuilder();
1571 const auto NextSym =
1572 SVB.evalBinOp(State, BO_Add,
1573 nonloc::SymbolVal(Pos->getOffset()),
1574 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1575 SymMgr.getType(Pos->getOffset())).getAsSymbol();
1576 State = invalidateIteratorPositions(State, NextSym, BO_EQ);
1577 C.addTransition(State);
1578}
1579
1580void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
1581 const SVal &Iter2) const {
1582 auto State = C.getState();
1583 const auto *Pos1 = getIteratorPosition(State, Iter1);
1584 const auto *Pos2 = getIteratorPosition(State, Iter2);
1585 if (!Pos1 || !Pos2)
1586 return;
1587
1588 // Invalidate the deleted iterator position range (first..last)
1589 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
1590 Pos2->getOffset(), BO_LT);
1591 C.addTransition(State);
1592}
1593
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001594IteratorPosition IteratorChecker::advancePosition(CheckerContext &C,
1595 OverloadedOperatorKind Op,
1596 const IteratorPosition &Pos,
1597 const SVal &Distance) const {
1598 auto State = C.getState();
1599 auto &SymMgr = C.getSymbolManager();
1600 auto &SVB = C.getSValBuilder();
1601
1602 assert ((Op == OO_Plus || Op == OO_PlusEqual ||
1603 Op == OO_Minus || Op == OO_MinusEqual) &&
1604 "Advance operator must be one of +, -, += and -=.");
1605 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
1606 if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
1607 // For concrete integers we can calculate the new position
1608 return Pos.setTo(SVB.evalBinOp(State, BinOp,
1609 nonloc::SymbolVal(Pos.getOffset()), *IntDist,
1610 SymMgr.getType(Pos.getOffset()))
1611 .getAsSymbol());
1612 } else {
1613 // For other symbols create a new symbol to keep expressions simple
1614 const auto &LCtx = C.getLocationContext();
1615 const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx,
1616 SymMgr.getType(Pos.getOffset()),
1617 C.blockCount());
1618 State = assumeNoOverflow(State, NewPosSym, 4);
1619 return Pos.setTo(NewPosSym);
1620 }
1621}
1622
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001623void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
1624 const SVal &Val, CheckerContext &C,
1625 ExplodedNode *ErrNode) const {
Artem Dergachev2f169e72019-09-09 20:34:40 +00001626 auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
1627 ErrNode);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001628 R->markInteresting(Val);
1629 C.emitReport(std::move(R));
1630}
1631
Adam Balogh21583b72018-09-10 09:03:22 +00001632void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1633 const SVal &Val1, const SVal &Val2,
1634 CheckerContext &C,
1635 ExplodedNode *ErrNode) const {
Artem Dergachev2f169e72019-09-09 20:34:40 +00001636 auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
1637 ErrNode);
Adam Balogh21583b72018-09-10 09:03:22 +00001638 R->markInteresting(Val1);
1639 R->markInteresting(Val2);
1640 C.emitReport(std::move(R));
1641}
1642
Adam Balogh3659f7a2018-09-10 09:05:31 +00001643void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1644 const SVal &Val, const MemRegion *Reg,
1645 CheckerContext &C,
1646 ExplodedNode *ErrNode) const {
Artem Dergachev2f169e72019-09-09 20:34:40 +00001647 auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
1648 ErrNode);
Adam Balogh3659f7a2018-09-10 09:05:31 +00001649 R->markInteresting(Val);
1650 R->markInteresting(Reg);
1651 C.emitReport(std::move(R));
1652}
1653
Adam Balogh2cfbe932018-08-28 08:41:15 +00001654void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
1655 const SVal &Val, CheckerContext &C,
1656 ExplodedNode *ErrNode) const {
Artem Dergachev2f169e72019-09-09 20:34:40 +00001657 auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType,
1658 Message, ErrNode);
Adam Balogh2cfbe932018-08-28 08:41:15 +00001659 R->markInteresting(Val);
1660 C.emitReport(std::move(R));
1661}
1662
Adam Balogh0f88cae2019-11-08 08:56:31 +01001663bool IteratorChecker::evalCall(const CallEvent &Call,
1664 CheckerContext &C) const {
1665 if (!ChecksEnabled[CK_DebugIteratorModeling])
1666 return false;
1667
1668 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1669 if (!CE)
1670 return false;
1671
1672 const FnCheck *Handler = Callbacks.lookup(Call);
1673 if (!Handler)
1674 return false;
1675
1676 (this->**Handler)(CE, C);
1677 return true;
1678}
1679
1680template <typename Getter>
1681void IteratorChecker::analyzerContainerDataField(const CallExpr *CE,
1682 CheckerContext &C,
1683 Getter get) const {
1684 if (CE->getNumArgs() == 0) {
1685 reportDebugMsg("Missing container argument", C);
1686 return;
1687 }
1688
1689 auto State = C.getState();
1690 const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
1691 if (Cont) {
1692 const auto *Data = getContainerData(State, Cont);
1693 if (Data) {
1694 SymbolRef Field = get(Data);
1695 if (Field) {
1696 State = State->BindExpr(CE, C.getLocationContext(),
1697 nonloc::SymbolVal(Field));
1698 C.addTransition(State);
1699 return;
1700 }
1701 }
1702 }
1703
1704 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1705 State = State->BindExpr(CE, C.getLocationContext(),
1706 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1707}
1708
1709void IteratorChecker::analyzerContainerBegin(const CallExpr *CE,
1710 CheckerContext &C) const {
1711 analyzerContainerDataField(CE, C, [](const ContainerData *D) {
1712 return D->getBegin();
1713 });
1714}
1715
1716void IteratorChecker::analyzerContainerEnd(const CallExpr *CE,
1717 CheckerContext &C) const {
1718 analyzerContainerDataField(CE, C, [](const ContainerData *D) {
1719 return D->getEnd();
1720 });
1721}
1722
1723template <typename Getter>
1724void IteratorChecker::analyzerIteratorDataField(const CallExpr *CE,
1725 CheckerContext &C,
1726 Getter get,
1727 SVal Default) const {
1728 if (CE->getNumArgs() == 0) {
1729 reportDebugMsg("Missing iterator argument", C);
1730 return;
1731 }
1732
1733 auto State = C.getState();
1734 SVal V = C.getSVal(CE->getArg(0));
1735 const auto *Pos = getIteratorPosition(State, V);
1736 if (Pos) {
1737 State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
1738 } else {
1739 State = State->BindExpr(CE, C.getLocationContext(), Default);
1740 }
1741 C.addTransition(State);
1742}
1743
1744void IteratorChecker::analyzerIteratorPosition(const CallExpr *CE,
1745 CheckerContext &C) const {
1746 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1747 analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
1748 return nonloc::SymbolVal(P->getOffset());
1749 }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1750}
1751
1752void IteratorChecker::analyzerIteratorContainer(const CallExpr *CE,
1753 CheckerContext &C) const {
1754 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1755 analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
1756 return loc::MemRegionVal(P->getContainer());
1757 }, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1758}
1759
1760void IteratorChecker::analyzerIteratorValidity(const CallExpr *CE,
1761 CheckerContext &C) const {
1762 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1763 analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
1764 return
1765 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
1766 }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1767}
1768
1769ExplodedNode *IteratorChecker::reportDebugMsg(llvm::StringRef Msg,
1770 CheckerContext &C) const {
1771 ExplodedNode *N = C.generateNonFatalErrorNode();
1772 if (!N)
1773 return nullptr;
1774
1775 auto &BR = C.getBugReporter();
1776 BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
1777 Msg, N));
1778 return N;
1779}
1780
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001781namespace {
1782
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001783bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001784bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1785bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001786bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1787 BinaryOperator::Opcode Opc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001788bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1789 BinaryOperator::Opcode Opc);
Adam Balogh9a48ba62018-09-10 09:06:31 +00001790const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1791 const MemRegion *Reg);
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001792SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
1793 SymbolRef OldSym, SymbolRef NewSym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001794
1795bool isIteratorType(const QualType &Type) {
1796 if (Type->isPointerType())
1797 return true;
1798
1799 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1800 return isIterator(CRD);
1801}
1802
1803bool isIterator(const CXXRecordDecl *CRD) {
1804 if (!CRD)
1805 return false;
1806
1807 const auto Name = CRD->getName();
1808 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
1809 Name.endswith_lower("it")))
1810 return false;
1811
1812 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
1813 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
1814 for (const auto *Method : CRD->methods()) {
1815 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
1816 if (Ctor->isCopyConstructor()) {
1817 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
1818 }
1819 continue;
1820 }
1821 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
1822 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
1823 continue;
1824 }
1825 if (Method->isCopyAssignmentOperator()) {
1826 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
1827 continue;
1828 }
1829 if (!Method->isOverloadedOperator())
1830 continue;
1831 const auto OPK = Method->getOverloadedOperator();
1832 if (OPK == OO_PlusPlus) {
1833 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
1834 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
1835 continue;
1836 }
1837 if (OPK == OO_Star) {
1838 HasDerefOp = (Method->getNumParams() == 0);
1839 continue;
1840 }
1841 }
1842
1843 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
1844 HasPostIncrOp && HasDerefOp;
1845}
1846
Adam Balogh3659f7a2018-09-10 09:05:31 +00001847bool isComparisonOperator(OverloadedOperatorKind OK) {
1848 return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
1849 OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
1850}
1851
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001852bool isBeginCall(const FunctionDecl *Func) {
1853 const auto *IdInfo = Func->getIdentifier();
1854 if (!IdInfo)
1855 return false;
1856 return IdInfo->getName().endswith_lower("begin");
1857}
1858
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001859bool isEndCall(const FunctionDecl *Func) {
1860 const auto *IdInfo = Func->getIdentifier();
1861 if (!IdInfo)
1862 return false;
1863 return IdInfo->getName().endswith_lower("end");
1864}
1865
Adam Balogh2e7cb342018-09-10 09:07:47 +00001866bool isAssignCall(const FunctionDecl *Func) {
1867 const auto *IdInfo = Func->getIdentifier();
1868 if (!IdInfo)
1869 return false;
1870 if (Func->getNumParams() > 2)
1871 return false;
1872 return IdInfo->getName() == "assign";
1873}
1874
1875bool isClearCall(const FunctionDecl *Func) {
1876 const auto *IdInfo = Func->getIdentifier();
1877 if (!IdInfo)
1878 return false;
1879 if (Func->getNumParams() > 0)
1880 return false;
1881 return IdInfo->getName() == "clear";
1882}
1883
Adam Balogh9a48ba62018-09-10 09:06:31 +00001884bool isPushBackCall(const FunctionDecl *Func) {
1885 const auto *IdInfo = Func->getIdentifier();
1886 if (!IdInfo)
1887 return false;
1888 if (Func->getNumParams() != 1)
1889 return false;
1890 return IdInfo->getName() == "push_back";
1891}
1892
1893bool isEmplaceBackCall(const FunctionDecl *Func) {
1894 const auto *IdInfo = Func->getIdentifier();
1895 if (!IdInfo)
1896 return false;
1897 if (Func->getNumParams() < 1)
1898 return false;
1899 return IdInfo->getName() == "emplace_back";
1900}
1901
1902bool isPopBackCall(const FunctionDecl *Func) {
1903 const auto *IdInfo = Func->getIdentifier();
1904 if (!IdInfo)
1905 return false;
1906 if (Func->getNumParams() > 0)
1907 return false;
1908 return IdInfo->getName() == "pop_back";
1909}
1910
1911bool isPushFrontCall(const FunctionDecl *Func) {
1912 const auto *IdInfo = Func->getIdentifier();
1913 if (!IdInfo)
1914 return false;
1915 if (Func->getNumParams() != 1)
1916 return false;
1917 return IdInfo->getName() == "push_front";
1918}
1919
1920bool isEmplaceFrontCall(const FunctionDecl *Func) {
1921 const auto *IdInfo = Func->getIdentifier();
1922 if (!IdInfo)
1923 return false;
1924 if (Func->getNumParams() < 1)
1925 return false;
1926 return IdInfo->getName() == "emplace_front";
1927}
1928
1929bool isPopFrontCall(const FunctionDecl *Func) {
1930 const auto *IdInfo = Func->getIdentifier();
1931 if (!IdInfo)
1932 return false;
1933 if (Func->getNumParams() > 0)
1934 return false;
1935 return IdInfo->getName() == "pop_front";
1936}
1937
Adam Balogh2e7cb342018-09-10 09:07:47 +00001938bool isInsertCall(const FunctionDecl *Func) {
1939 const auto *IdInfo = Func->getIdentifier();
1940 if (!IdInfo)
1941 return false;
1942 if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
1943 return false;
1944 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1945 return false;
1946 return IdInfo->getName() == "insert";
1947}
1948
1949bool isEmplaceCall(const FunctionDecl *Func) {
1950 const auto *IdInfo = Func->getIdentifier();
1951 if (!IdInfo)
1952 return false;
1953 if (Func->getNumParams() < 2)
1954 return false;
1955 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1956 return false;
1957 return IdInfo->getName() == "emplace";
1958}
1959
1960bool isEraseCall(const FunctionDecl *Func) {
1961 const auto *IdInfo = Func->getIdentifier();
1962 if (!IdInfo)
1963 return false;
1964 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1965 return false;
1966 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1967 return false;
1968 if (Func->getNumParams() == 2 &&
1969 !isIteratorType(Func->getParamDecl(1)->getType()))
1970 return false;
1971 return IdInfo->getName() == "erase";
1972}
1973
1974bool isEraseAfterCall(const FunctionDecl *Func) {
1975 const auto *IdInfo = Func->getIdentifier();
1976 if (!IdInfo)
1977 return false;
1978 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1979 return false;
1980 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1981 return false;
1982 if (Func->getNumParams() == 2 &&
1983 !isIteratorType(Func->getParamDecl(1)->getType()))
1984 return false;
1985 return IdInfo->getName() == "erase_after";
1986}
1987
Adam Balogh2cfbe932018-08-28 08:41:15 +00001988bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1989
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001990bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1991 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1992}
1993
Adam Balogh2cfbe932018-08-28 08:41:15 +00001994bool isAccessOperator(OverloadedOperatorKind OK) {
1995 return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
1996 isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
1997}
1998
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001999bool isDereferenceOperator(OverloadedOperatorKind OK) {
2000 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
2001 OK == OO_Subscript;
2002}
2003
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002004bool isIncrementOperator(OverloadedOperatorKind OK) {
2005 return OK == OO_PlusPlus;
2006}
2007
2008bool isDecrementOperator(OverloadedOperatorKind OK) {
2009 return OK == OO_MinusMinus;
2010}
2011
2012bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
2013 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
2014 OK == OO_MinusEqual;
2015}
2016
Adam Balogh9a48ba62018-09-10 09:06:31 +00002017bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
2018 const auto *CRD = getCXXRecordDecl(State, Reg);
2019 if (!CRD)
2020 return false;
2021
2022 for (const auto *Method : CRD->methods()) {
2023 if (!Method->isOverloadedOperator())
2024 continue;
2025 const auto OPK = Method->getOverloadedOperator();
2026 if (OPK == OO_Subscript) {
2027 return true;
2028 }
2029 }
2030 return false;
2031}
2032
2033bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
2034 const auto *CRD = getCXXRecordDecl(State, Reg);
2035 if (!CRD)
2036 return false;
2037
2038 for (const auto *Method : CRD->methods()) {
2039 if (!Method->getDeclName().isIdentifier())
2040 continue;
2041 if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
2042 return true;
2043 }
2044 }
2045 return false;
2046}
2047
2048bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
2049 const auto *CRD = getCXXRecordDecl(State, Reg);
2050 if (!CRD)
2051 return false;
2052
2053 for (const auto *Method : CRD->methods()) {
2054 if (!Method->getDeclName().isIdentifier())
2055 continue;
2056 if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
2057 return true;
2058 }
2059 }
2060 return false;
2061}
2062
2063const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
2064 const MemRegion *Reg) {
2065 auto TI = getDynamicTypeInfo(State, Reg);
2066 if (!TI.isValid())
2067 return nullptr;
2068
2069 auto Type = TI.getType();
2070 if (const auto *RefT = Type->getAs<ReferenceType>()) {
2071 Type = RefT->getPointeeType();
2072 }
2073
2074 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
2075}
2076
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002077SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
2078 const auto *CDataPtr = getContainerData(State, Cont);
2079 if (!CDataPtr)
2080 return nullptr;
2081
2082 return CDataPtr->getBegin();
2083}
2084
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002085SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
2086 const auto *CDataPtr = getContainerData(State, Cont);
2087 if (!CDataPtr)
2088 return nullptr;
2089
2090 return CDataPtr->getEnd();
2091}
2092
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002093ProgramStateRef createContainerBegin(ProgramStateRef State,
Adam Balogh33160c42019-05-20 11:04:27 +00002094 const MemRegion *Cont, const Expr *E,
2095 QualType T, const LocationContext *LCtx,
2096 unsigned BlockCount) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002097 // Only create if it does not exist
2098 const auto *CDataPtr = getContainerData(State, Cont);
Adam Balogh33160c42019-05-20 11:04:27 +00002099 if (CDataPtr && CDataPtr->getBegin())
2100 return State;
2101
2102 auto &SymMgr = State->getSymbolManager();
2103 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
2104 "begin");
2105 State = assumeNoOverflow(State, Sym, 4);
2106
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002107 if (CDataPtr) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002108 const auto CData = CDataPtr->newBegin(Sym);
2109 return setContainerData(State, Cont, CData);
2110 }
Adam Balogh33160c42019-05-20 11:04:27 +00002111
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002112 const auto CData = ContainerData::fromBegin(Sym);
2113 return setContainerData(State, Cont, CData);
2114}
2115
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002116ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
Adam Balogh33160c42019-05-20 11:04:27 +00002117 const Expr *E, QualType T,
2118 const LocationContext *LCtx,
2119 unsigned BlockCount) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002120 // Only create if it does not exist
2121 const auto *CDataPtr = getContainerData(State, Cont);
Adam Balogh33160c42019-05-20 11:04:27 +00002122 if (CDataPtr && CDataPtr->getEnd())
2123 return State;
2124
2125 auto &SymMgr = State->getSymbolManager();
2126 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
2127 "end");
2128 State = assumeNoOverflow(State, Sym, 4);
2129
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002130 if (CDataPtr) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002131 const auto CData = CDataPtr->newEnd(Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002132 return setContainerData(State, Cont, CData);
2133 }
Adam Balogh33160c42019-05-20 11:04:27 +00002134
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002135 const auto CData = ContainerData::fromEnd(Sym);
2136 return setContainerData(State, Cont, CData);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002137}
2138
2139const ContainerData *getContainerData(ProgramStateRef State,
2140 const MemRegion *Cont) {
2141 return State->get<ContainerMap>(Cont);
2142}
2143
2144ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
2145 const ContainerData &CData) {
2146 return State->set<ContainerMap>(Cont, CData);
2147}
2148
2149const IteratorPosition *getIteratorPosition(ProgramStateRef State,
2150 const SVal &Val) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002151 if (auto Reg = Val.getAsRegion()) {
2152 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002153 return State->get<IteratorRegionMap>(Reg);
2154 } else if (const auto Sym = Val.getAsSymbol()) {
2155 return State->get<IteratorSymbolMap>(Sym);
2156 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2157 return State->get<IteratorRegionMap>(LCVal->getRegion());
2158 }
2159 return nullptr;
2160}
2161
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002162ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
2163 const IteratorPosition &Pos) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002164 if (auto Reg = Val.getAsRegion()) {
2165 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002166 return State->set<IteratorRegionMap>(Reg, Pos);
2167 } else if (const auto Sym = Val.getAsSymbol()) {
2168 return State->set<IteratorSymbolMap>(Sym, Pos);
2169 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2170 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
2171 }
2172 return nullptr;
2173}
2174
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002175ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002176 if (auto Reg = Val.getAsRegion()) {
2177 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002178 return State->remove<IteratorRegionMap>(Reg);
2179 } else if (const auto Sym = Val.getAsSymbol()) {
2180 return State->remove<IteratorSymbolMap>(Sym);
2181 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2182 return State->remove<IteratorRegionMap>(LCVal->getRegion());
2183 }
2184 return nullptr;
2185}
2186
Adam Balogh54976e72019-04-23 07:15:55 +00002187ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
2188 SymbolRef Sym2, bool Equal) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002189 auto &SVB = State->getStateManager().getSValBuilder();
Adam Balogh0a7592b2018-07-16 09:27:27 +00002190
2191 // FIXME: This code should be reworked as follows:
2192 // 1. Subtract the operands using evalBinOp().
2193 // 2. Assume that the result doesn't overflow.
2194 // 3. Compare the result to 0.
2195 // 4. Assume the result of the comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002196 const auto comparison =
Adam Balogh54976e72019-04-23 07:15:55 +00002197 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
2198 nonloc::SymbolVal(Sym2), SVB.getConditionType());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002199
Adam Balogh0a7592b2018-07-16 09:27:27 +00002200 assert(comparison.getAs<DefinedSVal>() &&
2201 "Symbol comparison must be a `DefinedSVal`");
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002202
Adam Balogh0a7592b2018-07-16 09:27:27 +00002203 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
Adam Balogh54976e72019-04-23 07:15:55 +00002204 if (!NewState)
2205 return nullptr;
2206
Adam Balogh0a7592b2018-07-16 09:27:27 +00002207 if (const auto CompSym = comparison.getAsSymbol()) {
2208 assert(isa<SymIntExpr>(CompSym) &&
2209 "Symbol comparison must be a `SymIntExpr`");
2210 assert(BinaryOperator::isComparisonOp(
2211 cast<SymIntExpr>(CompSym)->getOpcode()) &&
2212 "Symbol comparison must be a comparison");
2213 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002214 }
2215
Adam Balogh0a7592b2018-07-16 09:27:27 +00002216 return NewState;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002217}
2218
Adam Balogha6921202018-07-30 08:52:21 +00002219bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
2220 auto RegionMap = State->get<IteratorRegionMap>();
2221 for (const auto Reg : RegionMap) {
2222 if (Reg.second.getContainer() == Cont)
2223 return true;
2224 }
2225
2226 auto SymbolMap = State->get<IteratorSymbolMap>();
2227 for (const auto Sym : SymbolMap) {
2228 if (Sym.second.getContainer() == Cont)
2229 return true;
2230 }
2231
2232 return false;
2233}
2234
Adam Balogh21583b72018-09-10 09:03:22 +00002235bool isBoundThroughLazyCompoundVal(const Environment &Env,
2236 const MemRegion *Reg) {
2237 for (const auto Binding: Env) {
2238 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
2239 if (LCVal->getRegion() == Reg)
2240 return true;
2241 }
2242 }
2243
2244 return false;
2245}
2246
Adam Balogh54976e72019-04-23 07:15:55 +00002247// This function tells the analyzer's engine that symbols produced by our
2248// checker, most notably iterator positions, are relatively small.
2249// A distance between items in the container should not be very large.
2250// By assuming that it is within around 1/8 of the address space,
2251// we can help the analyzer perform operations on these symbols
2252// without being afraid of integer overflows.
2253// FIXME: Should we provide it as an API, so that all checkers could use it?
2254ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
2255 long Scale) {
2256 SValBuilder &SVB = State->getStateManager().getSValBuilder();
2257 BasicValueFactory &BV = SVB.getBasicValueFactory();
2258
2259 QualType T = Sym->getType();
2260 assert(T->isSignedIntegerOrEnumerationType());
2261 APSIntType AT = BV.getAPSIntType(T);
2262
2263 ProgramStateRef NewState = State;
2264
2265 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
2266 SVal IsCappedFromAbove =
2267 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
2268 nonloc::ConcreteInt(Max), SVB.getConditionType());
2269 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
2270 NewState = NewState->assume(*DV, true);
2271 if (!NewState)
2272 return State;
2273 }
2274
2275 llvm::APSInt Min = -Max;
2276 SVal IsCappedFromBelow =
2277 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
2278 nonloc::ConcreteInt(Min), SVB.getConditionType());
2279 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
2280 NewState = NewState->assume(*DV, true);
2281 if (!NewState)
2282 return State;
2283 }
2284
2285 return NewState;
2286}
2287
Adam Balogh2cfbe932018-08-28 08:41:15 +00002288template <typename Condition, typename Process>
2289ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
2290 Process Proc) {
2291 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
2292 auto RegionMap = State->get<IteratorRegionMap>();
2293 bool Changed = false;
2294 for (const auto Reg : RegionMap) {
2295 if (Cond(Reg.second)) {
2296 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
2297 Changed = true;
2298 }
2299 }
2300
2301 if (Changed)
2302 State = State->set<IteratorRegionMap>(RegionMap);
2303
2304 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
2305 auto SymbolMap = State->get<IteratorSymbolMap>();
2306 Changed = false;
2307 for (const auto Sym : SymbolMap) {
2308 if (Cond(Sym.second)) {
2309 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
2310 Changed = true;
2311 }
2312 }
2313
2314 if (Changed)
2315 State = State->set<IteratorSymbolMap>(SymbolMap);
2316
2317 return State;
2318}
2319
2320ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
2321 const MemRegion *Cont) {
2322 auto MatchCont = [&](const IteratorPosition &Pos) {
2323 return Pos.getContainer() == Cont;
2324 };
2325 auto Invalidate = [&](const IteratorPosition &Pos) {
2326 return Pos.invalidate();
2327 };
2328 return processIteratorPositions(State, MatchCont, Invalidate);
2329}
2330
Adam Balogh2e7cb342018-09-10 09:07:47 +00002331ProgramStateRef
2332invalidateAllIteratorPositionsExcept(ProgramStateRef State,
2333 const MemRegion *Cont, SymbolRef Offset,
2334 BinaryOperator::Opcode Opc) {
2335 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2336 return Pos.getContainer() == Cont &&
2337 !compare(State, Pos.getOffset(), Offset, Opc);
2338 };
2339 auto Invalidate = [&](const IteratorPosition &Pos) {
2340 return Pos.invalidate();
2341 };
2342 return processIteratorPositions(State, MatchContAndCompare, Invalidate);
2343}
2344
Adam Balogh9a48ba62018-09-10 09:06:31 +00002345ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2346 SymbolRef Offset,
2347 BinaryOperator::Opcode Opc) {
2348 auto Compare = [&](const IteratorPosition &Pos) {
2349 return compare(State, Pos.getOffset(), Offset, Opc);
2350 };
2351 auto Invalidate = [&](const IteratorPosition &Pos) {
2352 return Pos.invalidate();
2353 };
2354 return processIteratorPositions(State, Compare, Invalidate);
2355}
2356
Adam Balogh2e7cb342018-09-10 09:07:47 +00002357ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2358 SymbolRef Offset1,
2359 BinaryOperator::Opcode Opc1,
2360 SymbolRef Offset2,
2361 BinaryOperator::Opcode Opc2) {
2362 auto Compare = [&](const IteratorPosition &Pos) {
2363 return compare(State, Pos.getOffset(), Offset1, Opc1) &&
2364 compare(State, Pos.getOffset(), Offset2, Opc2);
2365 };
2366 auto Invalidate = [&](const IteratorPosition &Pos) {
2367 return Pos.invalidate();
2368 };
2369 return processIteratorPositions(State, Compare, Invalidate);
2370}
2371
Adam Balogh6b23b1a2018-09-10 09:04:27 +00002372ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
2373 const MemRegion *Cont,
2374 const MemRegion *NewCont) {
2375 auto MatchCont = [&](const IteratorPosition &Pos) {
2376 return Pos.getContainer() == Cont;
2377 };
2378 auto ReAssign = [&](const IteratorPosition &Pos) {
2379 return Pos.reAssign(NewCont);
2380 };
2381 return processIteratorPositions(State, MatchCont, ReAssign);
2382}
2383
2384ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
2385 const MemRegion *Cont,
2386 const MemRegion *NewCont,
2387 SymbolRef Offset,
2388 BinaryOperator::Opcode Opc) {
2389 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2390 return Pos.getContainer() == Cont &&
2391 !compare(State, Pos.getOffset(), Offset, Opc);
2392 };
2393 auto ReAssign = [&](const IteratorPosition &Pos) {
2394 return Pos.reAssign(NewCont);
2395 };
2396 return processIteratorPositions(State, MatchContAndCompare, ReAssign);
2397}
2398
2399// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
2400// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
2401// position offsets where `CondSym` is true.
2402ProgramStateRef rebaseSymbolInIteratorPositionsIf(
2403 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
2404 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
2405 auto LessThanEnd = [&](const IteratorPosition &Pos) {
2406 return compare(State, Pos.getOffset(), CondSym, Opc);
2407 };
2408 auto RebaseSymbol = [&](const IteratorPosition &Pos) {
2409 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
2410 NewSym));
2411 };
2412 return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
2413}
2414
2415// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
2416// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
2417// `OrigExpr`.
2418SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
2419 SymbolRef OrigExpr, SymbolRef OldExpr,
2420 SymbolRef NewSym) {
2421 auto &SymMgr = SVB.getSymbolManager();
2422 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
2423 nonloc::SymbolVal(OldExpr),
2424 SymMgr.getType(OrigExpr));
2425
2426 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
2427 if (!DiffInt)
2428 return OrigExpr;
2429
2430 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
2431 SymMgr.getType(OrigExpr)).getAsSymbol();
2432}
2433
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002434bool isZero(ProgramStateRef State, const NonLoc &Val) {
2435 auto &BVF = State->getBasicVals();
2436 return compare(State, Val,
2437 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
2438 BO_EQ);
2439}
2440
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002441bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002442 const auto *Cont = Pos.getContainer();
2443 const auto *CData = getContainerData(State, Cont);
2444 if (!CData)
2445 return false;
2446
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002447 const auto End = CData->getEnd();
2448 if (End) {
2449 if (isEqual(State, Pos.getOffset(), End)) {
2450 return true;
2451 }
2452 }
2453
2454 return false;
2455}
2456
2457bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
2458 const auto *Cont = Pos.getContainer();
2459 const auto *CData = getContainerData(State, Cont);
2460 if (!CData)
2461 return false;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002462
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002463 const auto Beg = CData->getBegin();
2464 if (Beg) {
2465 if (isLess(State, Pos.getOffset(), Beg)) {
2466 return true;
2467 }
2468 }
2469
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002470 return false;
2471}
2472
2473bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
2474 const auto *Cont = Pos.getContainer();
2475 const auto *CData = getContainerData(State, Cont);
2476 if (!CData)
2477 return false;
2478
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002479 const auto End = CData->getEnd();
2480 if (End) {
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002481 if (isGreater(State, Pos.getOffset(), End)) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002482 return true;
2483 }
2484 }
2485
2486 return false;
2487}
2488
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002489bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2490 return compare(State, Sym1, Sym2, BO_LT);
2491}
2492
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002493bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2494 return compare(State, Sym1, Sym2, BO_GT);
2495}
2496
2497bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2498 return compare(State, Sym1, Sym2, BO_EQ);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002499}
2500
2501bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
2502 BinaryOperator::Opcode Opc) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002503 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
2504}
2505
2506bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
2507 BinaryOperator::Opcode Opc) {
2508 auto &SVB = State->getStateManager().getSValBuilder();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002509
2510 const auto comparison =
Adam Balogh0a7592b2018-07-16 09:27:27 +00002511 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002512
Adam Balogh0a7592b2018-07-16 09:27:27 +00002513 assert(comparison.getAs<DefinedSVal>() &&
2514 "Symbol comparison must be a `DefinedSVal`");
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002515
Adam Balogh0a7592b2018-07-16 09:27:27 +00002516 return !State->assume(comparison.castAs<DefinedSVal>(), false);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002517}
2518
2519} // namespace
2520
Kristof Umann8fd74eb2019-01-26 20:06:54 +00002521void ento::registerIteratorModeling(CheckerManager &mgr) {
2522 mgr.registerChecker<IteratorChecker>();
2523}
2524
2525bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
2526 return true;
2527}
2528
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002529#define REGISTER_CHECKER(name) \
2530 void ento::register##name(CheckerManager &Mgr) { \
Kristof Umann204bf2b2019-01-26 21:41:50 +00002531 auto *checker = Mgr.getChecker<IteratorChecker>(); \
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002532 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
2533 checker->CheckNames[IteratorChecker::CK_##name] = \
Kristof Umann72649422019-09-12 19:09:24 +00002534 Mgr.getCurrentCheckerName(); \
Kristof Umann058a7a42019-01-26 14:23:08 +00002535 } \
2536 \
Kristof Umann72649422019-09-12 19:09:24 +00002537 bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002538
2539REGISTER_CHECKER(IteratorRangeChecker)
Adam Balogh21583b72018-09-10 09:03:22 +00002540REGISTER_CHECKER(MismatchedIteratorChecker)
Adam Balogh2cfbe932018-08-28 08:41:15 +00002541REGISTER_CHECKER(InvalidatedIteratorChecker)
Adam Balogh0f88cae2019-11-08 08:56:31 +01002542REGISTER_CHECKER(DebugIteratorModeling)