Iterators are an abstraction of pointers and in some data structures iterators may be implemented by pointers. This patch adds support for iterators implemented as pointers in all the iterator checkers (including iterator modeling). Differential Revision: https://reviews.llvm.org/D82185
320 lines
11 KiB
C++
320 lines
11 KiB
C++
//=== Iterator.cpp - Common functions for iterator checkers. -------*- C++ -*-//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines common functions to be used by the itertor checkers .
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Iterator.h"
|
|
|
|
namespace clang {
|
|
namespace ento {
|
|
namespace iterator {
|
|
|
|
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 isComparisonOperator(OverloadedOperatorKind OK) {
|
|
return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
|
|
OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
|
|
}
|
|
|
|
bool isInsertCall(const FunctionDecl *Func) {
|
|
const auto *IdInfo = Func->getIdentifier();
|
|
if (!IdInfo)
|
|
return false;
|
|
if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
|
|
return false;
|
|
if (!isIteratorType(Func->getParamDecl(0)->getType()))
|
|
return false;
|
|
return IdInfo->getName() == "insert";
|
|
}
|
|
|
|
bool isEmplaceCall(const FunctionDecl *Func) {
|
|
const auto *IdInfo = Func->getIdentifier();
|
|
if (!IdInfo)
|
|
return false;
|
|
if (Func->getNumParams() < 2)
|
|
return false;
|
|
if (!isIteratorType(Func->getParamDecl(0)->getType()))
|
|
return false;
|
|
return IdInfo->getName() == "emplace";
|
|
}
|
|
|
|
bool isEraseCall(const FunctionDecl *Func) {
|
|
const auto *IdInfo = Func->getIdentifier();
|
|
if (!IdInfo)
|
|
return false;
|
|
if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
|
|
return false;
|
|
if (!isIteratorType(Func->getParamDecl(0)->getType()))
|
|
return false;
|
|
if (Func->getNumParams() == 2 &&
|
|
!isIteratorType(Func->getParamDecl(1)->getType()))
|
|
return false;
|
|
return IdInfo->getName() == "erase";
|
|
}
|
|
|
|
bool isEraseAfterCall(const FunctionDecl *Func) {
|
|
const auto *IdInfo = Func->getIdentifier();
|
|
if (!IdInfo)
|
|
return false;
|
|
if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
|
|
return false;
|
|
if (!isIteratorType(Func->getParamDecl(0)->getType()))
|
|
return false;
|
|
if (Func->getNumParams() == 2 &&
|
|
!isIteratorType(Func->getParamDecl(1)->getType()))
|
|
return false;
|
|
return IdInfo->getName() == "erase_after";
|
|
}
|
|
|
|
bool isAccessOperator(OverloadedOperatorKind OK) {
|
|
return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
|
|
isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
|
|
}
|
|
|
|
bool isAccessOperator(UnaryOperatorKind OK) {
|
|
return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
|
|
isDecrementOperator(OK);
|
|
}
|
|
|
|
bool isAccessOperator(BinaryOperatorKind OK) {
|
|
return isDereferenceOperator(OK) || isRandomIncrOrDecrOperator(OK);
|
|
}
|
|
|
|
bool isDereferenceOperator(OverloadedOperatorKind OK) {
|
|
return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
|
|
OK == OO_Subscript;
|
|
}
|
|
|
|
bool isDereferenceOperator(UnaryOperatorKind OK) {
|
|
return OK == UO_Deref;
|
|
}
|
|
|
|
bool isDereferenceOperator(BinaryOperatorKind OK) {
|
|
return OK == BO_PtrMemI;
|
|
}
|
|
|
|
bool isIncrementOperator(OverloadedOperatorKind OK) {
|
|
return OK == OO_PlusPlus;
|
|
}
|
|
|
|
bool isIncrementOperator(UnaryOperatorKind OK) {
|
|
return OK == UO_PreInc || OK == UO_PostInc;
|
|
}
|
|
|
|
bool isDecrementOperator(OverloadedOperatorKind OK) {
|
|
return OK == OO_MinusMinus;
|
|
}
|
|
|
|
bool isDecrementOperator(UnaryOperatorKind OK) {
|
|
return OK == UO_PreDec || OK == UO_PostDec;
|
|
}
|
|
|
|
bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
|
|
return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
|
|
OK == OO_MinusEqual;
|
|
}
|
|
|
|
bool isRandomIncrOrDecrOperator(BinaryOperatorKind OK) {
|
|
return OK == BO_Add || OK == BO_AddAssign ||
|
|
OK == BO_Sub || OK == BO_SubAssign;
|
|
}
|
|
|
|
const ContainerData *getContainerData(ProgramStateRef State,
|
|
const MemRegion *Cont) {
|
|
return State->get<ContainerMap>(Cont);
|
|
}
|
|
|
|
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
|
|
const SVal &Val) {
|
|
if (auto Reg = Val.getAsRegion()) {
|
|
Reg = Reg->getMostDerivedObjectRegion();
|
|
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;
|
|
}
|
|
|
|
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
|
|
const IteratorPosition &Pos) {
|
|
if (auto Reg = Val.getAsRegion()) {
|
|
Reg = Reg->getMostDerivedObjectRegion();
|
|
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 createIteratorPosition(ProgramStateRef State, const SVal &Val,
|
|
const MemRegion *Cont, const Stmt* S,
|
|
const LocationContext *LCtx,
|
|
unsigned blockCount) {
|
|
auto &StateMgr = State->getStateManager();
|
|
auto &SymMgr = StateMgr.getSymbolManager();
|
|
auto &ACtx = StateMgr.getContext();
|
|
|
|
auto Sym = SymMgr.conjureSymbol(S, LCtx, ACtx.LongTy, blockCount);
|
|
State = assumeNoOverflow(State, Sym, 4);
|
|
return setIteratorPosition(State, Val,
|
|
IteratorPosition::getPosition(Cont, Sym));
|
|
}
|
|
|
|
ProgramStateRef advancePosition(ProgramStateRef State, const SVal &Iter,
|
|
OverloadedOperatorKind Op,
|
|
const SVal &Distance) {
|
|
const auto *Pos = getIteratorPosition(State, Iter);
|
|
if (!Pos)
|
|
return nullptr;
|
|
|
|
auto &SymMgr = State->getStateManager().getSymbolManager();
|
|
auto &SVB = State->getStateManager().getSValBuilder();
|
|
auto &BVF = State->getStateManager().getBasicVals();
|
|
|
|
assert ((Op == OO_Plus || Op == OO_PlusEqual ||
|
|
Op == OO_Minus || Op == OO_MinusEqual) &&
|
|
"Advance operator must be one of +, -, += and -=.");
|
|
auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
|
|
const auto IntDistOp = Distance.getAs<nonloc::ConcreteInt>();
|
|
if (!IntDistOp)
|
|
return nullptr;
|
|
|
|
// For concrete integers we can calculate the new position
|
|
nonloc::ConcreteInt IntDist = *IntDistOp;
|
|
|
|
if (IntDist.getValue().isNegative()) {
|
|
IntDist = nonloc::ConcreteInt(BVF.getValue(-IntDist.getValue()));
|
|
BinOp = (BinOp == BO_Add) ? BO_Sub : BO_Add;
|
|
}
|
|
const auto NewPos =
|
|
Pos->setTo(SVB.evalBinOp(State, BinOp,
|
|
nonloc::SymbolVal(Pos->getOffset()),
|
|
IntDist, SymMgr.getType(Pos->getOffset()))
|
|
.getAsSymbol());
|
|
return setIteratorPosition(State, Iter, NewPos);
|
|
}
|
|
|
|
// This function tells the analyzer's engine that symbols produced by our
|
|
// checker, most notably iterator positions, are relatively small.
|
|
// A distance between items in the container should not be very large.
|
|
// By assuming that it is within around 1/8 of the address space,
|
|
// we can help the analyzer perform operations on these symbols
|
|
// without being afraid of integer overflows.
|
|
// FIXME: Should we provide it as an API, so that all checkers could use it?
|
|
ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
|
|
long Scale) {
|
|
SValBuilder &SVB = State->getStateManager().getSValBuilder();
|
|
BasicValueFactory &BV = SVB.getBasicValueFactory();
|
|
|
|
QualType T = Sym->getType();
|
|
assert(T->isSignedIntegerOrEnumerationType());
|
|
APSIntType AT = BV.getAPSIntType(T);
|
|
|
|
ProgramStateRef NewState = State;
|
|
|
|
llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
|
|
SVal IsCappedFromAbove =
|
|
SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
|
|
nonloc::ConcreteInt(Max), SVB.getConditionType());
|
|
if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
|
|
NewState = NewState->assume(*DV, true);
|
|
if (!NewState)
|
|
return State;
|
|
}
|
|
|
|
llvm::APSInt Min = -Max;
|
|
SVal IsCappedFromBelow =
|
|
SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
|
|
nonloc::ConcreteInt(Min), SVB.getConditionType());
|
|
if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
|
|
NewState = NewState->assume(*DV, true);
|
|
if (!NewState)
|
|
return State;
|
|
}
|
|
|
|
return NewState;
|
|
}
|
|
|
|
bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
|
|
BinaryOperator::Opcode Opc) {
|
|
return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
|
|
}
|
|
|
|
bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
|
|
BinaryOperator::Opcode Opc) {
|
|
auto &SVB = State->getStateManager().getSValBuilder();
|
|
|
|
const auto comparison =
|
|
SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
|
|
|
|
assert(comparison.getAs<DefinedSVal>() &&
|
|
"Symbol comparison must be a `DefinedSVal`");
|
|
|
|
return !State->assume(comparison.castAs<DefinedSVal>(), false);
|
|
}
|
|
|
|
} // namespace iterator
|
|
} // namespace ento
|
|
} // namespace clang
|