blob: ac0f24603dd904fd0f11125960de1bd60bc2b50e [file] [log] [blame]
//=== 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