blob: 531054aa7887d7c933f461df0f70ef4620c7f0d6 [file] [log] [blame]
//===-- 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>();
}