[analyzer] Add checker for iterators dereferenced beyond their range.
Patch by: Adam Balogh! Differential Revision: https://reviews.llvm.org/D25660 llvm-svn: 291430
This commit is contained in:
parent
fd18ec5885
commit
3d57457298
|
@ -278,6 +278,14 @@ def VirtualCallChecker : Checker<"VirtualCall">,
|
|||
|
||||
} // end: "optin.cplusplus"
|
||||
|
||||
let ParentPackage = CplusplusAlpha in {
|
||||
|
||||
def IteratorPastEndChecker : Checker<"IteratorPastEnd">,
|
||||
HelpText<"Check iterators used past end">,
|
||||
DescFile<"IteratorPastEndChecker.cpp">;
|
||||
|
||||
} // end: "alpha.cplusplus"
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Valist checkers.
|
||||
|
|
|
@ -39,6 +39,7 @@ add_clang_library(clangStaticAnalyzerCheckers
|
|||
GenericTaintChecker.cpp
|
||||
GTestChecker.cpp
|
||||
IdenticalExprChecker.cpp
|
||||
IteratorPastEndChecker.cpp
|
||||
IvarInvalidationChecker.cpp
|
||||
LLVMConventionsChecker.cpp
|
||||
LocalizationChecker.cpp
|
||||
|
|
|
@ -0,0 +1,842 @@
|
|||
//===-- IteratorPastEndChecker.cpp --------------------------------*- C++ -*--//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Defines a checker for using iterators outside their range (past end). Usage
|
||||
// means here dereferencing, incrementing etc.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ClangSACheckers.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
||||
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace clang;
|
||||
using namespace ento;
|
||||
|
||||
namespace {
|
||||
struct IteratorPosition {
|
||||
private:
|
||||
enum Kind { InRange, OutofRange } K;
|
||||
IteratorPosition(Kind InK) : K(InK) {}
|
||||
|
||||
public:
|
||||
bool isInRange() const { return K == InRange; }
|
||||
bool isOutofRange() const { return K == OutofRange; }
|
||||
|
||||
static IteratorPosition getInRange() { return IteratorPosition(InRange); }
|
||||
static IteratorPosition getOutofRange() {
|
||||
return IteratorPosition(OutofRange);
|
||||
}
|
||||
|
||||
bool operator==(const IteratorPosition &X) const { return K == X.K; }
|
||||
bool operator!=(const IteratorPosition &X) const { return K != X.K; }
|
||||
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
|
||||
};
|
||||
|
||||
typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
|
||||
|
||||
struct IteratorComparison {
|
||||
private:
|
||||
RegionOrSymbol Left, Right;
|
||||
bool Equality;
|
||||
|
||||
public:
|
||||
IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
|
||||
: Left(L), Right(R), Equality(Eq) {}
|
||||
|
||||
RegionOrSymbol getLeft() const { return Left; }
|
||||
RegionOrSymbol getRight() const { return Right; }
|
||||
bool isEquality() const { return Equality; }
|
||||
bool operator==(const IteratorComparison &X) const {
|
||||
return Left == X.Left && Right == X.Right && Equality == X.Equality;
|
||||
}
|
||||
bool operator!=(const IteratorComparison &X) const {
|
||||
return Left != X.Left || Right != X.Right || Equality != X.Equality;
|
||||
}
|
||||
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
|
||||
};
|
||||
|
||||
class IteratorPastEndChecker
|
||||
: public Checker<
|
||||
check::PreCall, check::PostCall, check::PreStmt<CXXOperatorCallExpr>,
|
||||
check::PostStmt<CXXConstructExpr>, check::PostStmt<DeclStmt>,
|
||||
check::PostStmt<MaterializeTemporaryExpr>, check::BeginFunction,
|
||||
check::DeadSymbols, eval::Assume, eval::Call> {
|
||||
mutable IdentifierInfo *II_find = nullptr,
|
||||
*II_find_end = nullptr, *II_find_first_of = nullptr,
|
||||
*II_find_if = nullptr, *II_find_if_not = nullptr,
|
||||
*II_lower_bound = nullptr, *II_upper_bound = nullptr,
|
||||
*II_search = nullptr, *II_search_n = nullptr;
|
||||
|
||||
std::unique_ptr<BugType> PastEndBugType;
|
||||
|
||||
void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
|
||||
const SVal &RVal, OverloadedOperatorKind Op) const;
|
||||
void handleAccess(CheckerContext &C, const SVal &Val) const;
|
||||
void handleDecrement(CheckerContext &C, const SVal &Val) const;
|
||||
void handleEnd(CheckerContext &C, const SVal &RetVal) const;
|
||||
|
||||
bool evalFind(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalFindEnd(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalFindFirstOf(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalFindIf(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalFindIfNot(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalLowerBound(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalUpperBound(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalSearch(CheckerContext &C, const CallExpr *CE) const;
|
||||
bool evalSearchN(CheckerContext &C, const CallExpr *CE) const;
|
||||
void Find(CheckerContext &C, const CallExpr *CE) const;
|
||||
|
||||
void reportPastEndBug(const StringRef &Message, const SVal &Val,
|
||||
CheckerContext &C, ExplodedNode *ErrNode) const;
|
||||
void initIdentifiers(ASTContext &Ctx) const;
|
||||
|
||||
public:
|
||||
IteratorPastEndChecker();
|
||||
|
||||
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
|
||||
void checkBeginFunction(CheckerContext &C) const;
|
||||
void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
|
||||
void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
|
||||
void checkPostStmt(const MaterializeTemporaryExpr *MTE,
|
||||
CheckerContext &C) const;
|
||||
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
|
||||
ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
|
||||
bool Assumption) const;
|
||||
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
|
||||
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
|
||||
IteratorPosition)
|
||||
|
||||
REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
|
||||
IteratorComparison)
|
||||
|
||||
#define INIT_ID(Id) \
|
||||
if (!II_##Id) \
|
||||
II_##Id = &Ctx.Idents.get(#Id)
|
||||
|
||||
namespace {
|
||||
|
||||
bool isIteratorType(const QualType &Type);
|
||||
bool isIterator(const CXXRecordDecl *CRD);
|
||||
bool isEndCall(const FunctionDecl *Func);
|
||||
bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
|
||||
bool isAccessOperator(OverloadedOperatorKind OK);
|
||||
bool isDecrementOperator(OverloadedOperatorKind OK);
|
||||
BinaryOperator::Opcode getOpcode(const SymExpr *SE);
|
||||
const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
|
||||
const ProgramStateRef processComparison(ProgramStateRef State,
|
||||
RegionOrSymbol LVal,
|
||||
RegionOrSymbol RVal, bool Equal);
|
||||
const ProgramStateRef saveComparison(ProgramStateRef State,
|
||||
const SymExpr *Condition, const SVal &LVal,
|
||||
const SVal &RVal, bool Eq);
|
||||
const IteratorComparison *loadComparison(ProgramStateRef State,
|
||||
const SymExpr *Condition);
|
||||
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
||||
const SVal &Val);
|
||||
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym);
|
||||
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
|
||||
IteratorPosition Pos);
|
||||
ProgramStateRef setIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym,
|
||||
IteratorPosition Pos);
|
||||
ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym,
|
||||
IteratorPosition Pos, bool Equal);
|
||||
bool contradictingIteratorPositions(IteratorPosition Pos1,
|
||||
IteratorPosition Pos2, bool Equal);
|
||||
}
|
||||
|
||||
IteratorPastEndChecker::IteratorPastEndChecker() {
|
||||
PastEndBugType.reset(
|
||||
new BugType(this, "Iterator Past End", "Misuse of STL APIs"));
|
||||
PastEndBugType->setSuppressOnSink(true);
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPreCall(const CallEvent &Call,
|
||||
CheckerContext &C) const {
|
||||
// Check for access past end
|
||||
const auto *Func = Call.getDecl()->getAsFunction();
|
||||
if (!Func)
|
||||
return;
|
||||
if (Func->isOverloadedOperator()) {
|
||||
if (isAccessOperator(Func->getOverloadedOperator())) {
|
||||
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
|
||||
handleAccess(C, InstCall->getCXXThisVal());
|
||||
} else {
|
||||
handleAccess(C, Call.getArgSVal(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPostCall(const CallEvent &Call,
|
||||
CheckerContext &C) const {
|
||||
// Record end() iterators, iterator decrementation and comparison
|
||||
const auto *Func = Call.getDecl()->getAsFunction();
|
||||
if (!Func)
|
||||
return;
|
||||
if (Func->isOverloadedOperator()) {
|
||||
const auto Op = Func->getOverloadedOperator();
|
||||
if (isSimpleComparisonOperator(Op)) {
|
||||
if (Func->isCXXInstanceMember()) {
|
||||
const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
|
||||
handleComparison(C, InstCall.getReturnValue(), InstCall.getCXXThisVal(),
|
||||
InstCall.getArgSVal(0), Op);
|
||||
} else {
|
||||
handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
|
||||
Call.getArgSVal(1), Op);
|
||||
}
|
||||
} else if (isDecrementOperator(Func->getOverloadedOperator())) {
|
||||
if (Func->isCXXInstanceMember()) {
|
||||
const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
|
||||
handleDecrement(C, InstCall.getCXXThisVal());
|
||||
} else {
|
||||
handleDecrement(C, Call.getArgSVal(0));
|
||||
}
|
||||
}
|
||||
} else if (Func->isCXXInstanceMember()) {
|
||||
if (!isEndCall(Func))
|
||||
return;
|
||||
if (!isIteratorType(Call.getResultType()))
|
||||
return;
|
||||
handleEnd(C, Call.getReturnValue());
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
|
||||
CheckerContext &C) const {
|
||||
const auto *ThisExpr = COCE->getArg(0);
|
||||
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getPredecessor()->getLocationContext();
|
||||
|
||||
const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
|
||||
if (const auto *Reg = CurrentThis.getAsRegion()) {
|
||||
if (!Reg->getAs<CXXTempObjectRegion>())
|
||||
return;
|
||||
const auto OldState = C.getPredecessor()->getFirstPred()->getState();
|
||||
const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
|
||||
const auto *Pos = getIteratorPosition(OldState, OldThis);
|
||||
if (!Pos)
|
||||
return;
|
||||
State = setIteratorPosition(State, CurrentThis, *Pos);
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkBeginFunction(CheckerContext &C) const {
|
||||
// Copy state of iterator arguments to iterator parameters
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getLocationContext();
|
||||
|
||||
const auto *Site = cast<StackFrameContext>(LCtx)->getCallSite();
|
||||
if (!Site)
|
||||
return;
|
||||
|
||||
const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
|
||||
if (!FD)
|
||||
return;
|
||||
|
||||
const auto *CE = dyn_cast<CallExpr>(Site);
|
||||
if (!CE)
|
||||
return;
|
||||
|
||||
bool Change = false;
|
||||
int idx = 0;
|
||||
for (const auto P : FD->parameters()) {
|
||||
auto Param = State->getLValue(P, LCtx);
|
||||
auto Arg = State->getSVal(CE->getArg(idx++), LCtx->getParent());
|
||||
const auto *Pos = getIteratorPosition(State, Arg);
|
||||
if (!Pos)
|
||||
continue;
|
||||
State = setIteratorPosition(State, Param, *Pos);
|
||||
Change = true;
|
||||
}
|
||||
if (Change) {
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPostStmt(const CXXConstructExpr *CCE,
|
||||
CheckerContext &C) const {
|
||||
// Transfer iterator state in case of copy or move by constructor
|
||||
const auto *ctr = CCE->getConstructor();
|
||||
if (!ctr->isCopyOrMoveConstructor())
|
||||
return;
|
||||
const auto *RHSExpr = CCE->getArg(0);
|
||||
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getLocationContext();
|
||||
|
||||
const auto RetVal = State->getSVal(CCE, LCtx);
|
||||
|
||||
const auto RHSVal = State->getSVal(RHSExpr, LCtx);
|
||||
const auto *RHSPos = getIteratorPosition(State, RHSVal);
|
||||
if (!RHSPos)
|
||||
return;
|
||||
State = setIteratorPosition(State, RetVal, *RHSPos);
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPostStmt(const DeclStmt *DS,
|
||||
CheckerContext &C) const {
|
||||
// Transfer iterator state to new variable declaration
|
||||
for (const auto *D : DS->decls()) {
|
||||
const auto *VD = dyn_cast<VarDecl>(D);
|
||||
if (!VD || !VD->hasInit())
|
||||
continue;
|
||||
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getPredecessor()->getLocationContext();
|
||||
const auto *Pos =
|
||||
getIteratorPosition(State, State->getSVal(VD->getInit(), LCtx));
|
||||
if (!Pos)
|
||||
continue;
|
||||
State = setIteratorPosition(State, State->getLValue(VD, LCtx), *Pos);
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
|
||||
CheckerContext &C) const {
|
||||
/* Transfer iterator state for to temporary objects */
|
||||
auto State = C.getState();
|
||||
const auto *LCtx = C.getPredecessor()->getLocationContext();
|
||||
const auto *Pos =
|
||||
getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
|
||||
if (!Pos)
|
||||
return;
|
||||
State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::checkDeadSymbols(SymbolReaper &SR,
|
||||
CheckerContext &C) const {
|
||||
auto State = C.getState();
|
||||
|
||||
auto RegionMap = State->get<IteratorRegionMap>();
|
||||
for (const auto Reg : RegionMap) {
|
||||
if (!SR.isLiveRegion(Reg.first)) {
|
||||
State = State->remove<IteratorRegionMap>(Reg.first);
|
||||
}
|
||||
}
|
||||
|
||||
auto SymbolMap = State->get<IteratorSymbolMap>();
|
||||
for (const auto Sym : SymbolMap) {
|
||||
if (SR.isDead(Sym.first)) {
|
||||
State = State->remove<IteratorSymbolMap>(Sym.first);
|
||||
}
|
||||
}
|
||||
|
||||
auto ComparisonMap = State->get<IteratorComparisonMap>();
|
||||
for (const auto Comp : ComparisonMap) {
|
||||
if (SR.isDead(Comp.first)) {
|
||||
State = State->remove<IteratorComparisonMap>(Comp.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProgramStateRef IteratorPastEndChecker::evalAssume(ProgramStateRef State,
|
||||
SVal Cond,
|
||||
bool Assumption) const {
|
||||
// Load recorded comparison and transfer iterator state between sides
|
||||
// according to comparison operator and assumption
|
||||
const auto *SE = Cond.getAsSymExpr();
|
||||
if (!SE)
|
||||
return State;
|
||||
|
||||
auto Opc = getOpcode(SE);
|
||||
if (Opc != BO_EQ && Opc != BO_NE)
|
||||
return State;
|
||||
|
||||
bool Negated = false;
|
||||
const auto *Comp = loadComparison(State, SE);
|
||||
if (!Comp) {
|
||||
// Try negated comparison, which is a SymExpr to 0 integer comparison
|
||||
const auto *SIE = dyn_cast<SymIntExpr>(SE);
|
||||
if (!SIE)
|
||||
return State;
|
||||
|
||||
if (SIE->getRHS() != 0)
|
||||
return State;
|
||||
|
||||
SE = SIE->getLHS();
|
||||
Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
|
||||
Opc = getOpcode(SE);
|
||||
if (Opc != BO_EQ && Opc != BO_NE)
|
||||
return State;
|
||||
|
||||
Comp = loadComparison(State, SE);
|
||||
if (!Comp)
|
||||
return State;
|
||||
}
|
||||
|
||||
return processComparison(State, Comp->getLeft(), Comp->getRight(),
|
||||
(Comp->isEquality() == Assumption) != Negated);
|
||||
}
|
||||
|
||||
// FIXME: Evaluation of these STL calls should be moved to StdCLibraryFunctions
|
||||
// checker (see patch r284960) or another similar checker for C++ STL
|
||||
// functions (e.g. StdCXXLibraryFunctions or StdCppLibraryFunctions).
|
||||
bool IteratorPastEndChecker::evalCall(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
const FunctionDecl *FD = C.getCalleeDecl(CE);
|
||||
if (!FD)
|
||||
return false;
|
||||
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
initIdentifiers(Ctx);
|
||||
|
||||
if (FD->getKind() == Decl::Function) {
|
||||
if (FD->isInStdNamespace()) {
|
||||
if (FD->getIdentifier() == II_find) {
|
||||
return evalFind(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_end) {
|
||||
return evalFindEnd(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_first_of) {
|
||||
return evalFindFirstOf(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_if) {
|
||||
return evalFindIf(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_if) {
|
||||
return evalFindIf(C, CE);
|
||||
} else if (FD->getIdentifier() == II_find_if_not) {
|
||||
return evalFindIfNot(C, CE);
|
||||
} else if (FD->getIdentifier() == II_upper_bound) {
|
||||
return evalUpperBound(C, CE);
|
||||
} else if (FD->getIdentifier() == II_lower_bound) {
|
||||
return evalLowerBound(C, CE);
|
||||
} else if (FD->getIdentifier() == II_search) {
|
||||
return evalSearch(C, CE);
|
||||
} else if (FD->getIdentifier() == II_search_n) {
|
||||
return evalSearchN(C, CE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::handleComparison(CheckerContext &C,
|
||||
const SVal &RetVal,
|
||||
const SVal &LVal,
|
||||
const SVal &RVal,
|
||||
OverloadedOperatorKind Op) const {
|
||||
// Record the operands and the operator of the comparison for the next
|
||||
// evalAssume, if the result is a symbolic expression. If it is a concrete
|
||||
// value (only one branch is possible), then transfer the state between
|
||||
// the operands according to the operator and the result
|
||||
auto State = C.getState();
|
||||
if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
|
||||
const auto *LPos = getIteratorPosition(State, LVal);
|
||||
const auto *RPos = getIteratorPosition(State, RVal);
|
||||
if (!LPos && !RPos)
|
||||
return;
|
||||
State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
|
||||
C.addTransition(State);
|
||||
} else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
|
||||
if ((State = processComparison(
|
||||
State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
|
||||
(Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
|
||||
C.addTransition(State);
|
||||
} else {
|
||||
C.generateSink(State, C.getPredecessor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::handleAccess(CheckerContext &C,
|
||||
const SVal &Val) const {
|
||||
auto State = C.getState();
|
||||
const auto *Pos = getIteratorPosition(State, Val);
|
||||
if (Pos && Pos->isOutofRange()) {
|
||||
auto *N = C.generateNonFatalErrorNode(State);
|
||||
if (!N) {
|
||||
return;
|
||||
}
|
||||
reportPastEndBug("Iterator accessed past its end.", Val, C, N);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::handleDecrement(CheckerContext &C,
|
||||
const SVal &Val) const {
|
||||
auto State = C.getState();
|
||||
const auto *Pos = getIteratorPosition(State, Val);
|
||||
if (Pos && Pos->isOutofRange()) {
|
||||
State = setIteratorPosition(State, Val, IteratorPosition::getInRange());
|
||||
// FIXME: We could also check for iterators ahead of their beginnig in the
|
||||
// future, but currently we do not care for such errors. We also
|
||||
// assume that the iterator is not past its end by more then one
|
||||
// position.
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::handleEnd(CheckerContext &C,
|
||||
const SVal &RetVal) const {
|
||||
auto State = C.getState();
|
||||
State = setIteratorPosition(State, RetVal, IteratorPosition::getOutofRange());
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFind(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFindEnd(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType()) &&
|
||||
isIteratorType(CE->getArg(2)->getType()) &&
|
||||
isIteratorType(CE->getArg(3)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFindFirstOf(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType()) &&
|
||||
isIteratorType(CE->getArg(2)->getType()) &&
|
||||
isIteratorType(CE->getArg(3)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFindIf(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalFindIfNot(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalLowerBound(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalUpperBound(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalSearch(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType()) &&
|
||||
isIteratorType(CE->getArg(2)->getType()) &&
|
||||
isIteratorType(CE->getArg(3)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IteratorPastEndChecker::evalSearchN(CheckerContext &C,
|
||||
const CallExpr *CE) const {
|
||||
if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
|
||||
isIteratorType(CE->getArg(0)->getType()) &&
|
||||
isIteratorType(CE->getArg(1)->getType())) {
|
||||
Find(C, CE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::Find(CheckerContext &C, const CallExpr *CE) const {
|
||||
auto state = C.getState();
|
||||
auto &svalBuilder = C.getSValBuilder();
|
||||
const auto *LCtx = C.getLocationContext();
|
||||
|
||||
auto RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
|
||||
auto SecondParam = state->getSVal(CE->getArg(1), LCtx);
|
||||
|
||||
auto stateFound = state->BindExpr(CE, LCtx, RetVal);
|
||||
auto stateNotFound = state->BindExpr(CE, LCtx, SecondParam);
|
||||
|
||||
C.addTransition(stateFound);
|
||||
C.addTransition(stateNotFound);
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::reportPastEndBug(const StringRef &Message,
|
||||
const SVal &Val,
|
||||
CheckerContext &C,
|
||||
ExplodedNode *ErrNode) const {
|
||||
auto R = llvm::make_unique<BugReport>(*PastEndBugType, Message, ErrNode);
|
||||
R->markInteresting(Val);
|
||||
C.emitReport(std::move(R));
|
||||
}
|
||||
|
||||
void IteratorPastEndChecker::initIdentifiers(ASTContext &Ctx) const {
|
||||
INIT_ID(find);
|
||||
INIT_ID(find_end);
|
||||
INIT_ID(find_first_of);
|
||||
INIT_ID(find_if);
|
||||
INIT_ID(find_if_not);
|
||||
INIT_ID(lower_bound);
|
||||
INIT_ID(upper_bound);
|
||||
INIT_ID(search);
|
||||
INIT_ID(search_n);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool isIteratorType(const QualType &Type) {
|
||||
if (Type->isPointerType())
|
||||
return true;
|
||||
|
||||
const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
|
||||
return isIterator(CRD);
|
||||
}
|
||||
|
||||
bool isIterator(const CXXRecordDecl *CRD) {
|
||||
if (!CRD)
|
||||
return false;
|
||||
|
||||
const auto Name = CRD->getName();
|
||||
if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
|
||||
Name.endswith_lower("it")))
|
||||
return false;
|
||||
|
||||
bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
|
||||
HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
|
||||
for (const auto *Method : CRD->methods()) {
|
||||
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
|
||||
if (Ctor->isCopyConstructor()) {
|
||||
HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
|
||||
HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
|
||||
continue;
|
||||
}
|
||||
if (Method->isCopyAssignmentOperator()) {
|
||||
HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
|
||||
continue;
|
||||
}
|
||||
if (!Method->isOverloadedOperator())
|
||||
continue;
|
||||
const auto OPK = Method->getOverloadedOperator();
|
||||
if (OPK == OO_PlusPlus) {
|
||||
HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
|
||||
HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
|
||||
continue;
|
||||
}
|
||||
if (OPK == OO_Star) {
|
||||
HasDerefOp = (Method->getNumParams() == 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
|
||||
HasPostIncrOp && HasDerefOp;
|
||||
}
|
||||
|
||||
bool isEndCall(const FunctionDecl *Func) {
|
||||
const auto *IdInfo = Func->getIdentifier();
|
||||
if (!IdInfo)
|
||||
return false;
|
||||
return IdInfo->getName().endswith_lower("end");
|
||||
}
|
||||
|
||||
bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
|
||||
return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
|
||||
}
|
||||
|
||||
bool isAccessOperator(OverloadedOperatorKind OK) {
|
||||
return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
|
||||
OK == OO_Plus || OK == OO_PlusEqual || OK == OO_PlusPlus ||
|
||||
OK == OO_Subscript;
|
||||
}
|
||||
|
||||
bool isDecrementOperator(OverloadedOperatorKind OK) {
|
||||
return OK == OO_MinusEqual || OK == OO_MinusMinus;
|
||||
}
|
||||
|
||||
BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
|
||||
if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
|
||||
return BSE->getOpcode();
|
||||
} else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
|
||||
const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
|
||||
if (!COE)
|
||||
return BO_Comma; // Extremal value, neither EQ nor NE
|
||||
if (COE->getOperator() == OO_EqualEqual) {
|
||||
return BO_EQ;
|
||||
} else if (COE->getOperator() == OO_ExclaimEqual) {
|
||||
return BO_NE;
|
||||
}
|
||||
return BO_Comma; // Extremal value, neither EQ nor NE
|
||||
}
|
||||
return BO_Comma; // Extremal value, neither EQ nor NE
|
||||
}
|
||||
|
||||
const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
|
||||
if (const auto Reg = Val.getAsRegion()) {
|
||||
return Reg;
|
||||
} else if (const auto Sym = Val.getAsSymbol()) {
|
||||
return Sym;
|
||||
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
|
||||
return LCVal->getRegion();
|
||||
}
|
||||
return RegionOrSymbol();
|
||||
}
|
||||
|
||||
const ProgramStateRef processComparison(ProgramStateRef State,
|
||||
RegionOrSymbol LVal,
|
||||
RegionOrSymbol RVal, bool Equal) {
|
||||
const auto *LPos = getIteratorPosition(State, LVal);
|
||||
const auto *RPos = getIteratorPosition(State, RVal);
|
||||
if (LPos && !RPos) {
|
||||
State = adjustIteratorPosition(State, RVal, *LPos, Equal);
|
||||
} else if (!LPos && RPos) {
|
||||
State = adjustIteratorPosition(State, LVal, *RPos, Equal);
|
||||
} else if (LPos && RPos) {
|
||||
if (contradictingIteratorPositions(*LPos, *RPos, Equal)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return State;
|
||||
}
|
||||
|
||||
const ProgramStateRef saveComparison(ProgramStateRef State,
|
||||
const SymExpr *Condition, const SVal &LVal,
|
||||
const SVal &RVal, bool Eq) {
|
||||
const auto Left = getRegionOrSymbol(LVal);
|
||||
const auto Right = getRegionOrSymbol(RVal);
|
||||
if (!Left || !Right)
|
||||
return State;
|
||||
return State->set<IteratorComparisonMap>(Condition,
|
||||
IteratorComparison(Left, Right, Eq));
|
||||
}
|
||||
|
||||
const IteratorComparison *loadComparison(ProgramStateRef State,
|
||||
const SymExpr *Condition) {
|
||||
return State->get<IteratorComparisonMap>(Condition);
|
||||
}
|
||||
|
||||
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
||||
const SVal &Val) {
|
||||
if (const auto Reg = Val.getAsRegion()) {
|
||||
return State->get<IteratorRegionMap>(Reg);
|
||||
} else if (const auto Sym = Val.getAsSymbol()) {
|
||||
return State->get<IteratorSymbolMap>(Sym);
|
||||
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
|
||||
return State->get<IteratorRegionMap>(LCVal->getRegion());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym) {
|
||||
if (RegOrSym.is<const MemRegion *>()) {
|
||||
return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
|
||||
} else if (RegOrSym.is<SymbolRef>()) {
|
||||
return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
|
||||
IteratorPosition Pos) {
|
||||
if (const auto Reg = Val.getAsRegion()) {
|
||||
return State->set<IteratorRegionMap>(Reg, Pos);
|
||||
} else if (const auto Sym = Val.getAsSymbol()) {
|
||||
return State->set<IteratorSymbolMap>(Sym, Pos);
|
||||
} else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
|
||||
return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProgramStateRef setIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym,
|
||||
IteratorPosition Pos) {
|
||||
if (RegOrSym.is<const MemRegion *>()) {
|
||||
return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
|
||||
Pos);
|
||||
} else if (RegOrSym.is<SymbolRef>()) {
|
||||
return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
|
||||
RegionOrSymbol RegOrSym,
|
||||
IteratorPosition Pos, bool Equal) {
|
||||
|
||||
if ((Pos.isInRange() && Equal) || (Pos.isOutofRange() && !Equal)) {
|
||||
return setIteratorPosition(State, RegOrSym, IteratorPosition::getInRange());
|
||||
} else if (Pos.isOutofRange() && Equal) {
|
||||
return setIteratorPosition(State, RegOrSym,
|
||||
IteratorPosition::getOutofRange());
|
||||
} else {
|
||||
return State;
|
||||
}
|
||||
}
|
||||
|
||||
bool contradictingIteratorPositions(IteratorPosition Pos1,
|
||||
IteratorPosition Pos2, bool Equal) {
|
||||
return ((Pos1 != Pos2) && Equal) ||
|
||||
((Pos1.isOutofRange() && Pos2.isOutofRange()) && !Equal);
|
||||
}
|
||||
}
|
||||
|
||||
void ento::registerIteratorPastEndChecker(CheckerManager &Mgr) {
|
||||
Mgr.registerChecker<IteratorPastEndChecker>();
|
||||
}
|
|
@ -1248,7 +1248,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
|||
case Expr::MaterializeTemporaryExprClass: {
|
||||
Bldr.takeNodes(Pred);
|
||||
const MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S);
|
||||
CreateCXXTemporaryObject(MTE, Pred, Dst);
|
||||
ExplodedNodeSet dstPrevisit;
|
||||
getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this);
|
||||
ExplodedNodeSet dstExpr;
|
||||
for (ExplodedNodeSet::iterator i = dstPrevisit.begin(),
|
||||
e = dstPrevisit.end(); i != e ; ++i) {
|
||||
CreateCXXTemporaryObject(MTE, *i, dstExpr);
|
||||
}
|
||||
getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this);
|
||||
Bldr.addNodes(Dst);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,29 @@ typedef unsigned char uint8_t;
|
|||
typedef __typeof__(sizeof(int)) size_t;
|
||||
void *memmove(void *s1, const void *s2, size_t n);
|
||||
|
||||
template <typename T, typename Ptr, typename Ref> struct __iterator {
|
||||
typedef __iterator<T, T *, T &> iterator;
|
||||
typedef __iterator<T, const T *, const T &> const_iterator;
|
||||
|
||||
__iterator(const Ptr p) : ptr(p) {}
|
||||
|
||||
__iterator<T, Ptr, Ref> operator++() { return *this; }
|
||||
__iterator<T, Ptr, Ref> operator++(int) { return *this; }
|
||||
__iterator<T, Ptr, Ref> operator--() { return *this; }
|
||||
__iterator<T, Ptr, Ref> operator--(int) { return *this; }
|
||||
Ref operator*() const { return *ptr; }
|
||||
Ptr operator->() const { return *ptr; }
|
||||
|
||||
bool operator==(const iterator &rhs) const { return ptr == rhs.ptr; }
|
||||
bool operator==(const const_iterator &rhs) const { return ptr == rhs.ptr; }
|
||||
|
||||
bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; }
|
||||
bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; }
|
||||
|
||||
private:
|
||||
Ptr ptr;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <class T1, class T2>
|
||||
struct pair {
|
||||
|
@ -27,6 +50,9 @@ namespace std {
|
|||
|
||||
template<typename T>
|
||||
class vector {
|
||||
typedef __iterator<T, T *, T &> iterator;
|
||||
typedef __iterator<T, const T *, const T &> const_iterator;
|
||||
|
||||
T *_start;
|
||||
T *_finish;
|
||||
T *_end_of_storage;
|
||||
|
@ -49,11 +75,10 @@ namespace std {
|
|||
return _start[n];
|
||||
}
|
||||
|
||||
T *begin() { return _start; }
|
||||
const T *begin() const { return _start; }
|
||||
|
||||
T *end() { return _finish; }
|
||||
const T *end() const { return _finish; }
|
||||
iterator begin() { return iterator(_start); }
|
||||
const_iterator begin() const { return const_iterator(_start); }
|
||||
iterator end() { return iterator(_finish); }
|
||||
const_iterator end() const { return const_iterator(_finish); }
|
||||
};
|
||||
|
||||
class exception {
|
||||
|
@ -223,6 +248,35 @@ namespace std {
|
|||
return __copy_backward(II, IE, OI);
|
||||
}
|
||||
|
||||
template <class InputIterator, class T>
|
||||
InputIterator find(InputIterator first, InputIterator last, const T &val);
|
||||
template <class ForwardIterator1, class ForwardIterator2>
|
||||
ForwardIterator1 find_end(ForwardIterator1 first1, ForwardIterator1 last1,
|
||||
ForwardIterator2 first2, ForwardIterator2 last2);
|
||||
template <class ForwardIterator1, class ForwardIterator2>
|
||||
ForwardIterator1 find_first_of(ForwardIterator1 first1,
|
||||
ForwardIterator1 last1,
|
||||
ForwardIterator2 first2,
|
||||
ForwardIterator2 last2);
|
||||
template <class InputIterator, class UnaryPredicate>
|
||||
InputIterator find_if(InputIterator first, InputIterator last,
|
||||
UnaryPredicate pred);
|
||||
template <class InputIterator, class UnaryPredicate>
|
||||
InputIterator find_if_not(InputIterator first, InputIterator last,
|
||||
UnaryPredicate pred);
|
||||
template <class InputIterator, class T>
|
||||
InputIterator lower_bound(InputIterator first, InputIterator last,
|
||||
const T &val);
|
||||
template <class InputIterator, class T>
|
||||
InputIterator upper_bound(InputIterator first, InputIterator last,
|
||||
const T &val);
|
||||
template <class ForwardIterator1, class ForwardIterator2>
|
||||
ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1,
|
||||
ForwardIterator2 first2, ForwardIterator2 last2);
|
||||
template <class ForwardIterator1, class ForwardIterator2>
|
||||
ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1,
|
||||
ForwardIterator2 first2, ForwardIterator2 last2);
|
||||
|
||||
struct input_iterator_tag { };
|
||||
struct output_iterator_tag { };
|
||||
struct forward_iterator_tag : public input_iterator_tag { };
|
||||
|
|
|
@ -18,6 +18,6 @@ class C {
|
|||
void testCopyNull(C *I, C *E) {
|
||||
std::copy(I, E, (C *)0);
|
||||
#ifndef SUPPRESSED
|
||||
// expected-warning@../Inputs/system-header-simulator-cxx.h:166 {{Called C++ object pointer is null}}
|
||||
// expected-warning@../Inputs/system-header-simulator-cxx.h:191 {{Called C++ object pointer is null}}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
void clang_analyzer_eval(bool);
|
||||
|
||||
void testVector(std::vector<int> &nums) {
|
||||
if (nums.begin()) return;
|
||||
if (nums.end()) return;
|
||||
if (nums.begin() != nums.end()) return;
|
||||
|
||||
clang_analyzer_eval(nums.size() == 0);
|
||||
#if INLINE
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -analyze -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=false %s -verify
|
||||
// RUN: %clang_cc1 -std=c++11 -analyze -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
|
||||
|
||||
#include "Inputs/system-header-simulator-cxx.h"
|
||||
|
||||
void simple_good(const std::vector<int> &v) {
|
||||
auto i = v.end();
|
||||
if (i != v.end())
|
||||
*i; // no-warning
|
||||
}
|
||||
|
||||
void simple_good_negated(const std::vector<int> &v) {
|
||||
auto i = v.end();
|
||||
if (!(i == v.end()))
|
||||
*i; // no-warning
|
||||
}
|
||||
|
||||
void simple_bad(const std::vector<int> &v) {
|
||||
auto i = v.end();
|
||||
*i; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void copy(const std::vector<int> &v) {
|
||||
auto i1 = v.end();
|
||||
auto i2 = i1;
|
||||
*i2; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void decrease(const std::vector<int> &v) {
|
||||
auto i = v.end();
|
||||
--i;
|
||||
*i; // no-warning
|
||||
}
|
||||
|
||||
void copy_and_decrease1(const std::vector<int> &v) {
|
||||
auto i1 = v.end();
|
||||
auto i2 = i1;
|
||||
--i1;
|
||||
*i1; // no-warning
|
||||
}
|
||||
|
||||
void copy_and_decrease2(const std::vector<int> &v) {
|
||||
auto i1 = v.end();
|
||||
auto i2 = i1;
|
||||
--i1;
|
||||
*i2; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void copy_and_increase1(const std::vector<int> &v) {
|
||||
auto i1 = v.begin();
|
||||
auto i2 = i1;
|
||||
++i1;
|
||||
if (i1 == v.end())
|
||||
*i2; // no-warning
|
||||
}
|
||||
|
||||
void copy_and_increase2(const std::vector<int> &v) {
|
||||
auto i1 = v.begin();
|
||||
auto i2 = i1;
|
||||
++i1;
|
||||
if (i2 == v.end())
|
||||
*i2; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_find(std::vector<int> &vec, int e) {
|
||||
auto first = std::find(vec.begin(), vec.end(), e);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_find(std::vector<int> &vec, int e) {
|
||||
auto first = std::find(vec.begin(), vec.end(), e);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_find_end(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
if (vec.end() != last)
|
||||
*last; // no-warning
|
||||
}
|
||||
|
||||
void bad_find_end(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
*last; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_find_first_of(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto first =
|
||||
std::find_first_of(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_find_first_of(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto first = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
bool odd(int i) { return i % 2; }
|
||||
|
||||
void good_find_if(std::vector<int> &vec) {
|
||||
auto first = std::find_if(vec.begin(), vec.end(), odd);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_find_if(std::vector<int> &vec, int e) {
|
||||
auto first = std::find_if(vec.begin(), vec.end(), odd);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_find_if_not(std::vector<int> &vec) {
|
||||
auto first = std::find_if_not(vec.begin(), vec.end(), odd);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_find_if_not(std::vector<int> &vec, int e) {
|
||||
auto first = std::find_if_not(vec.begin(), vec.end(), odd);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_lower_bound(std::vector<int> &vec, int e) {
|
||||
auto first = std::lower_bound(vec.begin(), vec.end(), e);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_lower_bound(std::vector<int> &vec, int e) {
|
||||
auto first = std::lower_bound(vec.begin(), vec.end(), e);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_upper_bound(std::vector<int> &vec, int e) {
|
||||
auto last = std::lower_bound(vec.begin(), vec.end(), e);
|
||||
if (vec.end() != last)
|
||||
*last; // no-warning
|
||||
}
|
||||
|
||||
void bad_upper_bound(std::vector<int> &vec, int e) {
|
||||
auto last = std::lower_bound(vec.begin(), vec.end(), e);
|
||||
*last; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_search(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_search(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void good_search_n(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
if (vec.end() != nth)
|
||||
*nth; // no-warning
|
||||
}
|
||||
|
||||
void bad_search_n(std::vector<int> &vec, std::vector<int> &seq) {
|
||||
auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end());
|
||||
*nth; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
template <class InputIterator, class T>
|
||||
InputIterator nonStdFind(InputIterator first, InputIterator last,
|
||||
const T &val) {
|
||||
for (auto i = first; i != last; ++i) {
|
||||
if (*i == val) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
void good_non_std_find(std::vector<int> &vec, int e) {
|
||||
auto first = nonStdFind(vec.begin(), vec.end(), e);
|
||||
if (vec.end() != first)
|
||||
*first; // no-warning
|
||||
}
|
||||
|
||||
void bad_non_std_find(std::vector<int> &vec, int e) {
|
||||
auto first = nonStdFind(vec.begin(), vec.end(), e);
|
||||
*first; // expected-warning{{Iterator accessed past its end}}
|
||||
}
|
||||
|
||||
void tricky(std::vector<int> &vec, int e) {
|
||||
const auto first = vec.begin();
|
||||
const auto comp1 = (first != vec.end()), comp2 = (first == vec.end());
|
||||
if (comp1)
|
||||
*first;
|
||||
}
|
||||
|
||||
void loop(std::vector<int> &vec, int e) {
|
||||
auto start = vec.begin();
|
||||
while (true) {
|
||||
auto item = std::find(start, vec.end(), e);
|
||||
if (item == vec.end())
|
||||
break;
|
||||
*item; // no-warning
|
||||
start = ++item; // no-warning
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue