69 using namespace clang;
76 struct IteratorPosition {
86 : Cont(C), Offset(Of) {}
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 {
124 static ContainerData fromEnd(
SymbolRef E) {
125 return ContainerData(E);
130 ContainerData newEnd(
SymbolRef E)
const {
return ContainerData(E); }
140 void Profile(llvm::FoldingSetNodeID &ID)
const {
149 RegionOrSymbol Left, Right;
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; }
160 return Left == X.Left && Right == X.Right && Equality == X.Equality;
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);
230 const SVal &RVal,
bool Eq);
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,
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);
548 const auto Name = CRD->
getName();
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,
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(); \ A call to an overloaded operator written using operator syntax.
const ProgramStateRef processComparison(ProgramStateRef State, RegionOrSymbol LVal, RegionOrSymbol RVal, bool Equal)
An instance of this class is created to represent a function declaration or definition.
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)
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.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
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.
const Expr * getOriginExpr() const
Returns the expression whose value will be the result of this call.
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
Expr * GetTemporaryExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue...
ProgramStateRef adjustIteratorPosition(ProgramStateRef State, RegionOrSymbol RegOrSym, const IteratorPosition &Pos, bool Equal)
virtual const Expr * getArgExpr(unsigned Index) const
Returns the expression associated with a given argument.
const SymExpr * getAsSymbolicExpression() const
getAsSymbolicExpression - If this Sval wraps a symbolic expression then return that expression...
bool isLiveRegion(const MemRegion *region)
SVal getReturnValue() const
Returns the return value of the call.
Represents a symbolic expression like 'x' + 3.
bool isEndCall(const FunctionDecl *Func)
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.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
const IteratorPosition * getIteratorPosition(ProgramStateRef State, RegionOrSymbol RegOrSym)
const RegionTy * getAs() const
Expr - This represents one expression.
static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y)
bool isDereferenceOperator(OverloadedOperatorKind OK)
SymbolManager & getSymbolManager()
ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, const SymbolRef Sym)
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
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)
const MemRegion * getAsRegion() const
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)
unsigned blockCount() const
Returns the number of times the current block has been visited along the analyzed path...
StringRef getName() const
Return the actual identifier string.
Dataflow Directional Tag Classes.
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.
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)
ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, const ContainerData &CData)
const ProgramStateRef & getState() const
const ProgramStateRef saveComparison(ProgramStateRef State, const SymExpr *Condition, const SVal &LVal, const SVal &RVal, bool Eq)
const SymExpr * getAsSymExpr() const
QualType getResultType() const
Returns the result type, adjusted for references.
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).
StringRef getName() const
getName - Get the name of identifier for this declaration as a StringRef.
bool isPointerType() const
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
Tag that can use a checker name as a message provider (see SimpleProgramPointTag).
const LocationContext * getLocationContext() const
bool isIterator(const CXXRecordDecl *CRD)
bool isLive(SymbolRef sym)
method_range methods() const