Files
clang-p2996/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
Adam Balogh 770ad9f55e [Analyzer] Fix for iterator modeling and checkers: handle negative numbers correctly
Currently, using negative numbers in iterator operations (additions and
subractions) results in advancements with huge positive numbers due to
an error. This patch fixes it.

Differential Revision: https://reviews.llvm.org/D74760
2020-02-25 14:57:34 +01:00

290 lines
9.9 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 isDereferenceOperator(OverloadedOperatorKind OK) {
return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
OK == OO_Subscript;
}
bool isIncrementOperator(OverloadedOperatorKind OK) {
return OK == OO_PlusPlus;
}
bool isDecrementOperator(OverloadedOperatorKind OK) {
return OK == OO_MinusMinus;
}
bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
OK == OO_MinusEqual;
}
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