1 //===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Defines a checker for using iterators outside their range (past end). Usage
11 // means here dereferencing, incrementing etc.
13 //===----------------------------------------------------------------------===//
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
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.
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.
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
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.
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.
49 // The checker works the following way: We record the begin and the
50 // past-end iterator for all containers whenever their `.begin()` and `.end()`
51 // are called. Since the Constraint Manager cannot handle such SVals we need
52 // to take over its role. We post-check equality and non-equality comparisons
53 // and record that the two sides are equal if we are in the 'equal' branch
54 // (true-branch for `==` and false-branch for `!=`).
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
63 // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
64 // we only use expressions of the format S, S+n or S-n for iterator positions
65 // where S is a conjured symbol and n is an unsigned concrete integer. When
66 // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
67 // a constraint which we later retrieve when doing an actual comparison.
69 #include "ClangSACheckers.h"
70 #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"
75 using namespace clang;
80 // Abstract position of an iterator. This helps to handle all three kinds
81 // of operators in a common way by using a symbolic position.
82 struct IteratorPosition {
85 // Container the iterator belongs to
86 const MemRegion *Cont;
89 const SymbolRef Offset;
91 IteratorPosition(const MemRegion *C, SymbolRef Of)
92 : Cont(C), Offset(Of) {}
95 const MemRegion *getContainer() const { return Cont; }
96 SymbolRef getOffset() const { return Offset; }
98 static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
99 return IteratorPosition(C, Of);
102 IteratorPosition setTo(SymbolRef NewOf) const {
103 return IteratorPosition(Cont, NewOf);
106 bool operator==(const IteratorPosition &X) const {
107 return Cont == X.Cont && Offset == X.Offset;
110 bool operator!=(const IteratorPosition &X) const {
111 return Cont != X.Cont || Offset != X.Offset;
114 void Profile(llvm::FoldingSetNodeID &ID) const {
120 typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
122 // Structure to record the symbolic begin and end position of a container
123 struct ContainerData {
125 const SymbolRef Begin, End;
127 ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
130 static ContainerData fromBegin(SymbolRef B) {
131 return ContainerData(B, nullptr);
134 static ContainerData fromEnd(SymbolRef E) {
135 return ContainerData(nullptr, E);
138 SymbolRef getBegin() const { return Begin; }
139 SymbolRef getEnd() const { return End; }
141 ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
143 ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
145 bool operator==(const ContainerData &X) const {
146 return Begin == X.Begin && End == X.End;
149 bool operator!=(const ContainerData &X) const {
150 return Begin != X.Begin || End != X.End;
153 void Profile(llvm::FoldingSetNodeID &ID) const {
159 // Structure fo recording iterator comparisons. We needed to retrieve the
160 // original comparison expression in assumptions.
161 struct IteratorComparison {
163 RegionOrSymbol Left, Right;
167 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
168 : Left(L), Right(R), Equality(Eq) {}
170 RegionOrSymbol getLeft() const { return Left; }
171 RegionOrSymbol getRight() const { return Right; }
172 bool isEquality() const { return Equality; }
173 bool operator==(const IteratorComparison &X) const {
174 return Left == X.Left && Right == X.Right && Equality == X.Equality;
176 bool operator!=(const IteratorComparison &X) const {
177 return Left != X.Left || Right != X.Right || Equality != X.Equality;
179 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
182 class IteratorChecker
183 : public Checker<check::PreCall, check::PostCall,
184 check::PreStmt<CXXOperatorCallExpr>,
185 check::PostStmt<MaterializeTemporaryExpr>,
186 check::LiveSymbols, check::DeadSymbols,
189 std::unique_ptr<BugType> OutOfRangeBugType;
191 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
192 const SVal &RVal, OverloadedOperatorKind Op) const;
193 void verifyDereference(CheckerContext &C, const SVal &Val) const;
194 void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
196 void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
198 void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
199 const SVal &RetVal, const SVal &LHS,
200 const SVal &RHS) const;
201 void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
202 const SVal &Cont) const;
203 void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
204 const SVal &Cont) const;
205 void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
206 const MemRegion *Cont) const;
207 void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
208 const SVal &RetVal, const SVal &LHS,
209 const SVal &RHS) const;
210 void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
211 CheckerContext &C, ExplodedNode *ErrNode) const;
217 CK_IteratorRangeChecker,
221 DefaultBool ChecksEnabled[CK_NumCheckKinds];
222 CheckName CheckNames[CK_NumCheckKinds];
224 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
225 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
226 void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
227 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
228 CheckerContext &C) const;
229 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
230 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
231 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
232 bool Assumption) const;
236 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
237 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
240 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
242 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
247 bool isIteratorType(const QualType &Type);
248 bool isIterator(const CXXRecordDecl *CRD);
249 bool isBeginCall(const FunctionDecl *Func);
250 bool isEndCall(const FunctionDecl *Func);
251 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
252 bool isDereferenceOperator(OverloadedOperatorKind OK);
253 bool isIncrementOperator(OverloadedOperatorKind OK);
254 bool isDecrementOperator(OverloadedOperatorKind OK);
255 bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
256 BinaryOperator::Opcode getOpcode(const SymExpr *SE);
257 const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
258 const ProgramStateRef processComparison(ProgramStateRef State,
260 RegionOrSymbol RVal, bool Equal);
261 const ProgramStateRef saveComparison(ProgramStateRef State,
262 const SymExpr *Condition, const SVal &LVal,
263 const SVal &RVal, bool Eq);
264 const IteratorComparison *loadComparison(ProgramStateRef State,
265 const SymExpr *Condition);
266 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
267 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
268 ProgramStateRef createContainerBegin(ProgramStateRef State,
269 const MemRegion *Cont,
270 const SymbolRef Sym);
271 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
272 const SymbolRef Sym);
273 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
275 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
276 RegionOrSymbol RegOrSym);
277 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
278 const IteratorPosition &Pos);
279 ProgramStateRef setIteratorPosition(ProgramStateRef State,
280 RegionOrSymbol RegOrSym,
281 const IteratorPosition &Pos);
282 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
283 ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
284 RegionOrSymbol RegOrSym,
285 const IteratorPosition &Pos, bool Equal);
286 ProgramStateRef relateIteratorPositions(ProgramStateRef State,
287 const IteratorPosition &Pos1,
288 const IteratorPosition &Pos2,
290 const ContainerData *getContainerData(ProgramStateRef State,
291 const MemRegion *Cont);
292 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
293 const ContainerData &CData);
294 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
295 bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
296 bool isZero(ProgramStateRef State, const NonLoc &Val);
299 IteratorChecker::IteratorChecker() {
300 OutOfRangeBugType.reset(
301 new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
302 OutOfRangeBugType->setSuppressOnSink(true);
305 void IteratorChecker::checkPreCall(const CallEvent &Call,
306 CheckerContext &C) const {
307 // Check for out of range access
308 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
312 if (Func->isOverloadedOperator()) {
313 if (ChecksEnabled[CK_IteratorRangeChecker] &&
314 isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
315 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
316 // Check for out-of-range incrementions and decrementions
317 if (Call.getNumArgs() >= 1) {
318 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
319 Call.getReturnValue(),
320 InstCall->getCXXThisVal(), Call.getArgSVal(0));
323 if (Call.getNumArgs() >= 2) {
324 verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
325 Call.getReturnValue(), Call.getArgSVal(0),
329 } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
330 isDereferenceOperator(Func->getOverloadedOperator())) {
331 // Check for dereference of out-of-range iterators
332 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
333 verifyDereference(C, InstCall->getCXXThisVal());
335 verifyDereference(C, Call.getArgSVal(0));
341 void IteratorChecker::checkPostCall(const CallEvent &Call,
342 CheckerContext &C) const {
343 // Record new iterator positions and iterator position changes
344 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
348 if (Func->isOverloadedOperator()) {
349 const auto Op = Func->getOverloadedOperator();
350 if (isSimpleComparisonOperator(Op)) {
351 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
352 handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
353 Call.getArgSVal(0), Op);
355 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
356 Call.getArgSVal(1), Op);
358 } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
359 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
360 if (Call.getNumArgs() >= 1) {
361 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
362 Call.getReturnValue(),
363 InstCall->getCXXThisVal(), Call.getArgSVal(0));
366 if (Call.getNumArgs() >= 2) {
367 handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
368 Call.getReturnValue(), Call.getArgSVal(0),
372 } else if (isIncrementOperator(Func->getOverloadedOperator())) {
373 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
374 handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
377 handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
380 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
381 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
382 handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
385 handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
390 const auto *OrigExpr = Call.getOriginExpr();
394 if (!isIteratorType(Call.getResultType()))
397 auto State = C.getState();
398 // Already bound to container?
399 if (getIteratorPosition(State, Call.getReturnValue()))
402 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
403 if (isBeginCall(Func)) {
404 handleBegin(C, OrigExpr, Call.getReturnValue(),
405 InstCall->getCXXThisVal());
408 if (isEndCall(Func)) {
409 handleEnd(C, OrigExpr, Call.getReturnValue(),
410 InstCall->getCXXThisVal());
415 // Copy-like and move constructors
416 if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
417 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
418 State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
419 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
420 State = removeIteratorPosition(State, Call.getArgSVal(0));
422 C.addTransition(State);
427 // Assumption: if return value is an iterator which is not yet bound to a
428 // container, then look for the first iterator argument, and
429 // bind the return value to the same container. This approach
430 // works for STL algorithms.
431 // FIXME: Add a more conservative mode
432 for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
433 if (isIteratorType(Call.getArgExpr(i)->getType())) {
434 if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
435 assignToContainer(C, OrigExpr, Call.getReturnValue(),
436 Pos->getContainer());
444 void IteratorChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
445 CheckerContext &C) const {
446 const auto *ThisExpr = COCE->getArg(0);
448 auto State = C.getState();
449 const auto *LCtx = C.getLocationContext();
451 const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
452 if (const auto *Reg = CurrentThis.getAsRegion()) {
453 if (!Reg->getAs<CXXTempObjectRegion>())
455 const auto OldState = C.getPredecessor()->getFirstPred()->getState();
456 const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
457 // FIXME: This solution is unreliable. It may happen that another checker
458 // subscribes to the pre-statement check of `CXXOperatorCallExpr`
459 // and adds a transition before us. The proper fix is to make the
460 // CFG provide a `ConstructionContext` for the `CXXOperatorCallExpr`,
461 // which would turn the corresponding `CFGStmt` element into a
462 // `CFGCXXRecordTypedCall` element, which will allow `ExprEngine` to
463 // foresee that the `begin()`/`end()` call constructs the object
464 // directly in the temporary region that `CXXOperatorCallExpr` takes
465 // as its implicit object argument.
466 const auto *Pos = getIteratorPosition(OldState, OldThis);
469 State = setIteratorPosition(State, CurrentThis, *Pos);
470 C.addTransition(State);
474 void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
475 CheckerContext &C) const {
476 /* Transfer iterator state to temporary objects */
477 auto State = C.getState();
479 getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
482 State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
483 C.addTransition(State);
486 void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
487 SymbolReaper &SR) const {
488 // Keep symbolic expressions of iterator positions, container begins and ends
490 auto RegionMap = State->get<IteratorRegionMap>();
491 for (const auto Reg : RegionMap) {
492 const auto Offset = Reg.second.getOffset();
493 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
494 if (isa<SymbolData>(*i))
498 auto SymbolMap = State->get<IteratorSymbolMap>();
499 for (const auto Sym : SymbolMap) {
500 const auto Offset = Sym.second.getOffset();
501 for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
502 if (isa<SymbolData>(*i))
506 auto ContMap = State->get<ContainerMap>();
507 for (const auto Cont : ContMap) {
508 const auto CData = Cont.second;
509 if (CData.getBegin()) {
510 SR.markLive(CData.getBegin());
512 if (CData.getEnd()) {
513 SR.markLive(CData.getEnd());
518 void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
519 CheckerContext &C) const {
521 auto State = C.getState();
523 auto RegionMap = State->get<IteratorRegionMap>();
524 for (const auto Reg : RegionMap) {
525 if (!SR.isLiveRegion(Reg.first)) {
526 State = State->remove<IteratorRegionMap>(Reg.first);
530 auto SymbolMap = State->get<IteratorSymbolMap>();
531 for (const auto Sym : SymbolMap) {
532 if (!SR.isLive(Sym.first)) {
533 State = State->remove<IteratorSymbolMap>(Sym.first);
537 auto ContMap = State->get<ContainerMap>();
538 for (const auto Cont : ContMap) {
539 if (!SR.isLiveRegion(Cont.first)) {
540 // We must keep the container data while it has live iterators to be able
541 // to compare them to the begin and the end of the container.
542 if (!hasLiveIterators(State, Cont.first)) {
543 State = State->remove<ContainerMap>(Cont.first);
548 auto ComparisonMap = State->get<IteratorComparisonMap>();
549 for (const auto Comp : ComparisonMap) {
550 if (!SR.isLive(Comp.first)) {
551 State = State->remove<IteratorComparisonMap>(Comp.first);
555 C.addTransition(State);
558 ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
559 bool Assumption) const {
560 // Load recorded comparison and transfer iterator state between sides
561 // according to comparison operator and assumption
562 const auto *SE = Cond.getAsSymExpr();
566 auto Opc = getOpcode(SE);
567 if (Opc != BO_EQ && Opc != BO_NE)
570 bool Negated = false;
571 const auto *Comp = loadComparison(State, SE);
573 // Try negated comparison, which is a SymExpr to 0 integer comparison
574 const auto *SIE = dyn_cast<SymIntExpr>(SE);
578 if (SIE->getRHS() != 0)
582 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
584 if (Opc != BO_EQ && Opc != BO_NE)
587 Comp = loadComparison(State, SE);
592 return processComparison(State, Comp->getLeft(), Comp->getRight(),
593 (Comp->isEquality() == Assumption) != Negated);
596 void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
597 const SVal &LVal, const SVal &RVal,
598 OverloadedOperatorKind Op) const {
599 // Record the operands and the operator of the comparison for the next
600 // evalAssume, if the result is a symbolic expression. If it is a concrete
601 // value (only one branch is possible), then transfer the state between
602 // the operands according to the operator and the result
603 auto State = C.getState();
604 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
605 const auto *LPos = getIteratorPosition(State, LVal);
606 const auto *RPos = getIteratorPosition(State, RVal);
609 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
610 C.addTransition(State);
611 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
612 if ((State = processComparison(
613 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
614 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
615 C.addTransition(State);
617 C.generateSink(State, C.getPredecessor());
622 void IteratorChecker::verifyDereference(CheckerContext &C,
623 const SVal &Val) const {
624 auto State = C.getState();
625 const auto *Pos = getIteratorPosition(State, Val);
626 if (Pos && isOutOfRange(State, *Pos)) {
627 // If I do not put a tag here, some range tests will fail
628 static CheckerProgramPointTag Tag("IteratorRangeChecker",
629 "IteratorOutOfRange");
630 auto *N = C.generateNonFatalErrorNode(State, &Tag);
633 reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
637 void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
638 const SVal &Iter, bool Postfix) const {
639 // Increment the symbolic expressions which represents the position of the
641 auto State = C.getState();
642 const auto *Pos = getIteratorPosition(State, Iter);
644 auto &SymMgr = C.getSymbolManager();
645 auto &BVF = SymMgr.getBasicVals();
646 auto &SVB = C.getSValBuilder();
647 const auto OldOffset = Pos->getOffset();
649 SVB.evalBinOp(State, BO_Add,
650 nonloc::SymbolVal(OldOffset),
651 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
652 SymMgr.getType(OldOffset)).getAsSymbol();
653 auto NewPos = Pos->setTo(NewOffset);
654 State = setIteratorPosition(State, Iter, NewPos);
655 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
656 C.addTransition(State);
660 void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
661 const SVal &Iter, bool Postfix) const {
662 // Decrement the symbolic expressions which represents the position of the
664 auto State = C.getState();
665 const auto *Pos = getIteratorPosition(State, Iter);
667 auto &SymMgr = C.getSymbolManager();
668 auto &BVF = SymMgr.getBasicVals();
669 auto &SVB = C.getSValBuilder();
670 const auto OldOffset = Pos->getOffset();
672 SVB.evalBinOp(State, BO_Sub,
673 nonloc::SymbolVal(OldOffset),
674 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
675 SymMgr.getType(OldOffset)).getAsSymbol();
676 auto NewPos = Pos->setTo(NewOffset);
677 State = setIteratorPosition(State, Iter, NewPos);
678 State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
679 C.addTransition(State);
683 // This function tells the analyzer's engine that symbols produced by our
684 // checker, most notably iterator positions, are relatively small.
685 // A distance between items in the container should not be very large.
686 // By assuming that it is within around 1/8 of the address space,
687 // we can help the analyzer perform operations on these symbols
688 // without being afraid of integer overflows.
689 // FIXME: Should we provide it as an API, so that all checkers could use it?
690 static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
692 SValBuilder &SVB = State->getStateManager().getSValBuilder();
693 BasicValueFactory &BV = SVB.getBasicValueFactory();
695 QualType T = Sym->getType();
696 assert(T->isSignedIntegerOrEnumerationType());
697 APSIntType AT = BV.getAPSIntType(T);
699 ProgramStateRef NewState = State;
701 llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
702 SVal IsCappedFromAbove =
703 SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
704 nonloc::ConcreteInt(Max), SVB.getConditionType());
705 if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
706 NewState = NewState->assume(*DV, true);
711 llvm::APSInt Min = -Max;
712 SVal IsCappedFromBelow =
713 SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
714 nonloc::ConcreteInt(Min), SVB.getConditionType());
715 if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
716 NewState = NewState->assume(*DV, true);
724 void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
725 OverloadedOperatorKind Op,
728 const SVal &RHS) const {
729 // Increment or decrement the symbolic expressions which represents the
730 // position of the iterator
731 auto State = C.getState();
732 const auto *Pos = getIteratorPosition(State, LHS);
736 const auto *value = &RHS;
737 if (auto loc = RHS.getAs<Loc>()) {
738 const auto val = State->getRawSVal(*loc);
742 auto &SymMgr = C.getSymbolManager();
743 auto &SVB = C.getSValBuilder();
744 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
745 const auto OldOffset = Pos->getOffset();
747 if (const auto intValue = value->getAs<nonloc::ConcreteInt>()) {
748 // For concrete integers we can calculate the new position
749 NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
751 SymMgr.getType(OldOffset)).getAsSymbol();
753 // For other symbols create a new symbol to keep expressions simple
754 const auto &LCtx = C.getLocationContext();
755 NewOffset = SymMgr.conjureSymbol(nullptr, LCtx, SymMgr.getType(OldOffset),
757 State = assumeNoOverflow(State, NewOffset, 4);
759 auto NewPos = Pos->setTo(NewOffset);
760 auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
761 State = setIteratorPosition(State, TgtVal, NewPos);
762 C.addTransition(State);
765 void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
766 OverloadedOperatorKind Op,
769 const SVal &RHS) const {
770 auto State = C.getState();
772 // If the iterator is initially inside its range, then the operation is valid
773 const auto *Pos = getIteratorPosition(State, LHS);
774 if (!Pos || !isOutOfRange(State, *Pos))
778 if (auto loc = RHS.getAs<Loc>()) {
779 value = State->getRawSVal(*loc);
782 // Incremention or decremention by 0 is never bug
783 if (isZero(State, value.castAs<NonLoc>()))
786 auto &SymMgr = C.getSymbolManager();
787 auto &SVB = C.getSValBuilder();
788 auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
789 const auto OldOffset = Pos->getOffset();
790 const auto intValue = value.getAs<nonloc::ConcreteInt>();
794 auto NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
796 SymMgr.getType(OldOffset)).getAsSymbol();
797 auto NewPos = Pos->setTo(NewOffset);
799 // If out of range, the only valid operation is to step into the range
800 if (isOutOfRange(State, NewPos)) {
801 auto *N = C.generateNonFatalErrorNode(State);
804 reportOutOfRangeBug("Iterator accessed past its end.", LHS, C, N);
808 void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
809 const SVal &RetVal, const SVal &Cont) const {
810 const auto *ContReg = Cont.getAsRegion();
814 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
815 ContReg = CBOR->getSuperRegion();
818 // If the container already has a begin symbol then use it. Otherwise first
820 auto State = C.getState();
821 auto BeginSym = getContainerBegin(State, ContReg);
823 auto &SymMgr = C.getSymbolManager();
824 BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
825 C.getASTContext().LongTy, C.blockCount());
826 State = assumeNoOverflow(State, BeginSym, 4);
827 State = createContainerBegin(State, ContReg, BeginSym);
829 State = setIteratorPosition(State, RetVal,
830 IteratorPosition::getPosition(ContReg, BeginSym));
831 C.addTransition(State);
834 void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
835 const SVal &RetVal, const SVal &Cont) const {
836 const auto *ContReg = Cont.getAsRegion();
840 while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
841 ContReg = CBOR->getSuperRegion();
844 // If the container already has an end symbol then use it. Otherwise first
846 auto State = C.getState();
847 auto EndSym = getContainerEnd(State, ContReg);
849 auto &SymMgr = C.getSymbolManager();
850 EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
851 C.getASTContext().LongTy, C.blockCount());
852 State = assumeNoOverflow(State, EndSym, 4);
853 State = createContainerEnd(State, ContReg, EndSym);
855 State = setIteratorPosition(State, RetVal,
856 IteratorPosition::getPosition(ContReg, EndSym));
857 C.addTransition(State);
860 void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
862 const MemRegion *Cont) const {
863 while (const auto *CBOR = Cont->getAs<CXXBaseObjectRegion>()) {
864 Cont = CBOR->getSuperRegion();
867 auto State = C.getState();
868 auto &SymMgr = C.getSymbolManager();
869 auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
870 C.getASTContext().LongTy, C.blockCount());
871 State = assumeNoOverflow(State, Sym, 4);
872 State = setIteratorPosition(State, RetVal,
873 IteratorPosition::getPosition(Cont, Sym));
874 C.addTransition(State);
877 void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
878 const SVal &Val, CheckerContext &C,
879 ExplodedNode *ErrNode) const {
880 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
881 R->markInteresting(Val);
882 C.emitReport(std::move(R));
887 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
888 bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
889 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
890 BinaryOperator::Opcode Opc);
891 bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
892 BinaryOperator::Opcode Opc);
894 bool isIteratorType(const QualType &Type) {
895 if (Type->isPointerType())
898 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
899 return isIterator(CRD);
902 bool isIterator(const CXXRecordDecl *CRD) {
906 const auto Name = CRD->getName();
907 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
908 Name.endswith_lower("it")))
911 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
912 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
913 for (const auto *Method : CRD->methods()) {
914 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
915 if (Ctor->isCopyConstructor()) {
916 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
920 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
921 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
924 if (Method->isCopyAssignmentOperator()) {
925 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
928 if (!Method->isOverloadedOperator())
930 const auto OPK = Method->getOverloadedOperator();
931 if (OPK == OO_PlusPlus) {
932 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
933 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
936 if (OPK == OO_Star) {
937 HasDerefOp = (Method->getNumParams() == 0);
942 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
943 HasPostIncrOp && HasDerefOp;
946 bool isBeginCall(const FunctionDecl *Func) {
947 const auto *IdInfo = Func->getIdentifier();
950 return IdInfo->getName().endswith_lower("begin");
953 bool isEndCall(const FunctionDecl *Func) {
954 const auto *IdInfo = Func->getIdentifier();
957 return IdInfo->getName().endswith_lower("end");
960 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
961 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
964 bool isDereferenceOperator(OverloadedOperatorKind OK) {
965 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
969 bool isIncrementOperator(OverloadedOperatorKind OK) {
970 return OK == OO_PlusPlus;
973 bool isDecrementOperator(OverloadedOperatorKind OK) {
974 return OK == OO_MinusMinus;
977 bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
978 return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
982 BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
983 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
984 return BSE->getOpcode();
985 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
986 const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt());
988 return BO_Comma; // Extremal value, neither EQ nor NE
989 if (COE->getOperator() == OO_EqualEqual) {
991 } else if (COE->getOperator() == OO_ExclaimEqual) {
994 return BO_Comma; // Extremal value, neither EQ nor NE
996 return BO_Comma; // Extremal value, neither EQ nor NE
999 const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
1000 if (const auto Reg = Val.getAsRegion()) {
1002 } else if (const auto Sym = Val.getAsSymbol()) {
1004 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1005 return LCVal->getRegion();
1007 return RegionOrSymbol();
1010 const ProgramStateRef processComparison(ProgramStateRef State,
1011 RegionOrSymbol LVal,
1012 RegionOrSymbol RVal, bool Equal) {
1013 const auto *LPos = getIteratorPosition(State, LVal);
1014 const auto *RPos = getIteratorPosition(State, RVal);
1015 if (LPos && !RPos) {
1016 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
1017 } else if (!LPos && RPos) {
1018 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
1019 } else if (LPos && RPos) {
1020 State = relateIteratorPositions(State, *LPos, *RPos, Equal);
1025 const ProgramStateRef saveComparison(ProgramStateRef State,
1026 const SymExpr *Condition, const SVal &LVal,
1027 const SVal &RVal, bool Eq) {
1028 const auto Left = getRegionOrSymbol(LVal);
1029 const auto Right = getRegionOrSymbol(RVal);
1030 if (!Left || !Right)
1032 return State->set<IteratorComparisonMap>(Condition,
1033 IteratorComparison(Left, Right, Eq));
1036 const IteratorComparison *loadComparison(ProgramStateRef State,
1037 const SymExpr *Condition) {
1038 return State->get<IteratorComparisonMap>(Condition);
1041 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
1042 const auto *CDataPtr = getContainerData(State, Cont);
1046 return CDataPtr->getBegin();
1049 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
1050 const auto *CDataPtr = getContainerData(State, Cont);
1054 return CDataPtr->getEnd();
1057 ProgramStateRef createContainerBegin(ProgramStateRef State,
1058 const MemRegion *Cont,
1059 const SymbolRef Sym) {
1060 // Only create if it does not exist
1061 const auto *CDataPtr = getContainerData(State, Cont);
1063 if (CDataPtr->getBegin()) {
1066 const auto CData = CDataPtr->newBegin(Sym);
1067 return setContainerData(State, Cont, CData);
1069 const auto CData = ContainerData::fromBegin(Sym);
1070 return setContainerData(State, Cont, CData);
1073 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
1074 const SymbolRef Sym) {
1075 // Only create if it does not exist
1076 const auto *CDataPtr = getContainerData(State, Cont);
1078 if (CDataPtr->getEnd()) {
1081 const auto CData = CDataPtr->newEnd(Sym);
1082 return setContainerData(State, Cont, CData);
1084 const auto CData = ContainerData::fromEnd(Sym);
1085 return setContainerData(State, Cont, CData);
1088 const ContainerData *getContainerData(ProgramStateRef State,
1089 const MemRegion *Cont) {
1090 return State->get<ContainerMap>(Cont);
1093 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
1094 const ContainerData &CData) {
1095 return State->set<ContainerMap>(Cont, CData);
1098 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1100 if (const auto Reg = Val.getAsRegion()) {
1101 return State->get<IteratorRegionMap>(Reg);
1102 } else if (const auto Sym = Val.getAsSymbol()) {
1103 return State->get<IteratorSymbolMap>(Sym);
1104 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1105 return State->get<IteratorRegionMap>(LCVal->getRegion());
1110 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
1111 RegionOrSymbol RegOrSym) {
1112 if (RegOrSym.is<const MemRegion *>()) {
1113 return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
1114 } else if (RegOrSym.is<SymbolRef>()) {
1115 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
1120 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
1121 const IteratorPosition &Pos) {
1122 if (const auto Reg = Val.getAsRegion()) {
1123 return State->set<IteratorRegionMap>(Reg, Pos);
1124 } else if (const auto Sym = Val.getAsSymbol()) {
1125 return State->set<IteratorSymbolMap>(Sym, Pos);
1126 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1127 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
1132 ProgramStateRef setIteratorPosition(ProgramStateRef State,
1133 RegionOrSymbol RegOrSym,
1134 const IteratorPosition &Pos) {
1135 if (RegOrSym.is<const MemRegion *>()) {
1136 return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
1138 } else if (RegOrSym.is<SymbolRef>()) {
1139 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
1144 ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
1145 if (const auto Reg = Val.getAsRegion()) {
1146 return State->remove<IteratorRegionMap>(Reg);
1147 } else if (const auto Sym = Val.getAsSymbol()) {
1148 return State->remove<IteratorSymbolMap>(Sym);
1149 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
1150 return State->remove<IteratorRegionMap>(LCVal->getRegion());
1155 ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
1156 RegionOrSymbol RegOrSym,
1157 const IteratorPosition &Pos,
1160 return setIteratorPosition(State, RegOrSym, Pos);
1166 ProgramStateRef relateIteratorPositions(ProgramStateRef State,
1167 const IteratorPosition &Pos1,
1168 const IteratorPosition &Pos2,
1170 auto &SVB = State->getStateManager().getSValBuilder();
1172 // FIXME: This code should be reworked as follows:
1173 // 1. Subtract the operands using evalBinOp().
1174 // 2. Assume that the result doesn't overflow.
1175 // 3. Compare the result to 0.
1176 // 4. Assume the result of the comparison.
1177 const auto comparison =
1178 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
1179 nonloc::SymbolVal(Pos2.getOffset()),
1180 SVB.getConditionType());
1182 assert(comparison.getAs<DefinedSVal>() &&
1183 "Symbol comparison must be a `DefinedSVal`");
1185 auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
1186 if (const auto CompSym = comparison.getAsSymbol()) {
1187 assert(isa<SymIntExpr>(CompSym) &&
1188 "Symbol comparison must be a `SymIntExpr`");
1189 assert(BinaryOperator::isComparisonOp(
1190 cast<SymIntExpr>(CompSym)->getOpcode()) &&
1191 "Symbol comparison must be a comparison");
1192 return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
1198 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1199 auto RegionMap = State->get<IteratorRegionMap>();
1200 for (const auto Reg : RegionMap) {
1201 if (Reg.second.getContainer() == Cont)
1205 auto SymbolMap = State->get<IteratorSymbolMap>();
1206 for (const auto Sym : SymbolMap) {
1207 if (Sym.second.getContainer() == Cont)
1214 bool isZero(ProgramStateRef State, const NonLoc &Val) {
1215 auto &BVF = State->getBasicVals();
1216 return compare(State, Val,
1217 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
1221 bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
1222 const auto *Cont = Pos.getContainer();
1223 const auto *CData = getContainerData(State, Cont);
1227 // Out of range means less than the begin symbol or greater or equal to the
1230 const auto Beg = CData->getBegin();
1232 if (isLess(State, Pos.getOffset(), Beg)) {
1237 const auto End = CData->getEnd();
1239 if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
1247 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1248 return compare(State, Sym1, Sym2, BO_LT);
1251 bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
1252 return compare(State, Sym1, Sym2, BO_GE);
1255 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
1256 BinaryOperator::Opcode Opc) {
1257 return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
1260 bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
1261 BinaryOperator::Opcode Opc) {
1262 auto &SVB = State->getStateManager().getSValBuilder();
1264 const auto comparison =
1265 SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
1267 assert(comparison.getAs<DefinedSVal>() &&
1268 "Symbol comparison must be a `DefinedSVal`");
1270 return !State->assume(comparison.castAs<DefinedSVal>(), false);
1275 #define REGISTER_CHECKER(name) \
1276 void ento::register##name(CheckerManager &Mgr) { \
1277 auto *checker = Mgr.registerChecker<IteratorChecker>(); \
1278 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
1279 checker->CheckNames[IteratorChecker::CK_##name] = \
1280 Mgr.getCurrentCheckName(); \
1283 REGISTER_CHECKER(IteratorRangeChecker)