[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:
Gabor Horvath 2017-01-09 09:52:32 +00:00
parent fd18ec5885
commit 3d57457298
8 changed files with 1125 additions and 9 deletions

View File

@ -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.

View File

@ -39,6 +39,7 @@ add_clang_library(clangStaticAnalyzerCheckers
GenericTaintChecker.cpp
GTestChecker.cpp
IdenticalExprChecker.cpp
IteratorPastEndChecker.cpp
IvarInvalidationChecker.cpp
LLVMConventionsChecker.cpp
LocalizationChecker.cpp

View File

@ -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>();
}

View File

@ -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;
}

View File

@ -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 { };

View File

@ -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
}

View File

@ -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

View File

@ -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
}
}