75#include "llvm/ADT/STLExtras.h"
83using namespace iterator;
88 :
public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
89 check::PostStmt<BinaryOperator>,
90 check::PostStmt<MaterializeTemporaryExpr>,
91 check::Bind, check::LiveSymbols, check::DeadSymbols> {
100 const AdvanceFn *Handler)
const;
126 const char *Sep)
const override;
132 {{CDM::SimpleFunc, {
"std",
"advance"}, 2},
133 &IteratorModeling::handleAdvance},
139 {{CDM::SimpleFunc, {
"std",
"prev"}, 2}, &IteratorModeling::handlePrev},
145 {{CDM::SimpleFunc, {
"std",
"next"}, 2}, &IteratorModeling::handleNext},
149 IteratorModeling() =
default;
175 const auto *
Func = dyn_cast_or_null<FunctionDecl>(
Call.getDecl());
179 if (
Func->isOverloadedOperator()) {
180 const auto Op =
Func->getOverloadedOperator();
181 handleOverloadedOperator(
C,
Call, Op);
185 const auto *OrigExpr =
Call.getOriginExpr();
189 const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(
Call);
191 handleAdvanceLikeFunction(
C,
Call, OrigExpr, Handler);
198 auto State =
C.getState();
205 if (isa<CXXConstructorCall>(&
Call) &&
Call.getNumArgs() == 1) {
208 if (cast<CXXConstructorDecl>(
Func)->isMoveConstructor()) {
209 State = removeIteratorPosition(State,
Call.getArgSVal(0));
211 C.addTransition(State);
221 for (
unsigned i = 0; i <
Call.getNumArgs(); ++i) {
223 Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
224 C.getASTContext()).getTypePtr() ==
225 Call.getResultType().getDesugaredType(
C.getASTContext()).getTypePtr()) {
227 assignToContainer(
C, OrigExpr,
Call.getReturnValue(),
228 Pos->getContainer());
237 auto State =
C.getState();
241 C.addTransition(State);
245 State = removeIteratorPosition(State,
Loc);
246 C.addTransition(State);
257 auto &SVB =
C.getSValBuilder();
260 SVB.makeArrayIndex(1));
269 const SVal LVal = State->getSVal(LHS,
C.getLocationContext());
270 const SVal RVal = State->getSVal(RHS,
C.getLocationContext());
272 if (isSimpleComparisonOperator(BO->
getOpcode())) {
273 SVal Result = State->getSVal(BO,
C.getLocationContext());
274 handleComparison(
C, BO, Result, LVal, RVal,
280 const Expr *
const &IterExpr = IsIterOnLHS ? LHS : RHS;
281 const Expr *
const &AmountExpr = IsIterOnLHS ? RHS : LHS;
286 SVal AmountVal = IsIterOnLHS ? RVal : LVal;
295 auto State =
C.getState();
300 C.addTransition(State);
309 if (isa<SymbolData>(Sym))
316 if (isa<SymbolData>(Sym))
321void IteratorModeling::checkDeadSymbols(
SymbolReaper &SR,
324 auto State =
C.getState();
327 for (
const auto &Reg : RegionMap) {
332 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
339 for (
const auto &Sym : SymbolMap) {
340 if (!SR.
isLive(Sym.first)) {
345 C.addTransition(State);
352 if (isSimpleComparisonOperator(Op)) {
353 const auto *OrigExpr =
Call.getOriginExpr();
357 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
358 handleComparison(
C, OrigExpr,
Call.getReturnValue(),
359 InstCall->getCXXThisVal(),
Call.getArgSVal(0), Op);
363 handleComparison(
C, OrigExpr,
Call.getReturnValue(),
Call.getArgSVal(0),
364 Call.getArgSVal(1), Op);
367 const auto *OrigExpr =
Call.getOriginExpr();
371 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
372 if (
Call.getNumArgs() >= 1 &&
373 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
374 handleRandomIncrOrDecr(
C, OrigExpr, Op,
Call.getReturnValue(),
375 InstCall->getCXXThisVal(),
Call.getArgSVal(0));
378 }
else if (
Call.getNumArgs() >= 2) {
379 const Expr *FirstArg =
Call.getArgExpr(0);
380 const Expr *SecondArg =
Call.getArgExpr(1);
389 const SVal FirstArg =
Call.getArgSVal(0);
390 const SVal SecondArg =
Call.getArgSVal(1);
391 SVal Iterator = IsIterFirst ? FirstArg : SecondArg;
392 SVal Amount = IsIterFirst ? SecondArg : FirstArg;
394 handleRandomIncrOrDecr(
C, OrigExpr, Op,
Call.getReturnValue(),
400 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
401 handleIncrement(
C,
Call.getReturnValue(), InstCall->getCXXThisVal(),
406 handleIncrement(
C,
Call.getReturnValue(),
Call.getArgSVal(0),
410 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
411 handleDecrement(
C,
Call.getReturnValue(), InstCall->getCXXThisVal(),
416 handleDecrement(
C,
Call.getReturnValue(),
Call.getArgSVal(0),
425 const Expr *OrigExpr,
426 const AdvanceFn *Handler)
const {
428 (this->**Handler)(
C, OrigExpr,
Call.getReturnValue(),
429 Call.getArgSVal(0),
Call.getArgSVal(1));
435 const auto *IdInfo = cast<FunctionDecl>(
Call.getDecl())->getIdentifier();
437 if (IdInfo->getName() ==
"advance") {
438 if (noChangeInAdvance(
C,
Call.getArgSVal(0), OrigExpr)) {
439 (this->**Handler)(
C, OrigExpr,
Call.getReturnValue(),
440 Call.getArgSVal(0),
Call.getArgSVal(1));
453 auto State =
C.getState();
458 Cont = LPos->getContainer();
460 Cont = RPos->getContainer();
468 if (!LPos || !RPos) {
469 auto &SymMgr =
C.getSymbolManager();
470 Sym = SymMgr.conjureSymbol(CE,
C.getLocationContext(),
471 C.getASTContext().LongTy,
C.blockCount());
494 auto &SymMgr =
C.getSymbolManager();
495 auto *LCtx =
C.getLocationContext();
497 CE, LCtx,
C.getASTContext().BoolTy,
C.blockCount()));
498 State = State->BindExpr(CE, LCtx, RetVal);
501 processComparison(
C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
509 if ((State = relateSymbols(State, Sym1, Sym2,
510 (Op == OO_EqualEqual) ==
511 (TruthVal->getValue() != 0)))) {
512 C.addTransition(State);
514 C.generateSink(State,
C.getPredecessor());
523 if (
auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
524 StateTrue = StateTrue->assume(*ConditionVal,
true);
525 C.addTransition(StateTrue);
528 if (
auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
529 StateFalse = StateFalse->assume(*ConditionVal,
false);
530 C.addTransition(StateFalse);
538 auto State =
C.getState();
539 auto &BVF =
C.getSymbolManager().getBasicVals();
549 "Advancing position by concrete int should always be successful");
553 "Iterator should have position after successful advancement");
557 C.addTransition(State);
564 auto State =
C.getState();
565 auto &BVF =
C.getSymbolManager().getBasicVals();
575 "Advancing position by concrete int should always be successful");
579 "Iterator should have position after successful advancement");
583 C.addTransition(State);
592 auto State =
C.getState();
598 const auto *
Value = &Amount;
600 if (
auto LocAmount = Amount.
getAs<
Loc>()) {
601 Val = State->getRawSVal(*LocAmount);
606 (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
615 "Iterator should have position after successful advancement");
618 C.addTransition(State);
620 assignToContainer(
C, CE, TgtVal, Pos->getContainer());
625 const Expr *Iterator,
628 if (!isa<DefinedSVal>(Offset))
637 SVal OldVal = State->getSVal(Iterator,
C.getLocationContext());
644 if (OK == OO_Plus || OK == OO_PlusEqual) {
645 NewVal = State->getLValue(ElementType, Offset, OldVal);
647 auto &SVB =
C.getSValBuilder();
648 SVal NegatedOffset = SVB.evalMinus(Offset.castAs<
NonLoc>());
649 NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
659 "Iterator should have position after successful advancement");
662 C.addTransition(NewState);
664 assignToContainer(
C, Iterator, NewVal, OldPos->
getContainer());
671 handleRandomIncrOrDecr(
C, CE, OO_PlusEqual, RetVal,
Iter, Amount);
676 handleRandomIncrOrDecr(
C, CE, OO_Minus, RetVal,
Iter, Amount);
681 handleRandomIncrOrDecr(
C, CE, OO_Plus, RetVal,
Iter, Amount);
689 auto State =
C.getState();
690 const auto *LCtx =
C.getLocationContext();
693 C.addTransition(State);
697 const Expr *CE)
const {
700 const auto StateAfter =
C.getState();
709 const ExplodedNode *N = findCallEnter(
C.getPredecessor(), CE);
710 assert(N &&
"Any call should have a `CallEnter` node.");
712 const auto StateBefore = N->
getState();
723 return PosBefore->getOffset() == PosAfter->getOffset();
726void IteratorModeling::printState(raw_ostream &Out,
ProgramStateRef State,
727 const char *NL,
const char *Sep)
const {
733 if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
734 Out << Sep <<
"Iterator Positions :" << NL;
735 for (
const auto &Sym : SymbolMap) {
741 const auto Pos = Sym.second;
742 Out << (Pos.isValid() ?
"Valid" :
"Invalid") <<
" ; Container == ";
743 Pos.getContainer()->dumpToStream(Out);
744 Out<<
" ; Offset == ";
745 Pos.getOffset()->dumpToStream(Out);
748 for (
const auto &Reg : RegionMap) {
752 Reg.first->dumpToStream(Out);
754 const auto Pos = Reg.second;
755 Out << (Pos.isValid() ?
"Valid" :
"Invalid") <<
" ; Container == ";
756 Pos.getContainer()->dumpToStream(Out);
757 Out<<
" ; Offset == ";
758 Pos.getOffset()->dumpToStream(Out);
766 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
770 return OK == BO_EQ || OK == BO_NE;
775 Reg = Reg->getMostDerivedObjectRegion();
787 auto &SVB = State->getStateManager().getSValBuilder();
794 const auto comparison =
798 assert(isa<DefinedSVal>(comparison) &&
799 "Symbol comparison must be a `DefinedSVal`");
805 if (
const auto CompSym = comparison.getAsSymbol()) {
806 assert(isa<SymIntExpr>(CompSym) &&
807 "Symbol comparison must be a `SymIntExpr`");
809 cast<SymIntExpr>(CompSym)->getOpcode()) &&
810 "Symbol comparison must be a comparison");
819 for (
const auto &Binding :
Env) {
821 if (LCVal->getRegion() == Reg)
833 if (Enter->getCallExpr() ==
Call)
849bool ento::shouldRegisterIteratorModeling(
const CheckerManager &mgr) {
Defines the C++ template declaration subclasses.
A builtin binary operation expression such as "x + y" or "x <= y".
static OverloadedOperatorKind getOverloadedOperator(Opcode Opc)
Retrieve the overloaded operator kind that corresponds to the given binary opcode.
bool isComparisonOp() const
Represents a point when we begin processing an inlined call.
This represents one expression.
Represents a prvalue temporary that is written into memory so that a reference can bind to it.
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
std::optional< T > getAs() const
Convert to the specified ProgramPoint type, returning std::nullopt if this ProgramPoint is not of the...
A (possibly-)qualified type.
Stmt - This represents one statement.
bool isPointerType() const
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
bool isStructureOrClassType() const
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Expr * getSubExpr() const
An immutable map from CallDescriptions to arbitrary data.
Represents an abstract call to a function or method along a particular path.
virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const
See CheckerManager::runCheckersForPrintState.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
An immutable map from EnvironemntEntries to SVals.
const ProgramStateRef & getState() const
MemRegion - The root abstract class for all memory regions.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getMostDerivedObjectRegion() const
Recursively retrieve the region of the most derived class instance of regions of C++ base class insta...
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
const MemRegion * getAsRegion() const
virtual void dumpToStream(raw_ostream &os) const
llvm::iterator_range< symbol_iterator > symbols() const
A class responsible for cleaning up unused symbols.
void markLive(SymbolRef sym)
Unconditionally marks a symbol as live.
bool isLiveRegion(const MemRegion *region)
bool isLive(SymbolRef sym)
Value representing integer constant.
While nonloc::CompoundVal covers a few simple use cases, nonloc::LazyCompoundVal is a more performant...
Represents symbolic expression that isn't a location.
const IteratorPosition * getIteratorPosition(ProgramStateRef State, SVal Val)
bool isIteratorType(const QualType &Type)
ProgramStateRef createIteratorPosition(ProgramStateRef State, SVal Val, const MemRegion *Cont, const Stmt *S, const LocationContext *LCtx, unsigned blockCount)
ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, long Scale)
bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK)
ProgramStateRef advancePosition(ProgramStateRef State, SVal Iter, OverloadedOperatorKind Op, SVal Distance)
bool isDecrementOperator(OverloadedOperatorKind OK)
ProgramStateRef setIteratorPosition(ProgramStateRef State, SVal Val, const IteratorPosition &Pos)
bool isIncrementOperator(OverloadedOperatorKind OK)
The JSON file list parser is used to communicate input to InstallAPI.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
const MemRegion * getContainer() const
static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of)