69 using namespace clang;
76 struct IteratorPosition {
89 const MemRegion *getContainer()
const {
return Cont; }
93 return IteratorPosition(C, Of);
96 IteratorPosition setTo(
SymbolRef NewOf)
const {
97 return IteratorPosition(Cont, NewOf);
101 return Cont == X.Cont &&
Offset == X.Offset;
105 return Cont != X.Cont ||
Offset != X.Offset;
108 void Profile(llvm::FoldingSetNodeID &
ID)
const {
114 typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
117 struct ContainerData {
125 return ContainerData(E);
130 ContainerData newEnd(
SymbolRef E)
const {
return ContainerData(E); }
140 void Profile(llvm::FoldingSetNodeID &ID)
const {
147 struct IteratorComparison {
149 RegionOrSymbol Left, Right;
153 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R,
bool Eq)
154 : Left(L), Right(R),
Equality(Eq) {}
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;
162 bool operator!=(
const IteratorComparison &
X)
const {
163 return Left != X.Left || Right != X.Right ||
Equality != X.Equality;
165 void Profile(llvm::FoldingSetNodeID &ID)
const { ID.AddInteger(
Equality); }
168 class IteratorChecker
169 :
public Checker<check::PreCall, check::PostCall,
170 check::PostStmt<MaterializeTemporaryExpr>,
174 std::unique_ptr<BugType> OutOfRangeBugType;
180 const SVal &Cont)
const;
183 void reportOutOfRangeBug(
const StringRef &Message,
const SVal &Val,
190 CK_IteratorRangeChecker,
203 bool Assumption)
const;
227 RegionOrSymbol RVal,
bool Equal);
229 const SymExpr *Condition,
const SVal &LVal,
230 const SVal &RVal,
bool Eq);
232 const SymExpr *Condition);
239 RegionOrSymbol RegOrSym);
241 const IteratorPosition &Pos);
243 RegionOrSymbol RegOrSym,
244 const IteratorPosition &Pos);
247 RegionOrSymbol RegOrSym,
248 const IteratorPosition &Pos,
bool Equal);
250 const IteratorPosition &Pos1,
251 const IteratorPosition &Pos2,
254 const MemRegion *Cont);
256 const ContainerData &CData);
260 IteratorChecker::IteratorChecker() {
261 OutOfRangeBugType.reset(
262 new BugType(
this,
"Iterator out of range",
"Misuse of STL APIs"));
263 OutOfRangeBugType->setSuppressOnSink(
true);
266 void IteratorChecker::checkPreCall(
const CallEvent &Call,
269 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.
getDecl());
273 if (Func->isOverloadedOperator()) {
274 if (ChecksEnabled[CK_IteratorRangeChecker] &&
277 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
278 verifyDereference(C, InstCall->getCXXThisVal());
286 void IteratorChecker::checkPostCall(
const CallEvent &Call,
289 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.
getDecl());
293 if (Func->isOverloadedOperator()) {
294 const auto Op = Func->getOverloadedOperator();
296 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
297 handleComparison(C, Call.
getReturnValue(), InstCall->getCXXThisVal(),
317 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
320 InstCall->getCXXThisVal());
326 if (isa<CXXConstructorCall>(&Call) && Call.
getNumArgs() == 1) {
329 if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
342 for (
unsigned i = 0; i < Call.
getNumArgs(); ++i) {
346 Pos->getContainer());
367 void IteratorChecker::checkDeadSymbols(
SymbolReaper &SR,
372 auto RegionMap =
State->get<IteratorRegionMap>();
373 for (
const auto Reg : RegionMap) {
375 State =
State->remove<IteratorRegionMap>(Reg.first);
379 auto SymbolMap =
State->get<IteratorSymbolMap>();
380 for (
const auto Sym : SymbolMap) {
381 if (!SR.
isLive(Sym.first)) {
382 State =
State->remove<IteratorSymbolMap>(Sym.first);
386 auto ContMap =
State->get<ContainerMap>();
387 for (
const auto Cont : ContMap) {
389 State =
State->remove<ContainerMap>(Cont.first);
393 auto ComparisonMap =
State->get<IteratorComparisonMap>();
394 for (
const auto Comp : ComparisonMap) {
395 if (!SR.
isLive(Comp.first)) {
396 State =
State->remove<IteratorComparisonMap>(Comp.first);
402 bool Assumption)
const {
410 if (Opc != BO_EQ && Opc != BO_NE)
413 bool Negated =
false;
421 if (SIE->getRHS() != 0)
425 Negated = SIE->getOpcode() == BO_EQ;
427 if (Opc != BO_EQ && Opc != BO_NE)
436 (Comp->isEquality() == Assumption) != Negated);
452 State =
saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
457 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
466 const SVal &Val)
const {
472 "IteratorOutOfRange");
477 reportOutOfRangeBug(
"Iterator accessed outside of its range.", Val, C, N);
482 const SVal &RetVal,
const SVal &Cont)
const {
488 ContReg = CBOR->getSuperRegion();
502 IteratorPosition::getPosition(ContReg, EndSym));
510 Cont = CBOR->getSuperRegion();
518 IteratorPosition::getPosition(Cont, Sym));
522 void IteratorChecker::reportOutOfRangeBug(
const StringRef &Message,
525 auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
526 R->markInteresting(Val);
549 if (!(
Name.endswith_lower(
"iterator") ||
Name.endswith_lower(
"iter") ||
550 Name.endswith_lower(
"it")))
553 bool HasCopyCtor =
false, HasCopyAssign =
true, HasDtor =
false,
554 HasPreIncrOp =
false, HasPostIncrOp =
false, HasDerefOp =
false;
555 for (
const auto *Method : CRD->
methods()) {
556 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
557 if (Ctor->isCopyConstructor()) {
558 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() ==
AS_public;
562 if (
const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
563 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() ==
AS_public;
566 if (Method->isCopyAssignmentOperator()) {
567 HasCopyAssign = !Method->isDeleted() && Method->getAccess() ==
AS_public;
570 if (!Method->isOverloadedOperator())
572 const auto OPK = Method->getOverloadedOperator();
573 if (OPK == OO_PlusPlus) {
574 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
575 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
578 if (OPK == OO_Star) {
579 HasDerefOp = (Method->getNumParams() == 0);
584 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
585 HasPostIncrOp && HasDerefOp;
592 return IdInfo->
getName().endswith_lower(
"end");
596 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
600 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
605 if (
const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
606 return BSE->getOpcode();
607 }
else if (
const auto *SC = dyn_cast<SymbolConjured>(SE)) {
611 if (COE->getOperator() == OO_EqualEqual) {
613 }
else if (COE->getOperator() == OO_ExclaimEqual) {
627 return LCVal->getRegion();
629 return RegionOrSymbol();
634 RegionOrSymbol RVal,
bool Equal) {
639 }
else if (!LPos && RPos) {
641 }
else if (LPos && RPos) {
649 const SVal &RVal,
bool Eq) {
654 return State->set<IteratorComparisonMap>(Condition,
655 IteratorComparison(Left, Right, Eq));
660 return State->get<IteratorComparisonMap>(Condition);
668 return CDataPtr->getEnd();
676 if (CDataPtr->getEnd()) {
679 const auto CData = CDataPtr->newEnd(Sym);
683 const auto CData = ContainerData::fromEnd(Sym);
690 return State->get<ContainerMap>(Cont);
694 const ContainerData &CData) {
695 return State->set<ContainerMap>(Cont, CData);
701 return State->get<IteratorRegionMap>(Reg);
703 return State->get<IteratorSymbolMap>(Sym);
705 return State->get<IteratorRegionMap>(LCVal->getRegion());
711 RegionOrSymbol RegOrSym) {
713 return State->get<IteratorRegionMap>(RegOrSym.get<
const MemRegion *>());
715 return State->get<IteratorSymbolMap>(RegOrSym.get<
SymbolRef>());
721 const IteratorPosition &Pos) {
723 return State->set<IteratorRegionMap>(Reg, Pos);
725 return State->set<IteratorSymbolMap>(Sym, Pos);
727 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
733 RegionOrSymbol RegOrSym,
734 const IteratorPosition &Pos) {
736 return State->set<IteratorRegionMap>(RegOrSym.get<
const MemRegion *>(),
739 return State->set<IteratorSymbolMap>(RegOrSym.get<
SymbolRef>(), Pos);
746 return State->remove<IteratorRegionMap>(Reg);
748 return State->remove<IteratorSymbolMap>(Sym);
750 return State->remove<IteratorRegionMap>(LCVal->getRegion());
756 RegionOrSymbol RegOrSym,
757 const IteratorPosition &Pos,
767 const IteratorPosition &Pos1,
768 const IteratorPosition &Pos2,
771 auto &SVB = State->getStateManager().getSValBuilder();
772 const auto comparison =
775 .getAs<DefinedSVal>();
777 return State->assume(*comparison, Equal);
784 const auto *Cont = Pos.getContainer();
792 const auto End = CData->getEnd();
794 if (isGreaterOrEqual(State, Pos.getOffset(),
End)) {
803 return compare(State, Sym1, Sym2, BO_GE);
808 auto &SMgr = State->getStateManager();
809 auto &SVB = SMgr.getSValBuilder();
811 const auto comparison =
814 .getAs<DefinedSVal>();
817 return !!State->assume(*comparison,
true);
825 #define REGISTER_CHECKER(name) \
826 void ento::register##name(CheckerManager &Mgr) { \
827 auto *checker = Mgr.registerChecker<IteratorChecker>(); \
828 checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
829 checker->CheckNames[IteratorChecker::CK_##name] = \
830 Mgr.getCurrentCheckName(); \
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
A call to an overloaded operator written using operator syntax.
const ProgramStateRef processComparison(ProgramStateRef State, RegionOrSymbol LVal, RegionOrSymbol RVal, bool Equal)
const SymExpr * getAsSymExpr() const
FunctionDecl - An instance of this class is created to represent a function declaration or definition...
StringRef getName() const
getName - Get the name of identifier for this declaration as a StringRef.
A (possibly-)qualified type.
MemRegion - The root abstract class for all memory regions.
SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont)
bool isSimpleComparisonOperator(OverloadedOperatorKind OK)
bool operator==(CanQual< T > x, CanQual< U > y)
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
method_range methods() const
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *, IteratorPosition) REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap
A helper class which wraps a boolean value set to false by default.
Expr * GetTemporaryExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue...
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
const RegionTy * getAs() const
The base class of the type hierarchy.
bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos)
Represents a prvalue temporary that is written into memory so that a reference can bind to it...
Value representing integer constant.
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs, typeofs, etc., as well as any qualifiers.
const Expr * getOriginExpr() const
Returns the expression whose value will be the result of this call.
ProgramStateRef adjustIteratorPosition(ProgramStateRef State, RegionOrSymbol RegOrSym, const IteratorPosition &Pos, bool Equal)
unsigned blockCount() const
Returns the number of times the current block has been visited along the analyzed path...
bool isLiveRegion(const MemRegion *region)
Represents a symbolic expression like 'x' + 3.
bool isEndCall(const FunctionDecl *Func)
const IteratorPosition * getIteratorPosition(ProgramStateRef State, RegionOrSymbol RegOrSym)
virtual const Expr * getArgExpr(unsigned Index) const
Returns the expression associated with a given argument.
Expr - This represents one expression.
StringRef getName() const
Return the actual identifier string.
const ProgramStateRef & getState() const
static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y)
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
bool isDereferenceOperator(OverloadedOperatorKind OK)
SymbolManager & getSymbolManager()
ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, const SymbolRef Sym)
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
const ContainerData * getContainerData(ProgramStateRef State, const MemRegion *Cont)
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
ExplodedNode * generateSink(ProgramStateRef State, ExplodedNode *Pred, const ProgramPointTag *Tag=nullptr)
Generate a sink node.
ProgramStateRef setIteratorPosition(ProgramStateRef State, RegionOrSymbol RegOrSym, const IteratorPosition &Pos)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
A class responsible for cleaning up unused symbols.
ProgramStateRef relateIteratorPositions(ProgramStateRef State, const IteratorPosition &Pos1, const IteratorPosition &Pos2, bool Equal)
const IteratorComparison * loadComparison(ProgramStateRef State, const SymExpr *Condition)
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
ASTContext & getASTContext()
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
#define REGISTER_CHECKER(name)
BinaryOperator::Opcode getOpcode(const SymExpr *SE)
Represents symbolic expression.
detail::InMemoryDirectory::const_iterator E
const MemRegion * getAsRegion() const
Represents an abstract call to a function or method along a particular path.
const SymbolConjured * conjureSymbol(const Stmt *E, const LocationContext *LCtx, QualType T, unsigned VisitCount, const void *SymbolTag=nullptr)
bool isIteratorType(const QualType &Type)
ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val)
QualType getResultType() const
Returns the result type, adjusted for references.
ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, const ContainerData &CData)
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
const ProgramStateRef saveComparison(ProgramStateRef State, const SymExpr *Condition, const SVal &LVal, const SVal &RVal, bool Eq)
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Represents a C++ struct/union/class.
const RegionOrSymbol getRegionOrSymbol(const SVal &Val)
bool operator!=(CanQual< T > x, CanQual< U > y)
virtual unsigned getNumArgs() const =0
Returns the number of arguments (explicit and implicit).
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
const SymExpr * getAsSymbolicExpression() const
getAsSymbolicExpression - If this Sval wraps a symbolic expression then return that expression...
Tag that can use a checker name as a message provider (see SimpleProgramPointTag).
SVal getReturnValue() const
Returns the return value of the call.
bool isIterator(const CXXRecordDecl *CRD)
bool isLive(SymbolRef sym)
const LocationContext * getLocationContext() const
bool isPointerType() const