73#include "llvm/ADT/STLExtras.h"
86 :
public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
87 check::PostStmt<BinaryOperator>,
88 check::PostStmt<MaterializeTemporaryExpr>,
89 check::Bind, check::LiveSymbols, check::DeadSymbols> {
91 using AdvanceFn = void (IteratorModeling::*)(CheckerContext &,
95 void handleOverloadedOperator(CheckerContext &
C,
const CallEvent &
Call,
97 void handleAdvanceLikeFunction(CheckerContext &
C,
const CallEvent &
Call,
99 const AdvanceFn *Handler)
const;
101 void handleComparison(CheckerContext &
C,
const Expr *CE,
107 void handleIncrement(CheckerContext &
C, SVal RetVal, SVal Iter,
109 void handleDecrement(CheckerContext &
C, SVal RetVal, SVal Iter,
113 SVal Iterator, SVal Amount)
const;
114 void handlePtrIncrOrDecr(CheckerContext &
C,
const Expr *Iterator,
118 SVal Iter, SVal Amount)
const;
120 SVal Iter, SVal Amount)
const;
122 SVal Iter, SVal Amount)
const;
124 SVal RetVal,
const MemRegion *Cont)
const;
125 bool noChangeInAdvance(CheckerContext &
C, SVal Iter,
const Expr *CE)
const;
126 void printState(raw_ostream &Out,
ProgramStateRef State,
const char *NL,
127 const char *Sep)
const override;
130 CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
133 {{CDM::SimpleFunc, {
"std",
"advance"}, 2},
134 &IteratorModeling::handleAdvance},
140 {{CDM::SimpleFunc, {
"std",
"prev"}, 2}, &IteratorModeling::handlePrev},
146 {{CDM::SimpleFunc, {
"std",
"next"}, 2}, &IteratorModeling::handleNext},
150 IteratorModeling() =
default;
152 void checkPostCall(
const CallEvent &
Call, CheckerContext &
C)
const;
153 void checkBind(SVal Loc, SVal Val,
const Stmt *S,
bool AtDeclInit,
154 CheckerContext &
C)
const;
155 void checkPostStmt(
const UnaryOperator *UO, CheckerContext &
C)
const;
156 void checkPostStmt(
const BinaryOperator *BO, CheckerContext &
C)
const;
157 void checkPostStmt(
const MaterializeTemporaryExpr *MTE,
158 CheckerContext &
C)
const;
160 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &
C)
const;
168bool isBoundThroughLazyCompoundVal(
const Environment &Env,
177 const auto *
Func = dyn_cast_or_null<FunctionDecl>(
Call.getDecl());
181 if (
Func->isOverloadedOperator()) {
182 const auto Op =
Func->getOverloadedOperator();
183 handleOverloadedOperator(
C,
Call, Op);
187 const auto *OrigExpr =
Call.getOriginExpr();
191 const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(
Call);
193 handleAdvanceLikeFunction(
C,
Call, OrigExpr, Handler);
200 auto State =
C.getState();
211 State = removeIteratorPosition(State,
Call.getArgSVal(0));
213 C.addTransition(State);
223 for (
unsigned i = 0; i <
Call.getNumArgs(); ++i) {
225 Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
226 C.getASTContext()).getTypePtr() ==
227 Call.getResultType().getDesugaredType(
C.getASTContext()).getTypePtr()) {
229 assignToContainer(
C,
Call.getCFGElementRef(),
Call.getReturnValue(),
230 Pos->getContainer());
237void IteratorModeling::checkBind(SVal Loc, SVal Val,
const Stmt *S,
238 bool AtDeclInit, CheckerContext &
C)
const {
239 auto State =
C.getState();
243 C.addTransition(State);
247 State = removeIteratorPosition(State, Loc);
248 C.addTransition(State);
253void IteratorModeling::checkPostStmt(
const UnaryOperator *UO,
254 CheckerContext &
C)
const {
259 auto &SVB =
C.getSValBuilder();
260 handlePtrIncrOrDecr(
C, UO->
getSubExpr(),
C.getCFGElementRef(),
262 SVB.makeArrayIndex(1));
265void IteratorModeling::checkPostStmt(
const BinaryOperator *BO,
266 CheckerContext &
C)
const {
269 const Expr *
const LHS = BO->
getLHS();
270 const Expr *
const RHS = BO->
getRHS();
271 const SVal LVal = State->getSVal(LHS,
C.getLocationContext());
272 const SVal RVal = State->getSVal(RHS,
C.getLocationContext());
274 if (isSimpleComparisonOperator(BO->
getOpcode())) {
275 SVal
Result = State->getSVal(BO,
C.getLocationContext());
276 handleComparison(
C, BO,
C.getCFGElementRef(),
Result, LVal, RVal,
282 const Expr *
const &IterExpr = IsIterOnLHS ? LHS : RHS;
283 const Expr *
const &AmountExpr = IsIterOnLHS ? RHS : LHS;
288 SVal AmountVal = IsIterOnLHS ? RVal : LVal;
289 handlePtrIncrOrDecr(
C, IterExpr,
C.getCFGElementRef(),
294void IteratorModeling::checkPostStmt(
const MaterializeTemporaryExpr *MTE,
295 CheckerContext &
C)
const {
297 auto State =
C.getState();
302 C.addTransition(State);
306 SymbolReaper &SR)
const {
308 auto RegionMap = State->get<IteratorRegionMap>();
309 for (
const IteratorPosition &Pos : llvm::make_second_range(RegionMap)) {
315 auto SymbolMap = State->get<IteratorSymbolMap>();
316 for (
const IteratorPosition &Pos : llvm::make_second_range(SymbolMap)) {
323void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
324 CheckerContext &
C)
const {
326 auto State =
C.getState();
328 auto RegionMap = State->get<IteratorRegionMap>();
329 for (
const auto &Reg : RegionMap) {
334 if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
335 State = State->remove<IteratorRegionMap>(Reg.first);
340 auto SymbolMap = State->get<IteratorSymbolMap>();
341 for (
const auto &Sym : SymbolMap) {
342 if (!SR.
isLive(Sym.first)) {
343 State = State->remove<IteratorSymbolMap>(Sym.first);
347 C.addTransition(State);
351IteratorModeling::handleOverloadedOperator(CheckerContext &
C,
352 const CallEvent &
Call,
354 if (isSimpleComparisonOperator(Op)) {
355 const auto *OrigExpr =
Call.getOriginExpr();
356 const auto Elem =
Call.getCFGElementRef();
360 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
361 handleComparison(
C, OrigExpr, Elem,
Call.getReturnValue(),
362 InstCall->getCXXThisVal(),
Call.getArgSVal(0), Op);
366 handleComparison(
C, OrigExpr, Elem,
Call.getReturnValue(),
367 Call.getArgSVal(0),
Call.getArgSVal(1), Op);
370 const auto *OrigExpr =
Call.getOriginExpr();
371 const auto Elem =
Call.getCFGElementRef();
375 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
376 if (
Call.getNumArgs() >= 1 &&
377 Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
378 handleRandomIncrOrDecr(
C, Elem, Op,
Call.getReturnValue(),
379 InstCall->getCXXThisVal(),
Call.getArgSVal(0));
382 }
else if (
Call.getNumArgs() >= 2) {
383 const Expr *FirstArg =
Call.getArgExpr(0);
384 const Expr *SecondArg =
Call.getArgExpr(1);
385 const QualType FirstType = FirstArg->
getType();
386 const QualType SecondType = SecondArg->
getType();
393 const SVal FirstArg =
Call.getArgSVal(0);
394 const SVal SecondArg =
Call.getArgSVal(1);
395 SVal Iterator = IsIterFirst ? FirstArg : SecondArg;
396 SVal Amount = IsIterFirst ? SecondArg : FirstArg;
398 handleRandomIncrOrDecr(
C, Elem, Op,
Call.getReturnValue(), Iterator,
404 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
405 handleIncrement(
C,
Call.getReturnValue(), InstCall->getCXXThisVal(),
410 handleIncrement(
C,
Call.getReturnValue(),
Call.getArgSVal(0),
414 if (
const auto *InstCall = dyn_cast<CXXInstanceCall>(&
Call)) {
415 handleDecrement(
C,
Call.getReturnValue(), InstCall->getCXXThisVal(),
420 handleDecrement(
C,
Call.getReturnValue(),
Call.getArgSVal(0),
427IteratorModeling::handleAdvanceLikeFunction(CheckerContext &
C,
428 const CallEvent &
Call,
429 const Expr *OrigExpr,
430 const AdvanceFn *Handler)
const {
432 (this->**Handler)(
C,
Call.getCFGElementRef(),
Call.getReturnValue(),
433 Call.getArgSVal(0),
Call.getArgSVal(1));
441 if (IdInfo->getName() ==
"advance") {
442 if (noChangeInAdvance(
C,
Call.getArgSVal(0), OrigExpr)) {
443 (this->**Handler)(
C,
Call.getCFGElementRef(),
Call.getReturnValue(),
444 Call.getArgSVal(0),
Call.getArgSVal(1));
450void IteratorModeling::handleComparison(CheckerContext &
C,
const Expr *CE,
452 SVal LVal, SVal RVal,
458 auto State =
C.getState();
461 const MemRegion *Cont =
nullptr;
463 Cont = LPos->getContainer();
465 Cont = RPos->getContainer();
473 if (!LPos || !RPos) {
474 auto &SymMgr =
C.getSymbolManager();
475 Sym = SymMgr.conjureSymbol(Elem,
C.getLocationContext(),
476 C.getASTContext().LongTy,
C.blockCount());
499 auto &SymMgr =
C.getSymbolManager();
500 auto *LCtx =
C.getLocationContext();
501 RetVal = nonloc::SymbolVal(SymMgr.conjureSymbol(
502 Elem, LCtx,
C.getASTContext().BoolTy,
C.blockCount()));
503 State = State->BindExpr(CE, LCtx, RetVal);
506 processComparison(
C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
509void IteratorModeling::processComparison(CheckerContext &
C,
513 if (
const auto TruthVal = RetVal.
getAs<nonloc::ConcreteInt>()) {
514 if ((State = relateSymbols(State, Sym1, Sym2,
515 (Op == OO_EqualEqual) ==
516 (TruthVal->getValue()->getBoolValue())))) {
517 C.addTransition(State);
519 C.generateSink(State,
C.getPredecessor());
524 const auto ConditionVal = RetVal.
getAs<DefinedSVal>();
528 if (
auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
529 StateTrue = StateTrue->assume(*ConditionVal,
true);
530 C.addTransition(StateTrue);
533 if (
auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
534 StateFalse = StateFalse->assume(*ConditionVal,
false);
535 C.addTransition(StateFalse);
539void IteratorModeling::handleIncrement(CheckerContext &
C, SVal RetVal,
540 SVal Iter,
bool Postfix)
const {
543 auto State =
C.getState();
544 auto &BVF =
C.getSymbolManager().getBasicVals();
552 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
554 "Advancing position by concrete int should always be successful");
558 "Iterator should have position after successful advancement");
562 C.addTransition(State);
565void IteratorModeling::handleDecrement(CheckerContext &
C, SVal RetVal,
566 SVal Iter,
bool Postfix)
const {
569 auto State =
C.getState();
570 auto &BVF =
C.getSymbolManager().getBasicVals();
578 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
580 "Advancing position by concrete int should always be successful");
584 "Iterator should have position after successful advancement");
588 C.addTransition(State);
591void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &
C,
594 SVal RetVal, SVal Iterator,
598 auto State =
C.getState();
604 const auto *
Value = &Amount;
606 if (
auto LocAmount = Amount.
getAs<Loc>()) {
607 Val = State->getRawSVal(*LocAmount);
612 (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
621 "Iterator should have position after successful advancement");
624 C.addTransition(State);
626 assignToContainer(
C, Elem, TgtVal, Pos->getContainer());
630void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &
C,
631 const Expr *Iterator,
638 QualType PtrType = Iterator->
getType();
644 SVal OldVal = State->getSVal(Iterator,
C.getLocationContext());
651 if (OK == OO_Plus || OK == OO_PlusEqual) {
652 NewVal = State->getLValue(ElementType, Offset, OldVal);
654 auto &SVB =
C.getSValBuilder();
655 SVal NegatedOffset = SVB.evalMinus(Offset.
castAs<NonLoc>());
656 NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
666 "Iterator should have position after successful advancement");
669 C.addTransition(NewState);
676 SVal RetVal, SVal Iter,
678 handleRandomIncrOrDecr(
C, Elem, OO_PlusEqual, RetVal, Iter, Amount);
682 SVal RetVal, SVal Iter, SVal Amount)
const {
683 handleRandomIncrOrDecr(
C, Elem, OO_Minus, RetVal, Iter, Amount);
687 SVal RetVal, SVal Iter, SVal Amount)
const {
688 handleRandomIncrOrDecr(
C, Elem, OO_Plus, RetVal, Iter, Amount);
691void IteratorModeling::assignToContainer(CheckerContext &
C,
693 const MemRegion *Cont)
const {
696 auto State =
C.getState();
697 const auto *LCtx =
C.getLocationContext();
701 C.addTransition(State);
704bool IteratorModeling::noChangeInAdvance(CheckerContext &
C, SVal Iter,
705 const Expr *CE)
const {
708 const auto StateAfter =
C.getState();
717 const ExplodedNode *N = findCallEnter(
C.getPredecessor(), CE);
718 assert(N &&
"Any call should have a `CallEnter` node.");
720 const auto StateBefore = N->
getState();
731 return PosBefore->getOffset() == PosAfter->getOffset();
734void IteratorModeling::printState(raw_ostream &Out,
ProgramStateRef State,
735 const char *NL,
const char *Sep)
const {
736 auto SymbolMap = State->get<IteratorSymbolMap>();
737 auto RegionMap = State->get<IteratorRegionMap>();
741 if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
742 Out << Sep <<
"Iterator Positions :" << NL;
743 for (
const auto &Sym : SymbolMap) {
749 const auto Pos = Sym.second;
750 Out << (Pos.isValid() ?
"Valid" :
"Invalid") <<
" ; Container == ";
751 Pos.getContainer()->dumpToStream(Out);
752 Out<<
" ; Offset == ";
753 Pos.getOffset()->dumpToStream(Out);
756 for (
const auto &Reg : RegionMap) {
760 Reg.first->dumpToStream(Out);
762 const auto Pos = Reg.second;
763 Out << (Pos.isValid() ?
"Valid" :
"Invalid") <<
" ; Container == ";
764 Pos.getContainer()->dumpToStream(Out);
765 Out<<
" ; Offset == ";
766 Pos.getOffset()->dumpToStream(Out);
774 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
778 return OK == BO_EQ || OK == BO_NE;
783 Reg = Reg->getMostDerivedObjectRegion();
784 return State->remove<IteratorRegionMap>(Reg);
786 return State->remove<IteratorSymbolMap>(Sym);
787 }
else if (
const auto LCVal = Val.
getAs<nonloc::LazyCompoundVal>()) {
788 return State->remove<IteratorRegionMap>(LCVal->getRegion());
795 auto &SVB = State->getStateManager().getSValBuilder();
802 const auto comparison =
803 SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
804 nonloc::SymbolVal(Sym2), SVB.getConditionType());
807 "Symbol comparison must be a `DefinedSVal`");
809 auto NewState = State->assume(comparison.castAs<DefinedSVal>(),
Equal);
813 if (
const auto CompSym = comparison.getAsSymbol()) {
815 "Symbol comparison must be a `SymIntExpr`");
818 "Symbol comparison must be a comparison");
825bool isBoundThroughLazyCompoundVal(
const Environment &Env,
826 const MemRegion *Reg) {
827 for (
const auto &Binding : Env) {
828 if (
const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
829 if (LCVal->getRegion() == Reg)
837const ExplodedNode *findCallEnter(
const ExplodedNode *Node,
const Expr *
Call) {
840 if (
auto Enter = PP.
getAs<CallEnter>()) {
841 if (Enter->getCallExpr() ==
Call)
853void ento::registerIteratorModeling(CheckerManager &mgr) {
857bool ento::shouldRegisterIteratorModeling(
const CheckerManager &mgr) {
Defines the C++ template declaration subclasses.
static OverloadedOperatorKind getOverloadedOperator(Opcode Opc)
Retrieve the overloaded operator kind that corresponds to the given binary opcode.
bool isComparisonOp() const
This represents one expression.
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...
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
Expr * getSubExpr() const
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
An immutable map from EnvironemntEntries to SVals.
const ProgramStateRef & getState() const
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
ExplodedNode * getFirstPred()
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
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
virtual void dumpToStream(raw_ostream &os) const
llvm::iterator_range< symbol_iterator > symbols() const
void markLive(SymbolRef sym)
Unconditionally marks a symbol as live.
bool isLiveRegion(const MemRegion *region)
bool isLive(SymbolRef sym)
ProgramStateRef createIteratorPosition(ProgramStateRef State, SVal Val, const MemRegion *Cont, ConstCFGElementRef Elem, const LocationContext *LCtx, unsigned blockCount)
const IteratorPosition * getIteratorPosition(ProgramStateRef State, SVal Val)
bool isIteratorType(const QualType &Type)
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)
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
const Fact * ProgramPoint
A ProgramPoint identifies a location in the CFG by pointing to a specific Fact.
The JSON file list parser is used to communicate input to InstallAPI.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
bool isa(CodeGen::Address addr)
CFGBlock::ConstCFGElementRef ConstCFGElementRef
@ Result
The result type of a method or function.
U cast(CodeGen::Address addr)
const MemRegion * getContainer() const
static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of)