blob: 11f42229f53d76d547e0e9572a3d4502815978fd [file] [log] [blame]
Artem Dergachev8fa639e2017-05-29 15:03:20 +00001//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Defines a checker for using iterators outside their range (past end). Usage
11// means here dereferencing, incrementing etc.
12//
13//===----------------------------------------------------------------------===//
14//
15// In the code, iterator can be represented as a:
16// * type-I: typedef-ed pointer. Operations over such iterator, such as
17// comparisons or increments, are modeled straightforwardly by the
18// analyzer.
19// * type-II: structure with its method bodies available. Operations over such
20// iterator are inlined by the analyzer, and results of modeling
21// these operations are exposing implementation details of the
22// iterators, which is not necessarily helping.
23// * type-III: completely opaque structure. Operations over such iterator are
24// modeled conservatively, producing conjured symbols everywhere.
25//
26// To handle all these types in a common way we introduce a structure called
27// IteratorPosition which is an abstraction of the position the iterator
28// represents using symbolic expressions. The checker handles all the
29// operations on this structure.
30//
31// Additionally, depending on the circumstances, operators of types II and III
32// can be represented as:
33// * type-IIa, type-IIIa: conjured structure symbols - when returned by value
34// from conservatively evaluated methods such as
35// `.begin()`.
36// * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
37// variables or temporaries, when the iterator object is
38// currently treated as an lvalue.
39// * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
40// iterator object is treated as an rvalue taken of a
41// particular lvalue, eg. a copy of "type-a" iterator
42// object, or an iterator that existed before the
43// analysis has started.
44//
45// To handle any of these three different representations stored in an SVal we
46// use setter and getters functions which separate the three cases. To store
47// them we use a pointer union of symbol and memory region.
48//
49// The checker works the following way: We record the past-end iterator for
50// all containers whenever their `.end()` is called. Since the Constraint
51// Manager cannot handle SVals we need to take over its role. We post-check
52// equality and non-equality comparisons and propagate the position of the
53// iterator to the other side of the comparison if it is past-end and we are in
54// the 'equal' branch (true-branch for `==` and false-branch for `!=`).
55//
56// In case of type-I or type-II iterators we get a concrete integer as a result
57// of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
58// this latter case we record the symbol and reload it in evalAssume() and do
59// the propagation there. We also handle (maybe double) negated comparisons
60// which are represented in the form of (x == 0 or x !=0 ) where x is the
61// comparison itself.
62
63#include "ClangSACheckers.h"
64#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
65#include "clang/StaticAnalyzer/Core/Checker.h"
66#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
67#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
68
69using namespace clang;
70using namespace ento;
71
72namespace {
73
74// Abstract position of an iterator. This helps to handle all three kinds
75// of operators in a common way by using a symbolic position.
76struct IteratorPosition {
77private:
78
79 // Container the iterator belongs to
80 const MemRegion *Cont;
81
82 // Abstract offset
83 SymbolRef Offset;
84
85 IteratorPosition(const MemRegion *C, SymbolRef Of)
86 : Cont(C), Offset(Of) {}
87
88public:
89 const MemRegion *getContainer() const { return Cont; }
90 SymbolRef getOffset() const { return Offset; }
91
92 static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
93 return IteratorPosition(C, Of);
94 }
95
96 IteratorPosition setTo(SymbolRef NewOf) const {
97 return IteratorPosition(Cont, NewOf);
98 }
99
100 bool operator==(const IteratorPosition &X) const {
101 return Cont == X.Cont && Offset == X.Offset;
102 }
103
104 bool operator!=(const IteratorPosition &X) const {
105 return Cont != X.Cont || Offset != X.Offset;
106 }
107
108 void Profile(llvm::FoldingSetNodeID &ID) const {
109 ID.AddPointer(Cont);
110 ID.Add(Offset);
111 }
112};
113
114typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
115
116// Structure to record the symbolic end position of a container
117struct ContainerData {
118private:
119 SymbolRef End;
120
121 ContainerData(SymbolRef E) : End(E) {}
122
123public:
124 static ContainerData fromEnd(SymbolRef E) {
125 return ContainerData(E);
126 }
127
128 SymbolRef getEnd() const { return End; }
129
130 ContainerData newEnd(SymbolRef E) const { return ContainerData(E); }
131
132 bool operator==(const ContainerData &X) const {
133 return End == X.End;
134 }
135
136 bool operator!=(const ContainerData &X) const {
137 return End != X.End;
138 }
139
140 void Profile(llvm::FoldingSetNodeID &ID) const {
141 ID.Add(End);
142 }
143};
144
145// Structure fo recording iterator comparisons. We needed to retrieve the
146// original comparison expression in assumptions.
147struct IteratorComparison {
148private:
149 RegionOrSymbol Left, Right;
150 bool Equality;
151
152public:
153 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
154 : Left(L), Right(R), Equality(Eq) {}
155
156 RegionOrSymbol getLeft() const { return Left; }
157 RegionOrSymbol getRight() const { return Right; }
158 bool isEquality() const { return Equality; }
159 bool operator==(const IteratorComparison &X) const {
160 return Left == X.Left && Right == X.Right && Equality == X.Equality;
161 }
162 bool operator!=(const IteratorComparison &X) const {
163 return Left != X.Left || Right != X.Right || Equality != X.Equality;
164 }
165 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
166};
167
168class IteratorChecker
169 : public Checker<check::PreCall, check::PostCall,
170 check::PostStmt<MaterializeTemporaryExpr>,
171 check::DeadSymbols,
172 eval::Assume> {
173
174 std::unique_ptr<BugType> OutOfRangeBugType;
175
176 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
177 const SVal &RVal, OverloadedOperatorKind Op) const;
178 void verifyDereference(CheckerContext &C, const SVal &Val) const;
179 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
180 const SVal &Cont) const;
181 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
182 const MemRegion *Cont) const;
183 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
184 CheckerContext &C, ExplodedNode *ErrNode) const;
185
186public:
187 IteratorChecker();
188
189 enum CheckKind {
190 CK_IteratorRangeChecker,
191 CK_NumCheckKinds
192 };
193
194 DefaultBool ChecksEnabled[CK_NumCheckKinds];
195 CheckName CheckNames[CK_NumCheckKinds];
196
197 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
198 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
199 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
200 CheckerContext &C) const;
201 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
202 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
203 bool Assumption) const;
204};
205} // namespace
206
207REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
208REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
209 IteratorPosition)
210
211REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
212
213REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
214 IteratorComparison)
215
216namespace {
217
218bool isIteratorType(const QualType &Type);
219bool isIterator(const CXXRecordDecl *CRD);
220bool isEndCall(const FunctionDecl *Func);
221bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
222bool isDereferenceOperator(OverloadedOperatorKind OK);
223BinaryOperator::Opcode getOpcode(const SymExpr *SE);
224const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
225const ProgramStateRef processComparison(ProgramStateRef State,
226 RegionOrSymbol LVal,
227 RegionOrSymbol RVal, bool Equal);
228const ProgramStateRef saveComparison(ProgramStateRef State,
229 const SymExpr *Condition, const SVal &LVal,
230 const SVal &RVal, bool Eq);
231const IteratorComparison *loadComparison(ProgramStateRef State,
232 const SymExpr *Condition);
233SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
234ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
235 const SymbolRef Sym);
236const IteratorPosition *getIteratorPosition(ProgramStateRef State,
237 const SVal &Val);
238const IteratorPosition *getIteratorPosition(ProgramStateRef State,
239 RegionOrSymbol RegOrSym);
240ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
241 const IteratorPosition &Pos);
242ProgramStateRef setIteratorPosition(ProgramStateRef State,
243 RegionOrSymbol RegOrSym,
244 const IteratorPosition &Pos);
245ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
246ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
247 RegionOrSymbol RegOrSym,
248 const IteratorPosition &Pos, bool Equal);
249ProgramStateRef relateIteratorPositions(ProgramStateRef State,
250 const IteratorPosition &Pos1,
251 const IteratorPosition &Pos2,
252 bool Equal);
253const ContainerData *getContainerData(ProgramStateRef State,
254 const MemRegion *Cont);
255ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
256 const ContainerData &CData);
257bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
258} // namespace
259
260IteratorChecker::IteratorChecker() {
261 OutOfRangeBugType.reset(
262 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
263 OutOfRangeBugType->setSuppressOnSink(true);
264}
265
266void IteratorChecker::checkPreCall(const CallEvent &Call,
267 CheckerContext &C) const {
268 // Check for out of range access
269 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
270 if (!Func)
271 return;
272
273 if (Func->isOverloadedOperator()) {
274 if (ChecksEnabled[CK_IteratorRangeChecker] &&
275 isDereferenceOperator(Func->getOverloadedOperator())) {
276 // Check for dereference of out-of-range iterators
277 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
278 verifyDereference(C, InstCall->getCXXThisVal());
279 } else {
280 verifyDereference(C, Call.getArgSVal(0));
281 }
282 }
283 }
284}
285
286void IteratorChecker::checkPostCall(const CallEvent &Call,
287 CheckerContext &C) const {
288 // Record new iterator positions and iterator position changes
289 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
290 if (!Func)
291 return;
292
293 if (Func->isOverloadedOperator()) {
294 const auto Op = Func->getOverloadedOperator();
295 if (isSimpleComparisonOperator(Op)) {
296 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
297 handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
298 Call.getArgSVal(0), Op);
299 } else {
300 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
301 Call.getArgSVal(1), Op);
302 }
303 }
304 } else {
305 const auto *OrigExpr = Call.getOriginExpr();
306 if (!OrigExpr)
307 return;
308
309 if (!isIteratorType(Call.getResultType()))
310 return;
311
312 auto State = C.getState();
313 // Already bound to container?
314 if (getIteratorPosition(State, Call.getReturnValue()))
315 return;
316
317 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
318 if (isEndCall(Func)) {
319 handleEnd(C, OrigExpr, Call.getReturnValue(),
320 InstCall->getCXXThisVal());
321 return;
322 }
323 }
324
325 // Copy-like and move constructors
326 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
327 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
328 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
329 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
330 State = removeIteratorPosition(State, Call.getArgSVal(0));
331 }
332 C.addTransition(State);
333 return;
334 }
335 }
336
337 // Assumption: if return value is an iterator which is not yet bound to a
338 // container, then look for the first iterator argument, and
339 // bind the return value to the same container. This approach
340 // works for STL algorithms.
341 // FIXME: Add a more conservative mode
342 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
343 if (isIteratorType(Call.getArgExpr(i)->getType())) {
344 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
345 assignToContainer(C, OrigExpr, Call.getReturnValue(),
346 Pos->getContainer());
347 return;
348 }
349 }
350 }
351 }
352}
353
354void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
355 CheckerContext &C) const {
356 /* Transfer iterator state to temporary objects */
357 auto State = C.getState();
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000358 const auto *Pos =
George Karpenkovd703ec92018-01-17 20:27:29 +0000359 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000360 if (!Pos)
361 return;
George Karpenkovd703ec92018-01-17 20:27:29 +0000362 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
Artem Dergachev8fa639e2017-05-29 15:03:20 +0000363 C.addTransition(State);
364}
365
366void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
367 CheckerContext &C) const {
368 // Cleanup
369 auto State = C.getState();
370
371 auto RegionMap = State->get<IteratorRegionMap>();
372 for (const auto Reg : RegionMap) {
373 if (!SR.isLiveRegion(Reg.first)) {
374 State = State->remove<IteratorRegionMap>(Reg.first);
375 }
376 }
377
378 auto SymbolMap = State->get<IteratorSymbolMap>();
379 for (const auto Sym : SymbolMap) {
380 if (!SR.isLive(Sym.first)) {
381 State = State->remove<IteratorSymbolMap>(Sym.first);
382 }
383 }
384
385 auto ContMap = State->get<ContainerMap>();
386 for (const auto Cont : ContMap) {
387 if (!SR.isLiveRegion(Cont.first)) {
388 State = State->remove<ContainerMap>(Cont.first);
389 }
390 }
391
392 auto ComparisonMap = State->get<IteratorComparisonMap>();
393 for (const auto Comp : ComparisonMap) {
394 if (!SR.isLive(Comp.first)) {
395 State = State->remove<IteratorComparisonMap>(Comp.first);
396 }
397 }
398}
399
400ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
401 bool Assumption) const {
402 // Load recorded comparison and transfer iterator state between sides
403 // according to comparison operator and assumption
404 const auto *SE = Cond.getAsSymExpr();
405 if (!SE)
406 return State;
407
408 auto Opc = getOpcode(SE);
409 if (Opc != BO_EQ && Opc != BO_NE)
410 return State;
411
412 bool Negated = false;
413 const auto *Comp = loadComparison(State, SE);
414 if (!Comp) {
415 // Try negated comparison, which is a SymExpr to 0 integer comparison
416 const auto *SIE = dyn_cast<SymIntExpr>(SE);
417 if (!SIE)
418 return State;
419
420 if (SIE->getRHS() != 0)
421 return State;
422
423 SE = SIE->getLHS();
424 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
425 Opc = getOpcode(SE);
426 if (Opc != BO_EQ && Opc != BO_NE)
427 return State;
428
429 Comp = loadComparison(State, SE);
430 if (!Comp)
431 return State;
432 }
433
434 return processComparison(State, Comp->getLeft(), Comp->getRight(),
435 (Comp->isEquality() == Assumption) != Negated);
436}
437
438void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
439 const SVal &LVal, const SVal &RVal,
440 OverloadedOperatorKind Op) const {
441 // Record the operands and the operator of the comparison for the next
442 // evalAssume, if the result is a symbolic expression. If it is a concrete
443 // value (only one branch is possible), then transfer the state between
444 // the operands according to the operator and the result
445 auto State = C.getState();
446 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
447 const auto *LPos = getIteratorPosition(State, LVal);
448 const auto *RPos = getIteratorPosition(State, RVal);
449 if (!LPos && !RPos)
450 return;
451 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
452 C.addTransition(State);
453 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
454 if ((State = processComparison(
455 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
456 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
457 C.addTransition(State);
458 } else {
459 C.generateSink(State, C.getPredecessor());
460 }
461 }
462}
463
464void IteratorChecker::verifyDereference(CheckerContext &C,
465 const SVal &Val) const {
466 auto State = C.getState();
467 const auto *Pos = getIteratorPosition(State, Val);
468 if (Pos && isOutOfRange(State, *Pos)) {
469 // If I do not put a tag here, some range tests will fail
470 static CheckerProgramPointTag Tag("IteratorRangeChecker",
471 "IteratorOutOfRange");
472 auto *N = C.generateNonFatalErrorNode(State, &Tag);
473 if (!N) {
474 return;
475 }
476 reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
477 }
478}
479
480void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
481 const SVal &RetVal, const SVal &Cont) const {
482 const auto *ContReg = Cont.getAsRegion();
483 if (!ContReg)
484 return;
485
486 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
487 ContReg = CBOR->getSuperRegion();
488 }
489
490 // If the container already has an end symbol then use it. Otherwise first
491 // create a new one.
492 auto State = C.getState();
493 auto EndSym = getContainerEnd(State, ContReg);
494 if (!EndSym) {
495 auto &SymMgr = C.getSymbolManager();
496 EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
497 C.getASTContext().LongTy, C.blockCount());
498 State = createContainerEnd(State, ContReg, EndSym);
499 }
500 State = setIteratorPosition(State, RetVal,
501 IteratorPosition::getPosition(ContReg, EndSym));
502 C.addTransition(State);
503}
504
505void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
506 const SVal &RetVal,
507 const MemRegion *Cont) const {
508 while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
509 Cont = CBOR->getSuperRegion();
510 }
511
512 auto State = C.getState();
513 auto &SymMgr = C.getSymbolManager();
514 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
515 C.getASTContext().LongTy, C.blockCount());
516 State = setIteratorPosition(State, RetVal,
517 IteratorPosition::getPosition(Cont, Sym));
518 C.addTransition(State);
519}
520
521void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
522 const SVal &Val, CheckerContext &C,
523 ExplodedNode *ErrNode) const {
524 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
525 R->markInteresting(Val);
526 C.emitReport(std::move(R));
527}
528
529namespace {
530
531bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
532bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
533 BinaryOperator::Opcode Opc);
534
535bool isIteratorType(const QualType &Type) {
536 if (Type->isPointerType())
537 return true;
538
539 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
540 return isIterator(CRD);
541}
542
543bool isIterator(const CXXRecordDecl *CRD) {
544 if (!CRD)
545 return false;
546
547 const auto Name = CRD->getName();
548 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
549 Name.endswith_lower("it")))
550 return false;
551
552 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
553 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
554 for (const auto *Method : CRD->methods()) {
555 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
556 if (Ctor->isCopyConstructor()) {
557 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
558 }
559 continue;
560 }
561 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
562 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
563 continue;
564 }
565 if (Method->isCopyAssignmentOperator()) {
566 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
567 continue;
568 }
569 if (!Method->isOverloadedOperator())
570 continue;
571 const auto OPK = Method->getOverloadedOperator();
572 if (OPK == OO_PlusPlus) {
573 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
574 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
575 continue;
576 }
577 if (OPK == OO_Star) {
578 HasDerefOp = (Method->getNumParams() == 0);
579 continue;
580 }
581 }
582
583 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
584 HasPostIncrOp && HasDerefOp;
585}
586
587bool isEndCall(const FunctionDecl *Func) {
588 const auto *IdInfo = Func->getIdentifier();
589 if (!IdInfo)
590 return false;
591 return IdInfo->getName().endswith_lower("end");
592}
593
594bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
595 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
596}
597
598bool isDereferenceOperator(OverloadedOperatorKind OK) {
599 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
600 OK == OO_Subscript;
601}
602
603BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
604 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
605 return BSE->getOpcode();
606 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
607 const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
608 if (!COE)
609 return BO_Comma; // Extremal value, neither EQ nor NE
610 if (COE->getOperator() == OO_EqualEqual) {
611 return BO_EQ;
612 } else if (COE->getOperator() == OO_ExclaimEqual) {
613 return BO_NE;
614 }
615 return BO_Comma; // Extremal value, neither EQ nor NE
616 }
617 return BO_Comma; // Extremal value, neither EQ nor NE
618}
619
620const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
621 if (const auto Reg = Val.getAsRegion()) {
622 return Reg;
623 } else if (const auto Sym = Val.getAsSymbol()) {
624 return Sym;
625 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
626 return LCVal->getRegion();
627 }
628 return RegionOrSymbol();
629}
630
631const ProgramStateRef processComparison(ProgramStateRef State,
632 RegionOrSymbol LVal,
633 RegionOrSymbol RVal, bool Equal) {
634 const auto *LPos = getIteratorPosition(State, LVal);
635 const auto *RPos = getIteratorPosition(State, RVal);
636 if (LPos && !RPos) {
637 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
638 } else if (!LPos && RPos) {
639 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
640 } else if (LPos && RPos) {
641 State = relateIteratorPositions(State, *LPos, *RPos, Equal);
642 }
643 return State;
644}
645
646const ProgramStateRef saveComparison(ProgramStateRef State,
647 const SymExpr *Condition, const SVal &LVal,
648 const SVal &RVal, bool Eq) {
649 const auto Left = getRegionOrSymbol(LVal);
650 const auto Right = getRegionOrSymbol(RVal);
651 if (!Left || !Right)
652 return State;
653 return State->set<IteratorComparisonMap>(Condition,
654 IteratorComparison(Left, Right, Eq));
655}
656
657const IteratorComparison *loadComparison(ProgramStateRef State,
658 const SymExpr *Condition) {
659 return State->get<IteratorComparisonMap>(Condition);
660}
661
662SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
663 const auto *CDataPtr = getContainerData(State, Cont);
664 if (!CDataPtr)
665 return nullptr;
666
667 return CDataPtr->getEnd();
668}
669
670ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
671 const SymbolRef Sym) {
672 // Only create if it does not exist
673 const auto *CDataPtr = getContainerData(State, Cont);
674 if (CDataPtr) {
675 if (CDataPtr->getEnd()) {
676 return State;
677 } else {
678 const auto CData = CDataPtr->newEnd(Sym);
679 return setContainerData(State, Cont, CData);
680 }
681 } else {
682 const auto CData = ContainerData::fromEnd(Sym);
683 return setContainerData(State, Cont, CData);
684 }
685}
686
687const ContainerData *getContainerData(ProgramStateRef State,
688 const MemRegion *Cont) {
689 return State->get<ContainerMap>(Cont);
690}
691
692ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
693 const ContainerData &CData) {
694 return State->set<ContainerMap>(Cont, CData);
695}
696
697const IteratorPosition *getIteratorPosition(ProgramStateRef State,
698 const SVal &Val) {
699 if (const auto Reg = Val.getAsRegion()) {
700 return State->get<IteratorRegionMap>(Reg);
701 } else if (const auto Sym = Val.getAsSymbol()) {
702 return State->get<IteratorSymbolMap>(Sym);
703 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
704 return State->get<IteratorRegionMap>(LCVal->getRegion());
705 }
706 return nullptr;
707}
708
709const IteratorPosition *getIteratorPosition(ProgramStateRef State,
710 RegionOrSymbol RegOrSym) {
711 if (RegOrSym.is<const MemRegion *>()) {
712 return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
713 } else if (RegOrSym.is<SymbolRef>()) {
714 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
715 }
716 return nullptr;
717}
718
719ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
720 const IteratorPosition &Pos) {
721 if (const auto Reg = Val.getAsRegion()) {
722 return State->set<IteratorRegionMap>(Reg, Pos);
723 } else if (const auto Sym = Val.getAsSymbol()) {
724 return State->set<IteratorSymbolMap>(Sym, Pos);
725 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
726 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
727 }
728 return nullptr;
729}
730
731ProgramStateRef setIteratorPosition(ProgramStateRef State,
732 RegionOrSymbol RegOrSym,
733 const IteratorPosition &Pos) {
734 if (RegOrSym.is<const MemRegion *>()) {
735 return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
736 Pos);
737 } else if (RegOrSym.is<SymbolRef>()) {
738 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
739 }
740 return nullptr;
741}
742
743ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
744 if (const auto Reg = Val.getAsRegion()) {
745 return State->remove<IteratorRegionMap>(Reg);
746 } else if (const auto Sym = Val.getAsSymbol()) {
747 return State->remove<IteratorSymbolMap>(Sym);
748 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
749 return State->remove<IteratorRegionMap>(LCVal->getRegion());
750 }
751 return nullptr;
752}
753
754ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
755 RegionOrSymbol RegOrSym,
756 const IteratorPosition &Pos,
757 bool Equal) {
758 if (Equal) {
759 return setIteratorPosition(State, RegOrSym, Pos);
760 } else {
761 return State;
762 }
763}
764
765ProgramStateRef relateIteratorPositions(ProgramStateRef State,
766 const IteratorPosition &Pos1,
767 const IteratorPosition &Pos2,
768 bool Equal) {
769 // Try to compare them and get a defined value
770 auto &SVB = State->getStateManager().getSValBuilder();
771 const auto comparison =
772 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
773 nonloc::SymbolVal(Pos2.getOffset()), SVB.getConditionType())
774 .getAs<DefinedSVal>();
775 if (comparison) {
776 return State->assume(*comparison, Equal);
777 }
778
779 return State;
780}
781
782bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
783 const auto *Cont = Pos.getContainer();
784 const auto *CData = getContainerData(State, Cont);
785 if (!CData)
786 return false;
787
788 // Out of range means less than the begin symbol or greater or equal to the
789 // end symbol.
790
791 const auto End = CData->getEnd();
792 if (End) {
793 if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
794 return true;
795 }
796 }
797
798 return false;
799}
800
801bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
802 return compare(State, Sym1, Sym2, BO_GE);
803}
804
805bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
806 BinaryOperator::Opcode Opc) {
807 auto &SMgr = State->getStateManager();
808 auto &SVB = SMgr.getSValBuilder();
809
810 const auto comparison =
811 SVB.evalBinOp(State, Opc, nonloc::SymbolVal(Sym1),
812 nonloc::SymbolVal(Sym2), SVB.getConditionType())
813 .getAs<DefinedSVal>();
814
815 if(comparison) {
816 return !!State->assume(*comparison, true);
817 }
818
819 return false;
820}
821
822} // namespace
823
824#define REGISTER_CHECKER(name) \
825 void ento::register##name(CheckerManager &Mgr) { \
826 auto *checker = Mgr.registerChecker<IteratorChecker>(); \
827 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
828 checker->CheckNames[IteratorChecker::CK_##name] = \
829 Mgr.getCurrentCheckName(); \
830 }
831
832REGISTER_CHECKER(IteratorRangeChecker)