blob: a84327f07a52a790ac7ae9fa2eb8f179c021d37b [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();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000803 const auto *Pos =
George Karpenkovd703ec92018-01-17 20:27:29 +0000804 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000805 if (!Pos)
806 return;
George Karpenkovd703ec92018-01-17 20:27:29 +0000807 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000808 C.addTransition(State);
809}
810
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000811void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
812 SymbolReaper &SR) const {
813 // Keep symbolic expressions of iterator positions, container begins and ends
814 // alive
815 auto RegionMap = State->get<IteratorRegionMap>();
816 for (const auto Reg : RegionMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000817 const auto Offset = Reg.second.getOffset();
818 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
819 if (isa<SymbolData>(*i))
820 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000821 }
822
823 auto SymbolMap = State->get<IteratorSymbolMap>();
824 for (const auto Sym : SymbolMap) {
Adam Balogh0a7592b2018-07-16 09:27:27 +0000825 const auto Offset = Sym.second.getOffset();
826 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
827 if (isa<SymbolData>(*i))
828 SR.markLive(*i);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000829 }
830
831 auto ContMap = State->get<ContainerMap>();
832 for (const auto Cont : ContMap) {
833 const auto CData = Cont.second;
834 if (CData.getBegin()) {
835 SR.markLive(CData.getBegin());
Adam Balogh9a48ba62018-09-10 09:06:31 +0000836 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
837 SR.markLive(SIE->getLHS());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000838 }
839 if (CData.getEnd()) {
840 SR.markLive(CData.getEnd());
Adam Balogh9a48ba62018-09-10 09:06:31 +0000841 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
842 SR.markLive(SIE->getLHS());
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000843 }
844 }
845}
846
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000847void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
848 CheckerContext &C) const {
849 // Cleanup
850 auto State = C.getState();
851
852 auto RegionMap = State->get<IteratorRegionMap>();
853 for (const auto Reg : RegionMap) {
854 if (!SR.isLiveRegion(Reg.first)) {
Adam Balogh21583b72018-09-10 09:03:22 +0000855 // The region behind the `LazyCompoundVal` is often cleaned up before
856 // the `LazyCompoundVal` itself. If there are iterator positions keyed
857 // by these regions their cleanup must be deferred.
858 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
859 State = State->remove<IteratorRegionMap>(Reg.first);
860 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000861 }
862 }
863
864 auto SymbolMap = State->get<IteratorSymbolMap>();
865 for (const auto Sym : SymbolMap) {
866 if (!SR.isLive(Sym.first)) {
867 State = State->remove<IteratorSymbolMap>(Sym.first);
868 }
869 }
870
871 auto ContMap = State->get<ContainerMap>();
872 for (const auto Cont : ContMap) {
873 if (!SR.isLiveRegion(Cont.first)) {
Adam Balogha6921202018-07-30 08:52:21 +0000874 // We must keep the container data while it has live iterators to be able
875 // to compare them to the begin and the end of the container.
876 if (!hasLiveIterators(State, Cont.first)) {
877 State = State->remove<ContainerMap>(Cont.first);
878 }
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000879 }
880 }
881
Reka Kovacse48ea892018-07-30 16:14:59 +0000882 C.addTransition(State);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000883}
884
Adam Balogh54976e72019-04-23 07:15:55 +0000885void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE,
886 const SVal &RetVal, const SVal &LVal,
887 const SVal &RVal,
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000888 OverloadedOperatorKind Op) const {
889 // Record the operands and the operator of the comparison for the next
890 // evalAssume, if the result is a symbolic expression. If it is a concrete
891 // value (only one branch is possible), then transfer the state between
892 // the operands according to the operator and the result
Adam Balogh54976e72019-04-23 07:15:55 +0000893 auto State = C.getState();
894 const auto *LPos = getIteratorPosition(State, LVal);
895 const auto *RPos = getIteratorPosition(State, RVal);
896 const MemRegion *Cont = nullptr;
897 if (LPos) {
898 Cont = LPos->getContainer();
899 } else if (RPos) {
900 Cont = RPos->getContainer();
901 }
902 if (!Cont)
903 return;
904
905 // At least one of the iterators have recorded positions. If one of them has
906 // not then create a new symbol for the offset.
907 SymbolRef Sym;
908 if (!LPos || !RPos) {
909 auto &SymMgr = C.getSymbolManager();
Adam Baloghd2e2e202019-04-23 11:18:50 +0000910 Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
Adam Balogh54976e72019-04-23 07:15:55 +0000911 C.getASTContext().LongTy, C.blockCount());
912 State = assumeNoOverflow(State, Sym, 4);
913 }
914
915 if (!LPos) {
916 State = setIteratorPosition(State, LVal,
917 IteratorPosition::getPosition(Cont, Sym));
918 LPos = getIteratorPosition(State, LVal);
919 } else if (!RPos) {
920 State = setIteratorPosition(State, RVal,
921 IteratorPosition::getPosition(Cont, Sym));
922 RPos = getIteratorPosition(State, RVal);
923 }
924
925 processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
926}
927
928void IteratorChecker::processComparison(CheckerContext &C,
929 ProgramStateRef State, SymbolRef Sym1,
930 SymbolRef Sym2, const SVal &RetVal,
931 OverloadedOperatorKind Op) const {
932 if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
Adam Balogh8f882702019-04-23 07:45:10 +0000933 if ((State = relateSymbols(State, Sym1, Sym2,
Adam Balogh54976e72019-04-23 07:15:55 +0000934 (Op == OO_EqualEqual) ==
Adam Balogh8f882702019-04-23 07:45:10 +0000935 (TruthVal->getValue() != 0)))) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000936 C.addTransition(State);
937 } else {
938 C.generateSink(State, C.getPredecessor());
939 }
Adam Balogh54976e72019-04-23 07:15:55 +0000940 return;
941 }
942
943 const auto ConditionVal = RetVal.getAs<DefinedSVal>();
944 if (!ConditionVal)
945 return;
946
947 if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
948 StateTrue = StateTrue->assume(*ConditionVal, true);
949 C.addTransition(StateTrue);
950 }
951
952 if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
953 StateFalse = StateFalse->assume(*ConditionVal, false);
954 C.addTransition(StateFalse);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000955 }
956}
957
958void IteratorChecker::verifyDereference(CheckerContext &C,
959 const SVal &Val) const {
960 auto State = C.getState();
961 const auto *Pos = getIteratorPosition(State, Val);
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000962 if (Pos && isPastTheEnd(State, *Pos)) {
Adam Balogh12f5c7f2019-08-29 09:35:47 +0000963 auto *N = C.generateErrorNode(State);
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000964 if (!N)
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000965 return;
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000966 reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
967 return;
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000968 }
969}
970
Adam Balogh2cfbe932018-08-28 08:41:15 +0000971void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
972 auto State = C.getState();
973 const auto *Pos = getIteratorPosition(State, Val);
974 if (Pos && !Pos->isValid()) {
Adam Balogh12f5c7f2019-08-29 09:35:47 +0000975 auto *N = C.generateErrorNode(State);
Adam Balogh2cfbe932018-08-28 08:41:15 +0000976 if (!N) {
977 return;
978 }
979 reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
980 }
981}
982
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000983void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
984 const SVal &Iter, bool Postfix) const {
985 // Increment the symbolic expressions which represents the position of the
986 // iterator
987 auto State = C.getState();
988 const auto *Pos = getIteratorPosition(State, Iter);
989 if (Pos) {
990 auto &SymMgr = C.getSymbolManager();
991 auto &BVF = SymMgr.getBasicVals();
Adam Baloghd5bd3f62018-12-04 10:27:27 +0000992 const auto NewPos =
993 advancePosition(C, OO_Plus, *Pos,
994 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
Adam Baloghb03ed5e2018-06-28 10:58:53 +0000995 State = setIteratorPosition(State, Iter, NewPos);
996 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
997 C.addTransition(State);
998 }
999}
1000
1001void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
1002 const SVal &Iter, bool Postfix) const {
1003 // Decrement the symbolic expressions which represents the position of the
1004 // iterator
1005 auto State = C.getState();
1006 const auto *Pos = getIteratorPosition(State, Iter);
1007 if (Pos) {
1008 auto &SymMgr = C.getSymbolManager();
1009 auto &BVF = SymMgr.getBasicVals();
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001010 const auto NewPos =
1011 advancePosition(C, OO_Minus, *Pos,
1012 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001013 State = setIteratorPosition(State, Iter, NewPos);
1014 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
1015 C.addTransition(State);
1016 }
1017}
1018
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001019void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
1020 OverloadedOperatorKind Op,
1021 const SVal &RetVal,
1022 const SVal &LHS,
1023 const SVal &RHS) const {
1024 // Increment or decrement the symbolic expressions which represents the
1025 // position of the iterator
1026 auto State = C.getState();
1027 const auto *Pos = getIteratorPosition(State, LHS);
1028 if (!Pos)
1029 return;
1030
1031 const auto *value = &RHS;
1032 if (auto loc = RHS.getAs<Loc>()) {
1033 const auto val = State->getRawSVal(*loc);
1034 value = &val;
1035 }
1036
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001037 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001038 State =
1039 setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value));
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001040 C.addTransition(State);
1041}
1042
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001043void IteratorChecker::verifyIncrement(CheckerContext &C,
1044 const SVal &Iter) const {
1045 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1046 verifyRandomIncrOrDecr(C, OO_Plus, Iter,
1047 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
1048}
1049
1050void IteratorChecker::verifyDecrement(CheckerContext &C,
1051 const SVal &Iter) const {
1052 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1053 verifyRandomIncrOrDecr(C, OO_Minus, Iter,
1054 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
1055}
1056
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001057void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
1058 OverloadedOperatorKind Op,
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001059 const SVal &LHS,
1060 const SVal &RHS) const {
1061 auto State = C.getState();
1062
1063 // If the iterator is initially inside its range, then the operation is valid
1064 const auto *Pos = getIteratorPosition(State, LHS);
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001065 if (!Pos)
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001066 return;
1067
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001068 auto Value = RHS;
1069 if (auto ValAsLoc = RHS.getAs<Loc>()) {
1070 Value = State->getRawSVal(*ValAsLoc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001071 }
1072
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001073 if (Value.isUnknown())
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001074 return;
1075
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001076 // Incremention or decremention by 0 is never a bug.
1077 if (isZero(State, Value.castAs<NonLoc>()))
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001078 return;
1079
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001080 // The result may be the past-end iterator of the container, but any other
1081 // out of range position is undefined behaviour
1082 if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) {
Adam Balogh12f5c7f2019-08-29 09:35:47 +00001083 auto *N = C.generateErrorNode(State);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001084 if (!N)
1085 return;
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001086 reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
1087 C, N);
1088 }
1089 if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
Adam Balogh12f5c7f2019-08-29 09:35:47 +00001090 auto *N = C.generateErrorNode(State);
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001091 if (!N)
1092 return;
1093 reportOutOfRangeBug("Iterator incremented behind the past-the-end "
1094 "iterator.", LHS, C, N);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001095 }
1096}
1097
Adam Balogh2e7cb342018-09-10 09:07:47 +00001098void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
1099 const MemRegion *Cont) const {
1100 // Verify match between a container and the container of an iterator
Adam Balogh42d241f2018-12-04 10:22:28 +00001101 Cont = Cont->getMostDerivedObjectRegion();
Adam Balogh2e7cb342018-09-10 09:07:47 +00001102
Adam Baloghd7033052019-03-13 13:55:11 +00001103 if (const auto *ContSym = Cont->getSymbolicBase()) {
1104 if (isa<SymbolConjured>(ContSym->getSymbol()))
1105 return;
1106 }
1107
Adam Balogh2e7cb342018-09-10 09:07:47 +00001108 auto State = C.getState();
1109 const auto *Pos = getIteratorPosition(State, Iter);
Adam Baloghd7033052019-03-13 13:55:11 +00001110 if (!Pos)
1111 return;
1112
1113 const auto *IterCont = Pos->getContainer();
1114
1115 // Skip symbolic regions based on conjured symbols. Two conjured symbols
1116 // may or may not be the same. For example, the same function can return
1117 // the same or a different container but we get different conjured symbols
1118 // for each call. This may cause false positives so omit them from the check.
1119 if (const auto *ContSym = IterCont->getSymbolicBase()) {
1120 if (isa<SymbolConjured>(ContSym->getSymbol()))
1121 return;
1122 }
1123
1124 if (IterCont != Cont) {
Adam Balogh2e7cb342018-09-10 09:07:47 +00001125 auto *N = C.generateNonFatalErrorNode(State);
1126 if (!N) {
1127 return;
1128 }
Adam Baloghd7033052019-03-13 13:55:11 +00001129 reportMismatchedBug("Container accessed using foreign iterator argument.",
1130 Iter, Cont, C, N);
Adam Balogh2e7cb342018-09-10 09:07:47 +00001131 }
1132}
1133
Adam Balogh21583b72018-09-10 09:03:22 +00001134void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
1135 const SVal &Iter2) const {
1136 // Verify match between the containers of two iterators
1137 auto State = C.getState();
1138 const auto *Pos1 = getIteratorPosition(State, Iter1);
Adam Baloghd7033052019-03-13 13:55:11 +00001139 if (!Pos1)
1140 return;
1141
1142 const auto *IterCont1 = Pos1->getContainer();
1143
1144 // Skip symbolic regions based on conjured symbols. Two conjured symbols
1145 // may or may not be the same. For example, the same function can return
1146 // the same or a different container but we get different conjured symbols
1147 // for each call. This may cause false positives so omit them from the check.
1148 if (const auto *ContSym = IterCont1->getSymbolicBase()) {
1149 if (isa<SymbolConjured>(ContSym->getSymbol()))
1150 return;
1151 }
1152
Adam Balogh21583b72018-09-10 09:03:22 +00001153 const auto *Pos2 = getIteratorPosition(State, Iter2);
Adam Baloghd7033052019-03-13 13:55:11 +00001154 if (!Pos2)
1155 return;
1156
1157 const auto *IterCont2 = Pos2->getContainer();
1158 if (const auto *ContSym = IterCont2->getSymbolicBase()) {
1159 if (isa<SymbolConjured>(ContSym->getSymbol()))
1160 return;
1161 }
1162
1163 if (IterCont1 != IterCont2) {
Adam Balogh21583b72018-09-10 09:03:22 +00001164 auto *N = C.generateNonFatalErrorNode(State);
1165 if (!N)
1166 return;
1167 reportMismatchedBug("Iterators of different containers used where the "
1168 "same container is expected.", Iter1, Iter2, C, N);
1169 }
1170}
1171
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001172void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
1173 const SVal &RetVal, const SVal &Cont) const {
1174 const auto *ContReg = Cont.getAsRegion();
1175 if (!ContReg)
1176 return;
1177
Adam Balogh42d241f2018-12-04 10:22:28 +00001178 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001179
1180 // If the container already has a begin symbol then use it. Otherwise first
1181 // create a new one.
1182 auto State = C.getState();
1183 auto BeginSym = getContainerBegin(State, ContReg);
1184 if (!BeginSym) {
Adam Balogh33160c42019-05-20 11:04:27 +00001185 State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
1186 C.getLocationContext(), C.blockCount());
1187 BeginSym = getContainerBegin(State, ContReg);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001188 }
1189 State = setIteratorPosition(State, RetVal,
1190 IteratorPosition::getPosition(ContReg, BeginSym));
1191 C.addTransition(State);
1192}
1193
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001194void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
1195 const SVal &RetVal, const SVal &Cont) const {
1196 const auto *ContReg = Cont.getAsRegion();
1197 if (!ContReg)
1198 return;
1199
Adam Balogh42d241f2018-12-04 10:22:28 +00001200 ContReg = ContReg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001201
1202 // If the container already has an end symbol then use it. Otherwise first
1203 // create a new one.
1204 auto State = C.getState();
1205 auto EndSym = getContainerEnd(State, ContReg);
1206 if (!EndSym) {
Adam Balogh33160c42019-05-20 11:04:27 +00001207 State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
1208 C.getLocationContext(), C.blockCount());
1209 EndSym = getContainerEnd(State, ContReg);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001210 }
1211 State = setIteratorPosition(State, RetVal,
1212 IteratorPosition::getPosition(ContReg, EndSym));
1213 C.addTransition(State);
1214}
1215
1216void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
1217 const SVal &RetVal,
1218 const MemRegion *Cont) const {
Adam Balogh42d241f2018-12-04 10:22:28 +00001219 Cont = Cont->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001220
1221 auto State = C.getState();
1222 auto &SymMgr = C.getSymbolManager();
1223 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
1224 C.getASTContext().LongTy, C.blockCount());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001225 State = assumeNoOverflow(State, Sym, 4);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001226 State = setIteratorPosition(State, RetVal,
1227 IteratorPosition::getPosition(Cont, Sym));
1228 C.addTransition(State);
1229}
1230
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001231void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
1232 const Expr *CE, const SVal &OldCont) const {
Adam Balogh2cfbe932018-08-28 08:41:15 +00001233 const auto *ContReg = Cont.getAsRegion();
1234 if (!ContReg)
1235 return;
1236
Adam Balogh42d241f2018-12-04 10:22:28 +00001237 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh2cfbe932018-08-28 08:41:15 +00001238
1239 // Assignment of a new value to a container always invalidates all its
1240 // iterators
1241 auto State = C.getState();
1242 const auto CData = getContainerData(State, ContReg);
1243 if (CData) {
1244 State = invalidateAllIteratorPositions(State, ContReg);
1245 }
1246
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001247 // In case of move, iterators of the old container (except the past-end
1248 // iterators) remain valid but refer to the new container
1249 if (!OldCont.isUndef()) {
1250 const auto *OldContReg = OldCont.getAsRegion();
1251 if (OldContReg) {
Adam Balogh42d241f2018-12-04 10:22:28 +00001252 OldContReg = OldContReg->getMostDerivedObjectRegion();
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001253 const auto OldCData = getContainerData(State, OldContReg);
1254 if (OldCData) {
1255 if (const auto OldEndSym = OldCData->getEnd()) {
Raphael Isemannb23ccec2018-12-10 12:37:46 +00001256 // If we already assigned an "end" symbol to the old container, then
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001257 // first reassign all iterator positions to the new container which
1258 // are not past the container (thus not greater or equal to the
1259 // current "end" symbol).
1260 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
1261 OldEndSym, BO_GE);
1262 auto &SymMgr = C.getSymbolManager();
1263 auto &SVB = C.getSValBuilder();
1264 // Then generate and assign a new "end" symbol for the new container.
1265 auto NewEndSym =
1266 SymMgr.conjureSymbol(CE, C.getLocationContext(),
1267 C.getASTContext().LongTy, C.blockCount());
1268 State = assumeNoOverflow(State, NewEndSym, 4);
1269 if (CData) {
1270 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
1271 } else {
1272 State = setContainerData(State, ContReg,
1273 ContainerData::fromEnd(NewEndSym));
1274 }
1275 // Finally, replace the old "end" symbol in the already reassigned
1276 // iterator positions with the new "end" symbol.
1277 State = rebaseSymbolInIteratorPositionsIf(
1278 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
1279 } else {
1280 // There was no "end" symbol assigned yet to the old container,
1281 // so reassign all iterator positions to the new container.
1282 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1283 }
1284 if (const auto OldBeginSym = OldCData->getBegin()) {
1285 // If we already assigned a "begin" symbol to the old container, then
1286 // assign it to the new container and remove it from the old one.
1287 if (CData) {
1288 State =
1289 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
1290 } else {
1291 State = setContainerData(State, ContReg,
1292 ContainerData::fromBegin(OldBeginSym));
1293 }
1294 State =
1295 setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
1296 }
1297 } else {
1298 // There was neither "begin" nor "end" symbol assigned yet to the old
1299 // container, so reassign all iterator positions to the new container.
1300 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
1301 }
1302 }
1303 }
Adam Balogh2cfbe932018-08-28 08:41:15 +00001304 C.addTransition(State);
1305}
1306
Adam Balogh2e7cb342018-09-10 09:07:47 +00001307void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
1308 const auto *ContReg = Cont.getAsRegion();
1309 if (!ContReg)
1310 return;
1311
Adam Balogh42d241f2018-12-04 10:22:28 +00001312 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh2e7cb342018-09-10 09:07:47 +00001313
1314 // The clear() operation invalidates all the iterators, except the past-end
1315 // iterators of list-like containers
1316 auto State = C.getState();
1317 if (!hasSubscriptOperator(State, ContReg) ||
1318 !backModifiable(State, ContReg)) {
1319 const auto CData = getContainerData(State, ContReg);
1320 if (CData) {
1321 if (const auto EndSym = CData->getEnd()) {
1322 State =
1323 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
1324 C.addTransition(State);
1325 return;
1326 }
1327 }
1328 }
1329 State = invalidateAllIteratorPositions(State, ContReg);
1330 C.addTransition(State);
1331}
1332
Adam Balogh9a48ba62018-09-10 09:06:31 +00001333void IteratorChecker::handlePushBack(CheckerContext &C,
1334 const SVal &Cont) const {
1335 const auto *ContReg = Cont.getAsRegion();
1336 if (!ContReg)
1337 return;
1338
Adam Balogh42d241f2018-12-04 10:22:28 +00001339 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001340
1341 // For deque-like containers invalidate all iterator positions
1342 auto State = C.getState();
1343 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
1344 State = invalidateAllIteratorPositions(State, ContReg);
1345 C.addTransition(State);
1346 return;
1347 }
1348
1349 const auto CData = getContainerData(State, ContReg);
1350 if (!CData)
1351 return;
1352
1353 // For vector-like containers invalidate the past-end iterator positions
1354 if (const auto EndSym = CData->getEnd()) {
1355 if (hasSubscriptOperator(State, ContReg)) {
1356 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1357 }
1358 auto &SymMgr = C.getSymbolManager();
1359 auto &BVF = SymMgr.getBasicVals();
1360 auto &SVB = C.getSValBuilder();
1361 const auto newEndSym =
1362 SVB.evalBinOp(State, BO_Add,
1363 nonloc::SymbolVal(EndSym),
1364 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1365 SymMgr.getType(EndSym)).getAsSymbol();
1366 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1367 }
1368 C.addTransition(State);
1369}
1370
1371void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
1372 const auto *ContReg = Cont.getAsRegion();
1373 if (!ContReg)
1374 return;
1375
Adam Balogh42d241f2018-12-04 10:22:28 +00001376 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001377
1378 auto State = C.getState();
1379 const auto CData = getContainerData(State, ContReg);
1380 if (!CData)
1381 return;
1382
1383 if (const auto EndSym = CData->getEnd()) {
1384 auto &SymMgr = C.getSymbolManager();
1385 auto &BVF = SymMgr.getBasicVals();
1386 auto &SVB = C.getSValBuilder();
1387 const auto BackSym =
1388 SVB.evalBinOp(State, BO_Sub,
1389 nonloc::SymbolVal(EndSym),
1390 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1391 SymMgr.getType(EndSym)).getAsSymbol();
1392 // For vector-like and deque-like containers invalidate the last and the
1393 // past-end iterator positions. For list-like containers only invalidate
1394 // the last position
1395 if (hasSubscriptOperator(State, ContReg) &&
1396 backModifiable(State, ContReg)) {
1397 State = invalidateIteratorPositions(State, BackSym, BO_GE);
1398 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
1399 } else {
1400 State = invalidateIteratorPositions(State, BackSym, BO_EQ);
1401 }
1402 auto newEndSym = BackSym;
1403 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
1404 C.addTransition(State);
1405 }
1406}
1407
1408void IteratorChecker::handlePushFront(CheckerContext &C,
1409 const SVal &Cont) const {
1410 const auto *ContReg = Cont.getAsRegion();
1411 if (!ContReg)
1412 return;
1413
Adam Balogh42d241f2018-12-04 10:22:28 +00001414 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001415
1416 // For deque-like containers invalidate all iterator positions
1417 auto State = C.getState();
1418 if (hasSubscriptOperator(State, ContReg)) {
1419 State = invalidateAllIteratorPositions(State, ContReg);
1420 C.addTransition(State);
1421 } else {
1422 const auto CData = getContainerData(State, ContReg);
1423 if (!CData)
1424 return;
1425
1426 if (const auto BeginSym = CData->getBegin()) {
1427 auto &SymMgr = C.getSymbolManager();
1428 auto &BVF = SymMgr.getBasicVals();
1429 auto &SVB = C.getSValBuilder();
1430 const auto newBeginSym =
1431 SVB.evalBinOp(State, BO_Sub,
1432 nonloc::SymbolVal(BeginSym),
1433 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1434 SymMgr.getType(BeginSym)).getAsSymbol();
1435 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1436 C.addTransition(State);
1437 }
1438 }
1439}
1440
1441void IteratorChecker::handlePopFront(CheckerContext &C,
1442 const SVal &Cont) const {
1443 const auto *ContReg = Cont.getAsRegion();
1444 if (!ContReg)
1445 return;
1446
Adam Balogh42d241f2018-12-04 10:22:28 +00001447 ContReg = ContReg->getMostDerivedObjectRegion();
Adam Balogh9a48ba62018-09-10 09:06:31 +00001448
1449 auto State = C.getState();
1450 const auto CData = getContainerData(State, ContReg);
1451 if (!CData)
1452 return;
1453
1454 // For deque-like containers invalidate all iterator positions. For list-like
1455 // iterators only invalidate the first position
1456 if (const auto BeginSym = CData->getBegin()) {
1457 if (hasSubscriptOperator(State, ContReg)) {
1458 State = invalidateIteratorPositions(State, BeginSym, BO_LE);
1459 } else {
1460 State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
1461 }
1462 auto &SymMgr = C.getSymbolManager();
1463 auto &BVF = SymMgr.getBasicVals();
1464 auto &SVB = C.getSValBuilder();
1465 const auto newBeginSym =
1466 SVB.evalBinOp(State, BO_Add,
1467 nonloc::SymbolVal(BeginSym),
1468 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1469 SymMgr.getType(BeginSym)).getAsSymbol();
1470 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
1471 C.addTransition(State);
1472 }
1473}
1474
Adam Balogh2e7cb342018-09-10 09:07:47 +00001475void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
1476 auto State = C.getState();
1477 const auto *Pos = getIteratorPosition(State, Iter);
1478 if (!Pos)
1479 return;
1480
1481 // For deque-like containers invalidate all iterator positions. For
1482 // vector-like containers invalidate iterator positions after the insertion.
1483 const auto *Cont = Pos->getContainer();
1484 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1485 if (frontModifiable(State, Cont)) {
1486 State = invalidateAllIteratorPositions(State, Cont);
1487 } else {
1488 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1489 }
1490 if (const auto *CData = getContainerData(State, Cont)) {
1491 if (const auto EndSym = CData->getEnd()) {
1492 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1493 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1494 }
1495 }
1496 C.addTransition(State);
1497 }
1498}
1499
1500void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
1501 auto State = C.getState();
1502 const auto *Pos = getIteratorPosition(State, Iter);
1503 if (!Pos)
1504 return;
1505
1506 // For deque-like containers invalidate all iterator positions. For
1507 // vector-like containers invalidate iterator positions at and after the
1508 // deletion. For list-like containers only invalidate the deleted position.
1509 const auto *Cont = Pos->getContainer();
1510 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1511 if (frontModifiable(State, Cont)) {
1512 State = invalidateAllIteratorPositions(State, Cont);
1513 } else {
1514 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
1515 }
1516 if (const auto *CData = getContainerData(State, Cont)) {
1517 if (const auto EndSym = CData->getEnd()) {
1518 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1519 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1520 }
1521 }
1522 } else {
1523 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
1524 }
1525 C.addTransition(State);
1526}
1527
1528void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
1529 const SVal &Iter2) const {
1530 auto State = C.getState();
1531 const auto *Pos1 = getIteratorPosition(State, Iter1);
1532 const auto *Pos2 = getIteratorPosition(State, Iter2);
1533 if (!Pos1 || !Pos2)
1534 return;
1535
1536 // For deque-like containers invalidate all iterator positions. For
1537 // vector-like containers invalidate iterator positions at and after the
1538 // deletion range. For list-like containers only invalidate the deleted
1539 // position range [first..last].
1540 const auto *Cont = Pos1->getContainer();
1541 if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
1542 if (frontModifiable(State, Cont)) {
1543 State = invalidateAllIteratorPositions(State, Cont);
1544 } else {
1545 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
1546 }
1547 if (const auto *CData = getContainerData(State, Cont)) {
1548 if (const auto EndSym = CData->getEnd()) {
1549 State = invalidateIteratorPositions(State, EndSym, BO_GE);
1550 State = setContainerData(State, Cont, CData->newEnd(nullptr));
1551 }
1552 }
1553 } else {
1554 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
1555 Pos2->getOffset(), BO_LT);
1556 }
1557 C.addTransition(State);
1558}
1559
1560void IteratorChecker::handleEraseAfter(CheckerContext &C,
1561 const SVal &Iter) const {
1562 auto State = C.getState();
1563 const auto *Pos = getIteratorPosition(State, Iter);
1564 if (!Pos)
1565 return;
1566
1567 // Invalidate the deleted iterator position, which is the position of the
1568 // parameter plus one.
1569 auto &SymMgr = C.getSymbolManager();
1570 auto &BVF = SymMgr.getBasicVals();
1571 auto &SVB = C.getSValBuilder();
1572 const auto NextSym =
1573 SVB.evalBinOp(State, BO_Add,
1574 nonloc::SymbolVal(Pos->getOffset()),
1575 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
1576 SymMgr.getType(Pos->getOffset())).getAsSymbol();
1577 State = invalidateIteratorPositions(State, NextSym, BO_EQ);
1578 C.addTransition(State);
1579}
1580
1581void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
1582 const SVal &Iter2) const {
1583 auto State = C.getState();
1584 const auto *Pos1 = getIteratorPosition(State, Iter1);
1585 const auto *Pos2 = getIteratorPosition(State, Iter2);
1586 if (!Pos1 || !Pos2)
1587 return;
1588
1589 // Invalidate the deleted iterator position range (first..last)
1590 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
1591 Pos2->getOffset(), BO_LT);
1592 C.addTransition(State);
1593}
1594
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001595IteratorPosition IteratorChecker::advancePosition(CheckerContext &C,
1596 OverloadedOperatorKind Op,
1597 const IteratorPosition &Pos,
1598 const SVal &Distance) const {
1599 auto State = C.getState();
1600 auto &SymMgr = C.getSymbolManager();
1601 auto &SVB = C.getSValBuilder();
1602
1603 assert ((Op == OO_Plus || Op == OO_PlusEqual ||
1604 Op == OO_Minus || Op == OO_MinusEqual) &&
1605 "Advance operator must be one of +, -, += and -=.");
1606 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
1607 if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
1608 // For concrete integers we can calculate the new position
1609 return Pos.setTo(SVB.evalBinOp(State, BinOp,
1610 nonloc::SymbolVal(Pos.getOffset()), *IntDist,
1611 SymMgr.getType(Pos.getOffset()))
1612 .getAsSymbol());
1613 } else {
1614 // For other symbols create a new symbol to keep expressions simple
1615 const auto &LCtx = C.getLocationContext();
1616 const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx,
1617 SymMgr.getType(Pos.getOffset()),
1618 C.blockCount());
1619 State = assumeNoOverflow(State, NewPosSym, 4);
1620 return Pos.setTo(NewPosSym);
1621 }
1622}
1623
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001624void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
1625 const SVal &Val, CheckerContext &C,
1626 ExplodedNode *ErrNode) const {
Artem Dergachev2f169e72019-09-09 20:34:40 +00001627 auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
1628 ErrNode);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001629 R->markInteresting(Val);
1630 C.emitReport(std::move(R));
1631}
1632
Adam Balogh21583b72018-09-10 09:03:22 +00001633void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1634 const SVal &Val1, const SVal &Val2,
1635 CheckerContext &C,
1636 ExplodedNode *ErrNode) const {
Artem Dergachev2f169e72019-09-09 20:34:40 +00001637 auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
1638 ErrNode);
Adam Balogh21583b72018-09-10 09:03:22 +00001639 R->markInteresting(Val1);
1640 R->markInteresting(Val2);
1641 C.emitReport(std::move(R));
1642}
1643
Adam Balogh3659f7a2018-09-10 09:05:31 +00001644void IteratorChecker::reportMismatchedBug(const StringRef &Message,
1645 const SVal &Val, const MemRegion *Reg,
1646 CheckerContext &C,
1647 ExplodedNode *ErrNode) const {
Artem Dergachev2f169e72019-09-09 20:34:40 +00001648 auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
1649 ErrNode);
Adam Balogh3659f7a2018-09-10 09:05:31 +00001650 R->markInteresting(Val);
1651 R->markInteresting(Reg);
1652 C.emitReport(std::move(R));
1653}
1654
Adam Balogh2cfbe932018-08-28 08:41:15 +00001655void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
1656 const SVal &Val, CheckerContext &C,
1657 ExplodedNode *ErrNode) const {
Artem Dergachev2f169e72019-09-09 20:34:40 +00001658 auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType,
1659 Message, ErrNode);
Adam Balogh2cfbe932018-08-28 08:41:15 +00001660 R->markInteresting(Val);
1661 C.emitReport(std::move(R));
1662}
1663
Adam Balogh0f88cae2019-11-08 08:56:31 +01001664bool IteratorChecker::evalCall(const CallEvent &Call,
1665 CheckerContext &C) const {
1666 if (!ChecksEnabled[CK_DebugIteratorModeling])
1667 return false;
1668
1669 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1670 if (!CE)
1671 return false;
1672
1673 const FnCheck *Handler = Callbacks.lookup(Call);
1674 if (!Handler)
1675 return false;
1676
1677 (this->**Handler)(CE, C);
1678 return true;
1679}
1680
1681template <typename Getter>
1682void IteratorChecker::analyzerContainerDataField(const CallExpr *CE,
1683 CheckerContext &C,
1684 Getter get) const {
1685 if (CE->getNumArgs() == 0) {
1686 reportDebugMsg("Missing container argument", C);
1687 return;
1688 }
1689
1690 auto State = C.getState();
1691 const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
1692 if (Cont) {
1693 const auto *Data = getContainerData(State, Cont);
1694 if (Data) {
1695 SymbolRef Field = get(Data);
1696 if (Field) {
1697 State = State->BindExpr(CE, C.getLocationContext(),
1698 nonloc::SymbolVal(Field));
1699 C.addTransition(State);
1700 return;
1701 }
1702 }
1703 }
1704
1705 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1706 State = State->BindExpr(CE, C.getLocationContext(),
1707 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1708}
1709
1710void IteratorChecker::analyzerContainerBegin(const CallExpr *CE,
1711 CheckerContext &C) const {
1712 analyzerContainerDataField(CE, C, [](const ContainerData *D) {
1713 return D->getBegin();
1714 });
1715}
1716
1717void IteratorChecker::analyzerContainerEnd(const CallExpr *CE,
1718 CheckerContext &C) const {
1719 analyzerContainerDataField(CE, C, [](const ContainerData *D) {
1720 return D->getEnd();
1721 });
1722}
1723
1724template <typename Getter>
1725void IteratorChecker::analyzerIteratorDataField(const CallExpr *CE,
1726 CheckerContext &C,
1727 Getter get,
1728 SVal Default) const {
1729 if (CE->getNumArgs() == 0) {
1730 reportDebugMsg("Missing iterator argument", C);
1731 return;
1732 }
1733
1734 auto State = C.getState();
1735 SVal V = C.getSVal(CE->getArg(0));
1736 const auto *Pos = getIteratorPosition(State, V);
1737 if (Pos) {
1738 State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
1739 } else {
1740 State = State->BindExpr(CE, C.getLocationContext(), Default);
1741 }
1742 C.addTransition(State);
1743}
1744
1745void IteratorChecker::analyzerIteratorPosition(const CallExpr *CE,
1746 CheckerContext &C) const {
1747 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1748 analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
1749 return nonloc::SymbolVal(P->getOffset());
1750 }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1751}
1752
1753void IteratorChecker::analyzerIteratorContainer(const CallExpr *CE,
1754 CheckerContext &C) const {
1755 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1756 analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
1757 return loc::MemRegionVal(P->getContainer());
1758 }, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1759}
1760
1761void IteratorChecker::analyzerIteratorValidity(const CallExpr *CE,
1762 CheckerContext &C) const {
1763 auto &BVF = C.getSValBuilder().getBasicValueFactory();
1764 analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
1765 return
1766 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
1767 }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1768}
1769
1770ExplodedNode *IteratorChecker::reportDebugMsg(llvm::StringRef Msg,
1771 CheckerContext &C) const {
1772 ExplodedNode *N = C.generateNonFatalErrorNode();
1773 if (!N)
1774 return nullptr;
1775
1776 auto &BR = C.getBugReporter();
1777 BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
1778 Msg, N));
1779 return N;
1780}
1781
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001782namespace {
1783
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001784bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Adam Baloghd5bd3f62018-12-04 10:27:27 +00001785bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
1786bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001787bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1788 BinaryOperator::Opcode Opc);
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001789bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1790 BinaryOperator::Opcode Opc);
Adam Balogh9a48ba62018-09-10 09:06:31 +00001791const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
1792 const MemRegion *Reg);
Adam Balogh6b23b1a2018-09-10 09:04:27 +00001793SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
1794 SymbolRef OldSym, SymbolRef NewSym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001795
1796bool isIteratorType(const QualType &Type) {
1797 if (Type->isPointerType())
1798 return true;
1799
1800 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
1801 return isIterator(CRD);
1802}
1803
1804bool isIterator(const CXXRecordDecl *CRD) {
1805 if (!CRD)
1806 return false;
1807
1808 const auto Name = CRD->getName();
1809 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
1810 Name.endswith_lower("it")))
1811 return false;
1812
1813 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
1814 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
1815 for (const auto *Method : CRD->methods()) {
1816 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
1817 if (Ctor->isCopyConstructor()) {
1818 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
1819 }
1820 continue;
1821 }
1822 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
1823 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
1824 continue;
1825 }
1826 if (Method->isCopyAssignmentOperator()) {
1827 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
1828 continue;
1829 }
1830 if (!Method->isOverloadedOperator())
1831 continue;
1832 const auto OPK = Method->getOverloadedOperator();
1833 if (OPK == OO_PlusPlus) {
1834 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
1835 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
1836 continue;
1837 }
1838 if (OPK == OO_Star) {
1839 HasDerefOp = (Method->getNumParams() == 0);
1840 continue;
1841 }
1842 }
1843
1844 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
1845 HasPostIncrOp && HasDerefOp;
1846}
1847
Adam Balogh3659f7a2018-09-10 09:05:31 +00001848bool isComparisonOperator(OverloadedOperatorKind OK) {
1849 return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
1850 OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
1851}
1852
Adam Baloghb03ed5e2018-06-28 10:58:53 +00001853bool isBeginCall(const FunctionDecl *Func) {
1854 const auto *IdInfo = Func->getIdentifier();
1855 if (!IdInfo)
1856 return false;
1857 return IdInfo->getName().endswith_lower("begin");
1858}
1859
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001860bool isEndCall(const FunctionDecl *Func) {
1861 const auto *IdInfo = Func->getIdentifier();
1862 if (!IdInfo)
1863 return false;
1864 return IdInfo->getName().endswith_lower("end");
1865}
1866
Adam Balogh2e7cb342018-09-10 09:07:47 +00001867bool isAssignCall(const FunctionDecl *Func) {
1868 const auto *IdInfo = Func->getIdentifier();
1869 if (!IdInfo)
1870 return false;
1871 if (Func->getNumParams() > 2)
1872 return false;
1873 return IdInfo->getName() == "assign";
1874}
1875
1876bool isClearCall(const FunctionDecl *Func) {
1877 const auto *IdInfo = Func->getIdentifier();
1878 if (!IdInfo)
1879 return false;
1880 if (Func->getNumParams() > 0)
1881 return false;
1882 return IdInfo->getName() == "clear";
1883}
1884
Adam Balogh9a48ba62018-09-10 09:06:31 +00001885bool isPushBackCall(const FunctionDecl *Func) {
1886 const auto *IdInfo = Func->getIdentifier();
1887 if (!IdInfo)
1888 return false;
1889 if (Func->getNumParams() != 1)
1890 return false;
1891 return IdInfo->getName() == "push_back";
1892}
1893
1894bool isEmplaceBackCall(const FunctionDecl *Func) {
1895 const auto *IdInfo = Func->getIdentifier();
1896 if (!IdInfo)
1897 return false;
1898 if (Func->getNumParams() < 1)
1899 return false;
1900 return IdInfo->getName() == "emplace_back";
1901}
1902
1903bool isPopBackCall(const FunctionDecl *Func) {
1904 const auto *IdInfo = Func->getIdentifier();
1905 if (!IdInfo)
1906 return false;
1907 if (Func->getNumParams() > 0)
1908 return false;
1909 return IdInfo->getName() == "pop_back";
1910}
1911
1912bool isPushFrontCall(const FunctionDecl *Func) {
1913 const auto *IdInfo = Func->getIdentifier();
1914 if (!IdInfo)
1915 return false;
1916 if (Func->getNumParams() != 1)
1917 return false;
1918 return IdInfo->getName() == "push_front";
1919}
1920
1921bool isEmplaceFrontCall(const FunctionDecl *Func) {
1922 const auto *IdInfo = Func->getIdentifier();
1923 if (!IdInfo)
1924 return false;
1925 if (Func->getNumParams() < 1)
1926 return false;
1927 return IdInfo->getName() == "emplace_front";
1928}
1929
1930bool isPopFrontCall(const FunctionDecl *Func) {
1931 const auto *IdInfo = Func->getIdentifier();
1932 if (!IdInfo)
1933 return false;
1934 if (Func->getNumParams() > 0)
1935 return false;
1936 return IdInfo->getName() == "pop_front";
1937}
1938
Adam Balogh2e7cb342018-09-10 09:07:47 +00001939bool isInsertCall(const FunctionDecl *Func) {
1940 const auto *IdInfo = Func->getIdentifier();
1941 if (!IdInfo)
1942 return false;
1943 if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
1944 return false;
1945 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1946 return false;
1947 return IdInfo->getName() == "insert";
1948}
1949
1950bool isEmplaceCall(const FunctionDecl *Func) {
1951 const auto *IdInfo = Func->getIdentifier();
1952 if (!IdInfo)
1953 return false;
1954 if (Func->getNumParams() < 2)
1955 return false;
1956 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1957 return false;
1958 return IdInfo->getName() == "emplace";
1959}
1960
1961bool isEraseCall(const FunctionDecl *Func) {
1962 const auto *IdInfo = Func->getIdentifier();
1963 if (!IdInfo)
1964 return false;
1965 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1966 return false;
1967 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1968 return false;
1969 if (Func->getNumParams() == 2 &&
1970 !isIteratorType(Func->getParamDecl(1)->getType()))
1971 return false;
1972 return IdInfo->getName() == "erase";
1973}
1974
1975bool isEraseAfterCall(const FunctionDecl *Func) {
1976 const auto *IdInfo = Func->getIdentifier();
1977 if (!IdInfo)
1978 return false;
1979 if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
1980 return false;
1981 if (!isIteratorType(Func->getParamDecl(0)->getType()))
1982 return false;
1983 if (Func->getNumParams() == 2 &&
1984 !isIteratorType(Func->getParamDecl(1)->getType()))
1985 return false;
1986 return IdInfo->getName() == "erase_after";
1987}
1988
Adam Balogh2cfbe932018-08-28 08:41:15 +00001989bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
1990
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001991bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
1992 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
1993}
1994
Adam Balogh2cfbe932018-08-28 08:41:15 +00001995bool isAccessOperator(OverloadedOperatorKind OK) {
1996 return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
1997 isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
1998}
1999
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002000bool isDereferenceOperator(OverloadedOperatorKind OK) {
2001 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
2002 OK == OO_Subscript;
2003}
2004
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002005bool isIncrementOperator(OverloadedOperatorKind OK) {
2006 return OK == OO_PlusPlus;
2007}
2008
2009bool isDecrementOperator(OverloadedOperatorKind OK) {
2010 return OK == OO_MinusMinus;
2011}
2012
2013bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
2014 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
2015 OK == OO_MinusEqual;
2016}
2017
Adam Balogh9a48ba62018-09-10 09:06:31 +00002018bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
2019 const auto *CRD = getCXXRecordDecl(State, Reg);
2020 if (!CRD)
2021 return false;
2022
2023 for (const auto *Method : CRD->methods()) {
2024 if (!Method->isOverloadedOperator())
2025 continue;
2026 const auto OPK = Method->getOverloadedOperator();
2027 if (OPK == OO_Subscript) {
2028 return true;
2029 }
2030 }
2031 return false;
2032}
2033
2034bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
2035 const auto *CRD = getCXXRecordDecl(State, Reg);
2036 if (!CRD)
2037 return false;
2038
2039 for (const auto *Method : CRD->methods()) {
2040 if (!Method->getDeclName().isIdentifier())
2041 continue;
2042 if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
2043 return true;
2044 }
2045 }
2046 return false;
2047}
2048
2049bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
2050 const auto *CRD = getCXXRecordDecl(State, Reg);
2051 if (!CRD)
2052 return false;
2053
2054 for (const auto *Method : CRD->methods()) {
2055 if (!Method->getDeclName().isIdentifier())
2056 continue;
2057 if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
2058 return true;
2059 }
2060 }
2061 return false;
2062}
2063
2064const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
2065 const MemRegion *Reg) {
2066 auto TI = getDynamicTypeInfo(State, Reg);
2067 if (!TI.isValid())
2068 return nullptr;
2069
2070 auto Type = TI.getType();
2071 if (const auto *RefT = Type->getAs<ReferenceType>()) {
2072 Type = RefT->getPointeeType();
2073 }
2074
2075 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
2076}
2077
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002078SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
2079 const auto *CDataPtr = getContainerData(State, Cont);
2080 if (!CDataPtr)
2081 return nullptr;
2082
2083 return CDataPtr->getBegin();
2084}
2085
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002086SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
2087 const auto *CDataPtr = getContainerData(State, Cont);
2088 if (!CDataPtr)
2089 return nullptr;
2090
2091 return CDataPtr->getEnd();
2092}
2093
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002094ProgramStateRef createContainerBegin(ProgramStateRef State,
Adam Balogh33160c42019-05-20 11:04:27 +00002095 const MemRegion *Cont, const Expr *E,
2096 QualType T, const LocationContext *LCtx,
2097 unsigned BlockCount) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002098 // Only create if it does not exist
2099 const auto *CDataPtr = getContainerData(State, Cont);
Adam Balogh33160c42019-05-20 11:04:27 +00002100 if (CDataPtr && CDataPtr->getBegin())
2101 return State;
2102
2103 auto &SymMgr = State->getSymbolManager();
2104 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
2105 "begin");
2106 State = assumeNoOverflow(State, Sym, 4);
2107
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002108 if (CDataPtr) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002109 const auto CData = CDataPtr->newBegin(Sym);
2110 return setContainerData(State, Cont, CData);
2111 }
Adam Balogh33160c42019-05-20 11:04:27 +00002112
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002113 const auto CData = ContainerData::fromBegin(Sym);
2114 return setContainerData(State, Cont, CData);
2115}
2116
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002117ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
Adam Balogh33160c42019-05-20 11:04:27 +00002118 const Expr *E, QualType T,
2119 const LocationContext *LCtx,
2120 unsigned BlockCount) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002121 // Only create if it does not exist
2122 const auto *CDataPtr = getContainerData(State, Cont);
Adam Balogh33160c42019-05-20 11:04:27 +00002123 if (CDataPtr && CDataPtr->getEnd())
2124 return State;
2125
2126 auto &SymMgr = State->getSymbolManager();
2127 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
2128 "end");
2129 State = assumeNoOverflow(State, Sym, 4);
2130
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002131 if (CDataPtr) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002132 const auto CData = CDataPtr->newEnd(Sym);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002133 return setContainerData(State, Cont, CData);
2134 }
Adam Balogh33160c42019-05-20 11:04:27 +00002135
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002136 const auto CData = ContainerData::fromEnd(Sym);
2137 return setContainerData(State, Cont, CData);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002138}
2139
2140const ContainerData *getContainerData(ProgramStateRef State,
2141 const MemRegion *Cont) {
2142 return State->get<ContainerMap>(Cont);
2143}
2144
2145ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
2146 const ContainerData &CData) {
2147 return State->set<ContainerMap>(Cont, CData);
2148}
2149
2150const IteratorPosition *getIteratorPosition(ProgramStateRef State,
2151 const SVal &Val) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002152 if (auto Reg = Val.getAsRegion()) {
2153 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002154 return State->get<IteratorRegionMap>(Reg);
2155 } else if (const auto Sym = Val.getAsSymbol()) {
2156 return State->get<IteratorSymbolMap>(Sym);
2157 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2158 return State->get<IteratorRegionMap>(LCVal->getRegion());
2159 }
2160 return nullptr;
2161}
2162
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002163ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
2164 const IteratorPosition &Pos) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002165 if (auto Reg = Val.getAsRegion()) {
2166 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002167 return State->set<IteratorRegionMap>(Reg, Pos);
2168 } else if (const auto Sym = Val.getAsSymbol()) {
2169 return State->set<IteratorSymbolMap>(Sym, Pos);
2170 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2171 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
2172 }
2173 return nullptr;
2174}
2175
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002176ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
Adam Balogh42d241f2018-12-04 10:22:28 +00002177 if (auto Reg = Val.getAsRegion()) {
2178 Reg = Reg->getMostDerivedObjectRegion();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002179 return State->remove<IteratorRegionMap>(Reg);
2180 } else if (const auto Sym = Val.getAsSymbol()) {
2181 return State->remove<IteratorSymbolMap>(Sym);
2182 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
2183 return State->remove<IteratorRegionMap>(LCVal->getRegion());
2184 }
2185 return nullptr;
2186}
2187
Adam Balogh54976e72019-04-23 07:15:55 +00002188ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
2189 SymbolRef Sym2, bool Equal) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002190 auto &SVB = State->getStateManager().getSValBuilder();
Adam Balogh0a7592b2018-07-16 09:27:27 +00002191
2192 // FIXME: This code should be reworked as follows:
2193 // 1. Subtract the operands using evalBinOp().
2194 // 2. Assume that the result doesn't overflow.
2195 // 3. Compare the result to 0.
2196 // 4. Assume the result of the comparison.
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002197 const auto comparison =
Adam Balogh54976e72019-04-23 07:15:55 +00002198 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
2199 nonloc::SymbolVal(Sym2), SVB.getConditionType());
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002200
Adam Balogh0a7592b2018-07-16 09:27:27 +00002201 assert(comparison.getAs<DefinedSVal>() &&
2202 "Symbol comparison must be a `DefinedSVal`");
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002203
Adam Balogh0a7592b2018-07-16 09:27:27 +00002204 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
Adam Balogh54976e72019-04-23 07:15:55 +00002205 if (!NewState)
2206 return nullptr;
2207
Adam Balogh0a7592b2018-07-16 09:27:27 +00002208 if (const auto CompSym = comparison.getAsSymbol()) {
2209 assert(isa<SymIntExpr>(CompSym) &&
2210 "Symbol comparison must be a `SymIntExpr`");
2211 assert(BinaryOperator::isComparisonOp(
2212 cast<SymIntExpr>(CompSym)->getOpcode()) &&
2213 "Symbol comparison must be a comparison");
2214 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002215 }
2216
Adam Balogh0a7592b2018-07-16 09:27:27 +00002217 return NewState;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002218}
2219
Adam Balogha6921202018-07-30 08:52:21 +00002220bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
2221 auto RegionMap = State->get<IteratorRegionMap>();
2222 for (const auto Reg : RegionMap) {
2223 if (Reg.second.getContainer() == Cont)
2224 return true;
2225 }
2226
2227 auto SymbolMap = State->get<IteratorSymbolMap>();
2228 for (const auto Sym : SymbolMap) {
2229 if (Sym.second.getContainer() == Cont)
2230 return true;
2231 }
2232
2233 return false;
2234}
2235
Adam Balogh21583b72018-09-10 09:03:22 +00002236bool isBoundThroughLazyCompoundVal(const Environment &Env,
2237 const MemRegion *Reg) {
2238 for (const auto Binding: Env) {
2239 if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
2240 if (LCVal->getRegion() == Reg)
2241 return true;
2242 }
2243 }
2244
2245 return false;
2246}
2247
Adam Balogh54976e72019-04-23 07:15:55 +00002248// This function tells the analyzer's engine that symbols produced by our
2249// checker, most notably iterator positions, are relatively small.
2250// A distance between items in the container should not be very large.
2251// By assuming that it is within around 1/8 of the address space,
2252// we can help the analyzer perform operations on these symbols
2253// without being afraid of integer overflows.
2254// FIXME: Should we provide it as an API, so that all checkers could use it?
2255ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
2256 long Scale) {
2257 SValBuilder &SVB = State->getStateManager().getSValBuilder();
2258 BasicValueFactory &BV = SVB.getBasicValueFactory();
2259
2260 QualType T = Sym->getType();
2261 assert(T->isSignedIntegerOrEnumerationType());
2262 APSIntType AT = BV.getAPSIntType(T);
2263
2264 ProgramStateRef NewState = State;
2265
2266 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
2267 SVal IsCappedFromAbove =
2268 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
2269 nonloc::ConcreteInt(Max), SVB.getConditionType());
2270 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
2271 NewState = NewState->assume(*DV, true);
2272 if (!NewState)
2273 return State;
2274 }
2275
2276 llvm::APSInt Min = -Max;
2277 SVal IsCappedFromBelow =
2278 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
2279 nonloc::ConcreteInt(Min), SVB.getConditionType());
2280 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
2281 NewState = NewState->assume(*DV, true);
2282 if (!NewState)
2283 return State;
2284 }
2285
2286 return NewState;
2287}
2288
Adam Balogh2cfbe932018-08-28 08:41:15 +00002289template <typename Condition, typename Process>
2290ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
2291 Process Proc) {
2292 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
2293 auto RegionMap = State->get<IteratorRegionMap>();
2294 bool Changed = false;
2295 for (const auto Reg : RegionMap) {
2296 if (Cond(Reg.second)) {
2297 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
2298 Changed = true;
2299 }
2300 }
2301
2302 if (Changed)
2303 State = State->set<IteratorRegionMap>(RegionMap);
2304
2305 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
2306 auto SymbolMap = State->get<IteratorSymbolMap>();
2307 Changed = false;
2308 for (const auto Sym : SymbolMap) {
2309 if (Cond(Sym.second)) {
2310 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
2311 Changed = true;
2312 }
2313 }
2314
2315 if (Changed)
2316 State = State->set<IteratorSymbolMap>(SymbolMap);
2317
2318 return State;
2319}
2320
2321ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
2322 const MemRegion *Cont) {
2323 auto MatchCont = [&](const IteratorPosition &Pos) {
2324 return Pos.getContainer() == Cont;
2325 };
2326 auto Invalidate = [&](const IteratorPosition &Pos) {
2327 return Pos.invalidate();
2328 };
2329 return processIteratorPositions(State, MatchCont, Invalidate);
2330}
2331
Adam Balogh2e7cb342018-09-10 09:07:47 +00002332ProgramStateRef
2333invalidateAllIteratorPositionsExcept(ProgramStateRef State,
2334 const MemRegion *Cont, SymbolRef Offset,
2335 BinaryOperator::Opcode Opc) {
2336 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2337 return Pos.getContainer() == Cont &&
2338 !compare(State, Pos.getOffset(), Offset, Opc);
2339 };
2340 auto Invalidate = [&](const IteratorPosition &Pos) {
2341 return Pos.invalidate();
2342 };
2343 return processIteratorPositions(State, MatchContAndCompare, Invalidate);
2344}
2345
Adam Balogh9a48ba62018-09-10 09:06:31 +00002346ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2347 SymbolRef Offset,
2348 BinaryOperator::Opcode Opc) {
2349 auto Compare = [&](const IteratorPosition &Pos) {
2350 return compare(State, Pos.getOffset(), Offset, Opc);
2351 };
2352 auto Invalidate = [&](const IteratorPosition &Pos) {
2353 return Pos.invalidate();
2354 };
2355 return processIteratorPositions(State, Compare, Invalidate);
2356}
2357
Adam Balogh2e7cb342018-09-10 09:07:47 +00002358ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
2359 SymbolRef Offset1,
2360 BinaryOperator::Opcode Opc1,
2361 SymbolRef Offset2,
2362 BinaryOperator::Opcode Opc2) {
2363 auto Compare = [&](const IteratorPosition &Pos) {
2364 return compare(State, Pos.getOffset(), Offset1, Opc1) &&
2365 compare(State, Pos.getOffset(), Offset2, Opc2);
2366 };
2367 auto Invalidate = [&](const IteratorPosition &Pos) {
2368 return Pos.invalidate();
2369 };
2370 return processIteratorPositions(State, Compare, Invalidate);
2371}
2372
Adam Balogh6b23b1a2018-09-10 09:04:27 +00002373ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
2374 const MemRegion *Cont,
2375 const MemRegion *NewCont) {
2376 auto MatchCont = [&](const IteratorPosition &Pos) {
2377 return Pos.getContainer() == Cont;
2378 };
2379 auto ReAssign = [&](const IteratorPosition &Pos) {
2380 return Pos.reAssign(NewCont);
2381 };
2382 return processIteratorPositions(State, MatchCont, ReAssign);
2383}
2384
2385ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
2386 const MemRegion *Cont,
2387 const MemRegion *NewCont,
2388 SymbolRef Offset,
2389 BinaryOperator::Opcode Opc) {
2390 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
2391 return Pos.getContainer() == Cont &&
2392 !compare(State, Pos.getOffset(), Offset, Opc);
2393 };
2394 auto ReAssign = [&](const IteratorPosition &Pos) {
2395 return Pos.reAssign(NewCont);
2396 };
2397 return processIteratorPositions(State, MatchContAndCompare, ReAssign);
2398}
2399
2400// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
2401// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
2402// position offsets where `CondSym` is true.
2403ProgramStateRef rebaseSymbolInIteratorPositionsIf(
2404 ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
2405 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
2406 auto LessThanEnd = [&](const IteratorPosition &Pos) {
2407 return compare(State, Pos.getOffset(), CondSym, Opc);
2408 };
2409 auto RebaseSymbol = [&](const IteratorPosition &Pos) {
2410 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
2411 NewSym));
2412 };
2413 return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
2414}
2415
2416// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
2417// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
2418// `OrigExpr`.
2419SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
2420 SymbolRef OrigExpr, SymbolRef OldExpr,
2421 SymbolRef NewSym) {
2422 auto &SymMgr = SVB.getSymbolManager();
2423 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
2424 nonloc::SymbolVal(OldExpr),
2425 SymMgr.getType(OrigExpr));
2426
2427 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
2428 if (!DiffInt)
2429 return OrigExpr;
2430
2431 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
2432 SymMgr.getType(OrigExpr)).getAsSymbol();
2433}
2434
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002435bool isZero(ProgramStateRef State, const NonLoc &Val) {
2436 auto &BVF = State->getBasicVals();
2437 return compare(State, Val,
2438 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
2439 BO_EQ);
2440}
2441
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002442bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002443 const auto *Cont = Pos.getContainer();
2444 const auto *CData = getContainerData(State, Cont);
2445 if (!CData)
2446 return false;
2447
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002448 const auto End = CData->getEnd();
2449 if (End) {
2450 if (isEqual(State, Pos.getOffset(), End)) {
2451 return true;
2452 }
2453 }
2454
2455 return false;
2456}
2457
2458bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
2459 const auto *Cont = Pos.getContainer();
2460 const auto *CData = getContainerData(State, Cont);
2461 if (!CData)
2462 return false;
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002463
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002464 const auto Beg = CData->getBegin();
2465 if (Beg) {
2466 if (isLess(State, Pos.getOffset(), Beg)) {
2467 return true;
2468 }
2469 }
2470
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002471 return false;
2472}
2473
2474bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
2475 const auto *Cont = Pos.getContainer();
2476 const auto *CData = getContainerData(State, Cont);
2477 if (!CData)
2478 return false;
2479
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002480 const auto End = CData->getEnd();
2481 if (End) {
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002482 if (isGreater(State, Pos.getOffset(), End)) {
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002483 return true;
2484 }
2485 }
2486
2487 return false;
2488}
2489
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002490bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2491 return compare(State, Sym1, Sym2, BO_LT);
2492}
2493
Adam Baloghd5bd3f62018-12-04 10:27:27 +00002494bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2495 return compare(State, Sym1, Sym2, BO_GT);
2496}
2497
2498bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
2499 return compare(State, Sym1, Sym2, BO_EQ);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002500}
2501
2502bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
2503 BinaryOperator::Opcode Opc) {
Adam Baloghb03ed5e2018-06-28 10:58:53 +00002504 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
2505}
2506
2507bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
2508 BinaryOperator::Opcode Opc) {
2509 auto &SVB = State->getStateManager().getSValBuilder();
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002510
2511 const auto comparison =
Adam Balogh0a7592b2018-07-16 09:27:27 +00002512 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002513
Adam Balogh0a7592b2018-07-16 09:27:27 +00002514 assert(comparison.getAs<DefinedSVal>() &&
2515 "Symbol comparison must be a `DefinedSVal`");
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002516
Adam Balogh0a7592b2018-07-16 09:27:27 +00002517 return !State->assume(comparison.castAs<DefinedSVal>(), false);
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002518}
2519
2520} // namespace
2521
Kristof Umann8fd74eb2019-01-26 20:06:54 +00002522void ento::registerIteratorModeling(CheckerManager &mgr) {
2523 mgr.registerChecker<IteratorChecker>();
2524}
2525
2526bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
2527 return true;
2528}
2529
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002530#define REGISTER_CHECKER(name) \
2531 void ento::register##name(CheckerManager &Mgr) { \
Kristof Umann204bf2b2019-01-26 21:41:50 +00002532 auto *checker = Mgr.getChecker<IteratorChecker>(); \
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002533 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
2534 checker->CheckNames[IteratorChecker::CK_##name] = \
Kristof Umann72649422019-09-12 19:09:24 +00002535 Mgr.getCurrentCheckerName(); \
Kristof Umann058a7a42019-01-26 14:23:08 +00002536 } \
2537 \
Kristof Umann72649422019-09-12 19:09:24 +00002538 bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
Artem Dergachev8fa639e2017-05-29 15:03:20 +00002539
2540REGISTER_CHECKER(IteratorRangeChecker)
Adam Balogh21583b72018-09-10 09:03:22 +00002541REGISTER_CHECKER(MismatchedIteratorChecker)
Adam Balogh2cfbe932018-08-28 08:41:15 +00002542REGISTER_CHECKER(InvalidatedIteratorChecker)
Adam Balogh0f88cae2019-11-08 08:56:31 +01002543REGISTER_CHECKER(DebugIteratorModeling)