123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388 |
- //===-- IteratorChecker.cpp ---------------------------------------*- 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 a checker for using iterators outside their range (past end). Usage
- // means here dereferencing, incrementing etc.
- //
- //===----------------------------------------------------------------------===//
- //
- // In the code, iterator can be represented as a:
- // * type-I: typedef-ed pointer. Operations over such iterator, such as
- // comparisons or increments, are modeled straightforwardly by the
- // analyzer.
- // * type-II: structure with its method bodies available. Operations over such
- // iterator are inlined by the analyzer, and results of modeling
- // these operations are exposing implementation details of the
- // iterators, which is not necessarily helping.
- // * type-III: completely opaque structure. Operations over such iterator are
- // modeled conservatively, producing conjured symbols everywhere.
- //
- // To handle all these types in a common way we introduce a structure called
- // IteratorPosition which is an abstraction of the position the iterator
- // represents using symbolic expressions. The checker handles all the
- // operations on this structure.
- //
- // Additionally, depending on the circumstances, operators of types II and III
- // can be represented as:
- // * type-IIa, type-IIIa: conjured structure symbols - when returned by value
- // from conservatively evaluated methods such as
- // `.begin()`.
- // * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
- // variables or temporaries, when the iterator object is
- // currently treated as an lvalue.
- // * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
- // iterator object is treated as an rvalue taken of a
- // particular lvalue, eg. a copy of "type-a" iterator
- // object, or an iterator that existed before the
- // analysis has started.
- //
- // To handle any of these three different representations stored in an SVal we
- // use setter and getters functions which separate the three cases. To store
- // them we use a pointer union of symbol and memory region.
- //
- // The checker works the following way: We record the begin and the
- // past-end iterator for all containers whenever their `.begin()` and `.end()`
- // are called. Since the Constraint Manager cannot handle such SVals we need
- // to take over its role. We post-check equality and non-equality comparisons
- // and record that the two sides are equal if we are in the 'equal' branch
- // (true-branch for `==` and false-branch for `!=`).
- //
- // In case of type-I or type-II iterators we get a concrete integer as a result
- // of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
- // this latter case we record the symbol and reload it in evalAssume() and do
- // the propagation there. We also handle (maybe double) negated comparisons
- // which are represented in the form of (x == 0 or x != 0) where x is the
- // comparison itself.
- //
- // Since `SimpleConstraintManager` cannot handle complex symbolic expressions
- // we only use expressions of the format S, S+n or S-n for iterator positions
- // where S is a conjured symbol and n is an unsigned concrete integer. When
- // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
- // a constraint which we later retrieve when doing an actual comparison.
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/AST/DeclTemplate.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
- #include <utility>
- using namespace clang;
- using namespace ento;
- namespace {
- // Abstract position of an iterator. This helps to handle all three kinds
- // of operators in a common way by using a symbolic position.
- struct IteratorPosition {
- private:
- // Container the iterator belongs to
- const MemRegion *Cont;
- // Whether iterator is valid
- const bool Valid;
- // Abstract offset
- const SymbolRef Offset;
- IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
- : Cont(C), Valid(V), Offset(Of) {}
- public:
- const MemRegion *getContainer() const { return Cont; }
- bool isValid() const { return Valid; }
- SymbolRef getOffset() const { return Offset; }
- IteratorPosition invalidate() const {
- return IteratorPosition(Cont, false, Offset);
- }
- static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
- return IteratorPosition(C, true, Of);
- }
- IteratorPosition setTo(SymbolRef NewOf) const {
- return IteratorPosition(Cont, Valid, NewOf);
- }
- IteratorPosition reAssign(const MemRegion *NewCont) const {
- return IteratorPosition(NewCont, Valid, Offset);
- }
- bool operator==(const IteratorPosition &X) const {
- return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
- }
- bool operator!=(const IteratorPosition &X) const {
- return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
- }
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddPointer(Cont);
- ID.AddInteger(Valid);
- ID.Add(Offset);
- }
- };
- // Structure to record the symbolic begin and end position of a container
- struct ContainerData {
- private:
- const SymbolRef Begin, End;
- ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
- public:
- static ContainerData fromBegin(SymbolRef B) {
- return ContainerData(B, nullptr);
- }
- static ContainerData fromEnd(SymbolRef E) {
- return ContainerData(nullptr, E);
- }
- SymbolRef getBegin() const { return Begin; }
- SymbolRef getEnd() const { return End; }
- ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
- ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
- bool operator==(const ContainerData &X) const {
- return Begin == X.Begin && End == X.End;
- }
- bool operator!=(const ContainerData &X) const {
- return Begin != X.Begin || End != X.End;
- }
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.Add(Begin);
- ID.Add(End);
- }
- };
- class IteratorChecker
- : public Checker<check::PreCall, check::PostCall,
- check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
- check::LiveSymbols, check::DeadSymbols> {
- std::unique_ptr<BugType> OutOfRangeBugType;
- std::unique_ptr<BugType> MismatchedBugType;
- std::unique_ptr<BugType> InvalidatedBugType;
- void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
- const SVal &LVal, const SVal &RVal,
- OverloadedOperatorKind Op) const;
- void processComparison(CheckerContext &C, ProgramStateRef State,
- SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
- OverloadedOperatorKind Op) const;
- void verifyAccess(CheckerContext &C, const SVal &Val) const;
- void verifyDereference(CheckerContext &C, const SVal &Val) const;
- void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
- bool Postfix) const;
- void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
- bool Postfix) const;
- void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
- const SVal &RetVal, const SVal &LHS,
- const SVal &RHS) const;
- void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
- const SVal &Cont) const;
- void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
- const SVal &Cont) const;
- void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
- const MemRegion *Cont) const;
- void handleAssign(CheckerContext &C, const SVal &Cont,
- const Expr *CE = nullptr,
- const SVal &OldCont = UndefinedVal()) const;
- void handleClear(CheckerContext &C, const SVal &Cont) const;
- void handlePushBack(CheckerContext &C, const SVal &Cont) const;
- void handlePopBack(CheckerContext &C, const SVal &Cont) const;
- void handlePushFront(CheckerContext &C, const SVal &Cont) const;
- void handlePopFront(CheckerContext &C, const SVal &Cont) const;
- void handleInsert(CheckerContext &C, const SVal &Iter) const;
- void handleErase(CheckerContext &C, const SVal &Iter) const;
- void handleErase(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const;
- void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
- void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const;
- void verifyIncrement(CheckerContext &C, const SVal &Iter) const;
- void verifyDecrement(CheckerContext &C, const SVal &Iter) const;
- void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
- const SVal &LHS, const SVal &RHS) const;
- void verifyMatch(CheckerContext &C, const SVal &Iter,
- const MemRegion *Cont) const;
- void verifyMatch(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const;
- IteratorPosition advancePosition(CheckerContext &C, OverloadedOperatorKind Op,
- const IteratorPosition &Pos,
- const SVal &Distance) const;
- void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
- CheckerContext &C, ExplodedNode *ErrNode) const;
- void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
- const SVal &Val2, CheckerContext &C,
- ExplodedNode *ErrNode) const;
- void reportMismatchedBug(const StringRef &Message, const SVal &Val,
- const MemRegion *Reg, CheckerContext &C,
- ExplodedNode *ErrNode) const;
- void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
- CheckerContext &C, ExplodedNode *ErrNode) const;
- public:
- IteratorChecker();
- enum CheckKind {
- CK_IteratorRangeChecker,
- CK_MismatchedIteratorChecker,
- CK_InvalidatedIteratorChecker,
- CK_NumCheckKinds
- };
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
- CheckName CheckNames[CK_NumCheckKinds];
- void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
- void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
- void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
- void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
- void checkPostStmt(const MaterializeTemporaryExpr *MTE,
- CheckerContext &C) const;
- void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
- void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
- };
- } // namespace
- REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
- REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
- IteratorPosition)
- REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
- namespace {
- bool isIteratorType(const QualType &Type);
- bool isIterator(const CXXRecordDecl *CRD);
- bool isComparisonOperator(OverloadedOperatorKind OK);
- bool isBeginCall(const FunctionDecl *Func);
- bool isEndCall(const FunctionDecl *Func);
- bool isAssignCall(const FunctionDecl *Func);
- bool isClearCall(const FunctionDecl *Func);
- bool isPushBackCall(const FunctionDecl *Func);
- bool isEmplaceBackCall(const FunctionDecl *Func);
- bool isPopBackCall(const FunctionDecl *Func);
- bool isPushFrontCall(const FunctionDecl *Func);
- bool isEmplaceFrontCall(const FunctionDecl *Func);
- bool isPopFrontCall(const FunctionDecl *Func);
- bool isInsertCall(const FunctionDecl *Func);
- bool isEraseCall(const FunctionDecl *Func);
- bool isEraseAfterCall(const FunctionDecl *Func);
- bool isEmplaceCall(const FunctionDecl *Func);
- bool isAssignmentOperator(OverloadedOperatorKind OK);
- bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
- bool isAccessOperator(OverloadedOperatorKind OK);
- bool isDereferenceOperator(OverloadedOperatorKind OK);
- bool isIncrementOperator(OverloadedOperatorKind OK);
- bool isDecrementOperator(OverloadedOperatorKind OK);
- bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
- bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
- bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
- bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
- SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
- SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
- ProgramStateRef createContainerBegin(ProgramStateRef State,
- const MemRegion *Cont, const Expr *E,
- QualType T, const LocationContext *LCtx,
- unsigned BlockCount);
- ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
- const Expr *E, QualType T,
- const LocationContext *LCtx,
- unsigned BlockCount);
- const IteratorPosition *getIteratorPosition(ProgramStateRef State,
- const SVal &Val);
- ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
- const IteratorPosition &Pos);
- ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
- ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
- long Scale);
- ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
- const MemRegion *Cont);
- ProgramStateRef
- invalidateAllIteratorPositionsExcept(ProgramStateRef State,
- const MemRegion *Cont, SymbolRef Offset,
- BinaryOperator::Opcode Opc);
- ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
- SymbolRef Offset,
- BinaryOperator::Opcode Opc);
- ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
- SymbolRef Offset1,
- BinaryOperator::Opcode Opc1,
- SymbolRef Offset2,
- BinaryOperator::Opcode Opc2);
- ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
- const MemRegion *Cont,
- const MemRegion *NewCont);
- ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
- const MemRegion *Cont,
- const MemRegion *NewCont,
- SymbolRef Offset,
- BinaryOperator::Opcode Opc);
- ProgramStateRef rebaseSymbolInIteratorPositionsIf(
- ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
- SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
- ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
- SymbolRef Sym2, bool Equal);
- const ContainerData *getContainerData(ProgramStateRef State,
- const MemRegion *Cont);
- ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
- const ContainerData &CData);
- bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
- bool isBoundThroughLazyCompoundVal(const Environment &Env,
- const MemRegion *Reg);
- bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
- bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
- bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
- bool isZero(ProgramStateRef State, const NonLoc &Val);
- } // namespace
- IteratorChecker::IteratorChecker() {
- OutOfRangeBugType.reset(
- new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
- MismatchedBugType.reset(
- new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
- /*SuppressOnSink=*/true));
- InvalidatedBugType.reset(
- new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
- }
- void IteratorChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
- // Check for out of range access or access of invalidated position and
- // iterator mismatches
- const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!Func)
- return;
- if (Func->isOverloadedOperator()) {
- if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
- isAccessOperator(Func->getOverloadedOperator())) {
- // Check for any kind of access of invalidated iterator positions
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyAccess(C, InstCall->getCXXThisVal());
- } else {
- verifyAccess(C, Call.getArgSVal(0));
- }
- }
- if (ChecksEnabled[CK_IteratorRangeChecker]) {
- if (isIncrementOperator(Func->getOverloadedOperator())) {
- // Check for out-of-range incrementions
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyIncrement(C, InstCall->getCXXThisVal());
- } else {
- if (Call.getNumArgs() >= 1) {
- verifyIncrement(C, Call.getArgSVal(0));
- }
- }
- } else if (isDecrementOperator(Func->getOverloadedOperator())) {
- // Check for out-of-range decrementions
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyDecrement(C, InstCall->getCXXThisVal());
- } else {
- if (Call.getNumArgs() >= 1) {
- verifyDecrement(C, Call.getArgSVal(0));
- }
- }
- } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- // Check for out-of-range incrementions and decrementions
- if (Call.getNumArgs() >= 1 &&
- Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
- verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- InstCall->getCXXThisVal(),
- Call.getArgSVal(0));
- }
- } else {
- if (Call.getNumArgs() >= 2 &&
- Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
- verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- Call.getArgSVal(0), Call.getArgSVal(1));
- }
- }
- } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
- // Check for dereference of out-of-range iterators
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyDereference(C, InstCall->getCXXThisVal());
- } else {
- verifyDereference(C, Call.getArgSVal(0));
- }
- }
- } else if (ChecksEnabled[CK_MismatchedIteratorChecker] &&
- isComparisonOperator(Func->getOverloadedOperator())) {
- // Check for comparisons of iterators of different containers
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (Call.getNumArgs() < 1)
- return;
- if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
- !isIteratorType(Call.getArgExpr(0)->getType()))
- return;
- verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
- } else {
- if (Call.getNumArgs() < 2)
- return;
- if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
- !isIteratorType(Call.getArgExpr(1)->getType()))
- return;
- verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
- }
- }
- } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (!ChecksEnabled[CK_MismatchedIteratorChecker])
- return;
- const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
- if (!ContReg)
- return;
- // Check for erase, insert and emplace using iterator of another container
- if (isEraseCall(Func) || isEraseAfterCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
- if (Call.getNumArgs() == 2) {
- verifyMatch(C, Call.getArgSVal(1),
- InstCall->getCXXThisVal().getAsRegion());
- }
- } else if (isInsertCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
- if (Call.getNumArgs() == 3 &&
- isIteratorType(Call.getArgExpr(1)->getType()) &&
- isIteratorType(Call.getArgExpr(2)->getType())) {
- verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
- }
- } else if (isEmplaceCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
- }
- } else if (isa<CXXConstructorCall>(&Call)) {
- // Check match of first-last iterator pair in a constructor of a container
- if (Call.getNumArgs() < 2)
- return;
- const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
- if (Ctr->getNumParams() < 2)
- return;
- if (Ctr->getParamDecl(0)->getName() != "first" ||
- Ctr->getParamDecl(1)->getName() != "last")
- return;
- if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
- !isIteratorType(Call.getArgExpr(1)->getType()))
- return;
- verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
- } else {
- // The main purpose of iterators is to abstract away from different
- // containers and provide a (maybe limited) uniform access to them.
- // This implies that any correctly written template function that
- // works on multiple containers using iterators takes different
- // template parameters for different containers. So we can safely
- // assume that passing iterators of different containers as arguments
- // whose type replaces the same template parameter is a bug.
- //
- // Example:
- // template<typename I1, typename I2>
- // void f(I1 first1, I1 last1, I2 first2, I2 last2);
- //
- // In this case the first two arguments to f() must be iterators must belong
- // to the same container and the last to also to the same container but
- // not necessarily to the same as the first two.
- if (!ChecksEnabled[CK_MismatchedIteratorChecker])
- return;
- const auto *Templ = Func->getPrimaryTemplate();
- if (!Templ)
- return;
- const auto *TParams = Templ->getTemplateParameters();
- const auto *TArgs = Func->getTemplateSpecializationArgs();
- // Iterate over all the template parameters
- for (size_t I = 0; I < TParams->size(); ++I) {
- const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
- if (!TPDecl)
- continue;
- if (TPDecl->isParameterPack())
- continue;
- const auto TAType = TArgs->get(I).getAsType();
- if (!isIteratorType(TAType))
- continue;
- SVal LHS = UndefinedVal();
- // For every template parameter which is an iterator type in the
- // instantiation look for all functions' parameters' type by it and
- // check whether they belong to the same container
- for (auto J = 0U; J < Func->getNumParams(); ++J) {
- const auto *Param = Func->getParamDecl(J);
- const auto *ParamType =
- Param->getType()->getAs<SubstTemplateTypeParmType>();
- if (!ParamType ||
- ParamType->getReplacedParameter()->getDecl() != TPDecl)
- continue;
- if (LHS.isUndef()) {
- LHS = Call.getArgSVal(J);
- } else {
- verifyMatch(C, LHS, Call.getArgSVal(J));
- }
- }
- }
- }
- }
- void IteratorChecker::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
- // Record new iterator positions and iterator position changes
- const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!Func)
- return;
- if (Func->isOverloadedOperator()) {
- const auto Op = Func->getOverloadedOperator();
- if (isAssignmentOperator(Op)) {
- // Overloaded 'operator=' must be a non-static member function.
- const auto *InstCall = cast<CXXInstanceCall>(&Call);
- if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
- handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
- Call.getArgSVal(0));
- return;
- }
- handleAssign(C, InstCall->getCXXThisVal());
- return;
- } else if (isSimpleComparisonOperator(Op)) {
- const auto *OrigExpr = Call.getOriginExpr();
- if (!OrigExpr)
- return;
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleComparison(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
- return;
- }
- handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getArgSVal(1), Op);
- return;
- } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (Call.getNumArgs() >= 1 &&
- Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
- handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- Call.getReturnValue(),
- InstCall->getCXXThisVal(), Call.getArgSVal(0));
- return;
- }
- } else {
- if (Call.getNumArgs() >= 2 &&
- Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
- handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- Call.getReturnValue(), Call.getArgSVal(0),
- Call.getArgSVal(1));
- return;
- }
- }
- } else if (isIncrementOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
- Call.getNumArgs());
- return;
- }
- handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getNumArgs());
- return;
- } else if (isDecrementOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
- Call.getNumArgs());
- return;
- }
- handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getNumArgs());
- return;
- }
- } else {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (isAssignCall(Func)) {
- handleAssign(C, InstCall->getCXXThisVal());
- return;
- }
- if (isClearCall(Func)) {
- handleClear(C, InstCall->getCXXThisVal());
- return;
- }
- if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
- handlePushBack(C, InstCall->getCXXThisVal());
- return;
- }
- if (isPopBackCall(Func)) {
- handlePopBack(C, InstCall->getCXXThisVal());
- return;
- }
- if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
- handlePushFront(C, InstCall->getCXXThisVal());
- return;
- }
- if (isPopFrontCall(Func)) {
- handlePopFront(C, InstCall->getCXXThisVal());
- return;
- }
- if (isInsertCall(Func) || isEmplaceCall(Func)) {
- handleInsert(C, Call.getArgSVal(0));
- return;
- }
- if (isEraseCall(Func)) {
- if (Call.getNumArgs() == 1) {
- handleErase(C, Call.getArgSVal(0));
- return;
- }
- if (Call.getNumArgs() == 2) {
- handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
- return;
- }
- }
- if (isEraseAfterCall(Func)) {
- if (Call.getNumArgs() == 1) {
- handleEraseAfter(C, Call.getArgSVal(0));
- return;
- }
- if (Call.getNumArgs() == 2) {
- handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
- return;
- }
- }
- }
- const auto *OrigExpr = Call.getOriginExpr();
- if (!OrigExpr)
- return;
- if (!isIteratorType(Call.getResultType()))
- return;
- auto State = C.getState();
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (isBeginCall(Func)) {
- handleBegin(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal());
- return;
- }
-
- if (isEndCall(Func)) {
- handleEnd(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal());
- return;
- }
- }
- // Already bound to container?
- if (getIteratorPosition(State, Call.getReturnValue()))
- return;
- // Copy-like and move constructors
- if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
- if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
- State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
- if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
- State = removeIteratorPosition(State, Call.getArgSVal(0));
- }
- C.addTransition(State);
- return;
- }
- }
- // Assumption: if return value is an iterator which is not yet bound to a
- // container, then look for the first iterator argument, and
- // bind the return value to the same container. This approach
- // works for STL algorithms.
- // FIXME: Add a more conservative mode
- for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
- if (isIteratorType(Call.getArgExpr(i)->getType())) {
- if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
- assignToContainer(C, OrigExpr, Call.getReturnValue(),
- Pos->getContainer());
- return;
- }
- }
- }
- }
- }
- void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
- CheckerContext &C) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Val);
- if (Pos) {
- State = setIteratorPosition(State, Loc, *Pos);
- C.addTransition(State);
- } else {
- const auto *OldPos = getIteratorPosition(State, Loc);
- if (OldPos) {
- State = removeIteratorPosition(State, Loc);
- C.addTransition(State);
- }
- }
- }
- void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
- CheckerContext &C) const {
- /* Transfer iterator state to temporary objects */
- auto State = C.getState();
- const auto *Pos =
- getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
- if (!Pos)
- return;
- State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
- C.addTransition(State);
- }
- void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
- SymbolReaper &SR) const {
- // Keep symbolic expressions of iterator positions, container begins and ends
- // alive
- auto RegionMap = State->get<IteratorRegionMap>();
- for (const auto Reg : RegionMap) {
- const auto Offset = Reg.second.getOffset();
- for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
- if (isa<SymbolData>(*i))
- SR.markLive(*i);
- }
- auto SymbolMap = State->get<IteratorSymbolMap>();
- for (const auto Sym : SymbolMap) {
- const auto Offset = Sym.second.getOffset();
- for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
- if (isa<SymbolData>(*i))
- SR.markLive(*i);
- }
- auto ContMap = State->get<ContainerMap>();
- for (const auto Cont : ContMap) {
- const auto CData = Cont.second;
- if (CData.getBegin()) {
- SR.markLive(CData.getBegin());
- if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
- SR.markLive(SIE->getLHS());
- }
- if (CData.getEnd()) {
- SR.markLive(CData.getEnd());
- if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
- SR.markLive(SIE->getLHS());
- }
- }
- }
- void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
- CheckerContext &C) const {
- // Cleanup
- auto State = C.getState();
- auto RegionMap = State->get<IteratorRegionMap>();
- for (const auto Reg : RegionMap) {
- if (!SR.isLiveRegion(Reg.first)) {
- // The region behind the `LazyCompoundVal` is often cleaned up before
- // the `LazyCompoundVal` itself. If there are iterator positions keyed
- // by these regions their cleanup must be deferred.
- if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
- State = State->remove<IteratorRegionMap>(Reg.first);
- }
- }
- }
- auto SymbolMap = State->get<IteratorSymbolMap>();
- for (const auto Sym : SymbolMap) {
- if (!SR.isLive(Sym.first)) {
- State = State->remove<IteratorSymbolMap>(Sym.first);
- }
- }
- auto ContMap = State->get<ContainerMap>();
- for (const auto Cont : ContMap) {
- if (!SR.isLiveRegion(Cont.first)) {
- // We must keep the container data while it has live iterators to be able
- // to compare them to the begin and the end of the container.
- if (!hasLiveIterators(State, Cont.first)) {
- State = State->remove<ContainerMap>(Cont.first);
- }
- }
- }
- C.addTransition(State);
- }
- void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE,
- const SVal &RetVal, const SVal &LVal,
- const SVal &RVal,
- OverloadedOperatorKind Op) const {
- // Record the operands and the operator of the comparison for the next
- // evalAssume, if the result is a symbolic expression. If it is a concrete
- // value (only one branch is possible), then transfer the state between
- // the operands according to the operator and the result
- auto State = C.getState();
- const auto *LPos = getIteratorPosition(State, LVal);
- const auto *RPos = getIteratorPosition(State, RVal);
- const MemRegion *Cont = nullptr;
- if (LPos) {
- Cont = LPos->getContainer();
- } else if (RPos) {
- Cont = RPos->getContainer();
- }
- if (!Cont)
- return;
- // At least one of the iterators have recorded positions. If one of them has
- // not then create a new symbol for the offset.
- SymbolRef Sym;
- if (!LPos || !RPos) {
- auto &SymMgr = C.getSymbolManager();
- Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
- C.getASTContext().LongTy, C.blockCount());
- State = assumeNoOverflow(State, Sym, 4);
- }
- if (!LPos) {
- State = setIteratorPosition(State, LVal,
- IteratorPosition::getPosition(Cont, Sym));
- LPos = getIteratorPosition(State, LVal);
- } else if (!RPos) {
- State = setIteratorPosition(State, RVal,
- IteratorPosition::getPosition(Cont, Sym));
- RPos = getIteratorPosition(State, RVal);
- }
- processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
- }
- void IteratorChecker::processComparison(CheckerContext &C,
- ProgramStateRef State, SymbolRef Sym1,
- SymbolRef Sym2, const SVal &RetVal,
- OverloadedOperatorKind Op) const {
- if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
- if ((State = relateSymbols(State, Sym1, Sym2,
- (Op == OO_EqualEqual) ==
- (TruthVal->getValue() != 0)))) {
- C.addTransition(State);
- } else {
- C.generateSink(State, C.getPredecessor());
- }
- return;
- }
- const auto ConditionVal = RetVal.getAs<DefinedSVal>();
- if (!ConditionVal)
- return;
-
- if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
- StateTrue = StateTrue->assume(*ConditionVal, true);
- C.addTransition(StateTrue);
- }
-
- if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
- StateFalse = StateFalse->assume(*ConditionVal, false);
- C.addTransition(StateFalse);
- }
- }
- void IteratorChecker::verifyDereference(CheckerContext &C,
- const SVal &Val) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Val);
- if (Pos && isPastTheEnd(State, *Pos)) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
- return;
- }
- }
- void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Val);
- if (Pos && !Pos->isValid()) {
- auto *N = C.generateErrorNode(State);
- if (!N) {
- return;
- }
- reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
- }
- }
- void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
- const SVal &Iter, bool Postfix) const {
- // Increment the symbolic expressions which represents the position of the
- // iterator
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (Pos) {
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- const auto NewPos =
- advancePosition(C, OO_Plus, *Pos,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- State = setIteratorPosition(State, Iter, NewPos);
- State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
- C.addTransition(State);
- }
- }
- void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
- const SVal &Iter, bool Postfix) const {
- // Decrement the symbolic expressions which represents the position of the
- // iterator
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (Pos) {
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- const auto NewPos =
- advancePosition(C, OO_Minus, *Pos,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- State = setIteratorPosition(State, Iter, NewPos);
- State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
- C.addTransition(State);
- }
- }
- void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
- OverloadedOperatorKind Op,
- const SVal &RetVal,
- const SVal &LHS,
- const SVal &RHS) const {
- // Increment or decrement the symbolic expressions which represents the
- // position of the iterator
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, LHS);
- if (!Pos)
- return;
- const auto *value = &RHS;
- if (auto loc = RHS.getAs<Loc>()) {
- const auto val = State->getRawSVal(*loc);
- value = &val;
- }
- auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
- State =
- setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value));
- C.addTransition(State);
- }
- void IteratorChecker::verifyIncrement(CheckerContext &C,
- const SVal &Iter) const {
- auto &BVF = C.getSValBuilder().getBasicValueFactory();
- verifyRandomIncrOrDecr(C, OO_Plus, Iter,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- }
- void IteratorChecker::verifyDecrement(CheckerContext &C,
- const SVal &Iter) const {
- auto &BVF = C.getSValBuilder().getBasicValueFactory();
- verifyRandomIncrOrDecr(C, OO_Minus, Iter,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- }
- void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
- OverloadedOperatorKind Op,
- const SVal &LHS,
- const SVal &RHS) const {
- auto State = C.getState();
- // If the iterator is initially inside its range, then the operation is valid
- const auto *Pos = getIteratorPosition(State, LHS);
- if (!Pos)
- return;
- auto Value = RHS;
- if (auto ValAsLoc = RHS.getAs<Loc>()) {
- Value = State->getRawSVal(*ValAsLoc);
- }
- if (Value.isUnknown())
- return;
- // Incremention or decremention by 0 is never a bug.
- if (isZero(State, Value.castAs<NonLoc>()))
- return;
- // The result may be the past-end iterator of the container, but any other
- // out of range position is undefined behaviour
- if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
- C, N);
- }
- if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportOutOfRangeBug("Iterator incremented behind the past-the-end "
- "iterator.", LHS, C, N);
- }
- }
- void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
- const MemRegion *Cont) const {
- // Verify match between a container and the container of an iterator
- Cont = Cont->getMostDerivedObjectRegion();
- if (const auto *ContSym = Cont->getSymbolicBase()) {
- if (isa<SymbolConjured>(ContSym->getSymbol()))
- return;
- }
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
- return;
- const auto *IterCont = Pos->getContainer();
- // Skip symbolic regions based on conjured symbols. Two conjured symbols
- // may or may not be the same. For example, the same function can return
- // the same or a different container but we get different conjured symbols
- // for each call. This may cause false positives so omit them from the check.
- if (const auto *ContSym = IterCont->getSymbolicBase()) {
- if (isa<SymbolConjured>(ContSym->getSymbol()))
- return;
- }
- if (IterCont != Cont) {
- auto *N = C.generateNonFatalErrorNode(State);
- if (!N) {
- return;
- }
- reportMismatchedBug("Container accessed using foreign iterator argument.",
- Iter, Cont, C, N);
- }
- }
- void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const {
- // Verify match between the containers of two iterators
- auto State = C.getState();
- const auto *Pos1 = getIteratorPosition(State, Iter1);
- if (!Pos1)
- return;
- const auto *IterCont1 = Pos1->getContainer();
- // Skip symbolic regions based on conjured symbols. Two conjured symbols
- // may or may not be the same. For example, the same function can return
- // the same or a different container but we get different conjured symbols
- // for each call. This may cause false positives so omit them from the check.
- if (const auto *ContSym = IterCont1->getSymbolicBase()) {
- if (isa<SymbolConjured>(ContSym->getSymbol()))
- return;
- }
- const auto *Pos2 = getIteratorPosition(State, Iter2);
- if (!Pos2)
- return;
- const auto *IterCont2 = Pos2->getContainer();
- if (const auto *ContSym = IterCont2->getSymbolicBase()) {
- if (isa<SymbolConjured>(ContSym->getSymbol()))
- return;
- }
- if (IterCont1 != IterCont2) {
- auto *N = C.generateNonFatalErrorNode(State);
- if (!N)
- return;
- reportMismatchedBug("Iterators of different containers used where the "
- "same container is expected.", Iter1, Iter2, C, N);
- }
- }
- void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
- const SVal &RetVal, const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- // If the container already has a begin symbol then use it. Otherwise first
- // create a new one.
- auto State = C.getState();
- auto BeginSym = getContainerBegin(State, ContReg);
- if (!BeginSym) {
- State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
- C.getLocationContext(), C.blockCount());
- BeginSym = getContainerBegin(State, ContReg);
- }
- State = setIteratorPosition(State, RetVal,
- IteratorPosition::getPosition(ContReg, BeginSym));
- C.addTransition(State);
- }
- void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
- const SVal &RetVal, const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- // If the container already has an end symbol then use it. Otherwise first
- // create a new one.
- auto State = C.getState();
- auto EndSym = getContainerEnd(State, ContReg);
- if (!EndSym) {
- State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
- C.getLocationContext(), C.blockCount());
- EndSym = getContainerEnd(State, ContReg);
- }
- State = setIteratorPosition(State, RetVal,
- IteratorPosition::getPosition(ContReg, EndSym));
- C.addTransition(State);
- }
- void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
- const SVal &RetVal,
- const MemRegion *Cont) const {
- Cont = Cont->getMostDerivedObjectRegion();
- auto State = C.getState();
- auto &SymMgr = C.getSymbolManager();
- auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
- C.getASTContext().LongTy, C.blockCount());
- State = assumeNoOverflow(State, Sym, 4);
- State = setIteratorPosition(State, RetVal,
- IteratorPosition::getPosition(Cont, Sym));
- C.addTransition(State);
- }
- void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
- const Expr *CE, const SVal &OldCont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- // Assignment of a new value to a container always invalidates all its
- // iterators
- auto State = C.getState();
- const auto CData = getContainerData(State, ContReg);
- if (CData) {
- State = invalidateAllIteratorPositions(State, ContReg);
- }
- // In case of move, iterators of the old container (except the past-end
- // iterators) remain valid but refer to the new container
- if (!OldCont.isUndef()) {
- const auto *OldContReg = OldCont.getAsRegion();
- if (OldContReg) {
- OldContReg = OldContReg->getMostDerivedObjectRegion();
- const auto OldCData = getContainerData(State, OldContReg);
- if (OldCData) {
- if (const auto OldEndSym = OldCData->getEnd()) {
- // If we already assigned an "end" symbol to the old container, then
- // first reassign all iterator positions to the new container which
- // are not past the container (thus not greater or equal to the
- // current "end" symbol).
- State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
- OldEndSym, BO_GE);
- auto &SymMgr = C.getSymbolManager();
- auto &SVB = C.getSValBuilder();
- // Then generate and assign a new "end" symbol for the new container.
- auto NewEndSym =
- SymMgr.conjureSymbol(CE, C.getLocationContext(),
- C.getASTContext().LongTy, C.blockCount());
- State = assumeNoOverflow(State, NewEndSym, 4);
- if (CData) {
- State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
- } else {
- State = setContainerData(State, ContReg,
- ContainerData::fromEnd(NewEndSym));
- }
- // Finally, replace the old "end" symbol in the already reassigned
- // iterator positions with the new "end" symbol.
- State = rebaseSymbolInIteratorPositionsIf(
- State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
- } else {
- // There was no "end" symbol assigned yet to the old container,
- // so reassign all iterator positions to the new container.
- State = reassignAllIteratorPositions(State, OldContReg, ContReg);
- }
- if (const auto OldBeginSym = OldCData->getBegin()) {
- // If we already assigned a "begin" symbol to the old container, then
- // assign it to the new container and remove it from the old one.
- if (CData) {
- State =
- setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
- } else {
- State = setContainerData(State, ContReg,
- ContainerData::fromBegin(OldBeginSym));
- }
- State =
- setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
- }
- } else {
- // There was neither "begin" nor "end" symbol assigned yet to the old
- // container, so reassign all iterator positions to the new container.
- State = reassignAllIteratorPositions(State, OldContReg, ContReg);
- }
- }
- }
- C.addTransition(State);
- }
- void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- // The clear() operation invalidates all the iterators, except the past-end
- // iterators of list-like containers
- auto State = C.getState();
- if (!hasSubscriptOperator(State, ContReg) ||
- !backModifiable(State, ContReg)) {
- const auto CData = getContainerData(State, ContReg);
- if (CData) {
- if (const auto EndSym = CData->getEnd()) {
- State =
- invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
- C.addTransition(State);
- return;
- }
- }
- }
- State = invalidateAllIteratorPositions(State, ContReg);
- C.addTransition(State);
- }
- void IteratorChecker::handlePushBack(CheckerContext &C,
- const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- // For deque-like containers invalidate all iterator positions
- auto State = C.getState();
- if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
- State = invalidateAllIteratorPositions(State, ContReg);
- C.addTransition(State);
- return;
- }
- const auto CData = getContainerData(State, ContReg);
- if (!CData)
- return;
- // For vector-like containers invalidate the past-end iterator positions
- if (const auto EndSym = CData->getEnd()) {
- if (hasSubscriptOperator(State, ContReg)) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- }
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto newEndSym =
- SVB.evalBinOp(State, BO_Add,
- nonloc::SymbolVal(EndSym),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(EndSym)).getAsSymbol();
- State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
- }
- C.addTransition(State);
- }
- void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- auto State = C.getState();
- const auto CData = getContainerData(State, ContReg);
- if (!CData)
- return;
- if (const auto EndSym = CData->getEnd()) {
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto BackSym =
- SVB.evalBinOp(State, BO_Sub,
- nonloc::SymbolVal(EndSym),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(EndSym)).getAsSymbol();
- // For vector-like and deque-like containers invalidate the last and the
- // past-end iterator positions. For list-like containers only invalidate
- // the last position
- if (hasSubscriptOperator(State, ContReg) &&
- backModifiable(State, ContReg)) {
- State = invalidateIteratorPositions(State, BackSym, BO_GE);
- State = setContainerData(State, ContReg, CData->newEnd(nullptr));
- } else {
- State = invalidateIteratorPositions(State, BackSym, BO_EQ);
- }
- auto newEndSym = BackSym;
- State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
- C.addTransition(State);
- }
- }
- void IteratorChecker::handlePushFront(CheckerContext &C,
- const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- // For deque-like containers invalidate all iterator positions
- auto State = C.getState();
- if (hasSubscriptOperator(State, ContReg)) {
- State = invalidateAllIteratorPositions(State, ContReg);
- C.addTransition(State);
- } else {
- const auto CData = getContainerData(State, ContReg);
- if (!CData)
- return;
- if (const auto BeginSym = CData->getBegin()) {
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto newBeginSym =
- SVB.evalBinOp(State, BO_Sub,
- nonloc::SymbolVal(BeginSym),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(BeginSym)).getAsSymbol();
- State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
- C.addTransition(State);
- }
- }
- }
- void IteratorChecker::handlePopFront(CheckerContext &C,
- const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- auto State = C.getState();
- const auto CData = getContainerData(State, ContReg);
- if (!CData)
- return;
- // For deque-like containers invalidate all iterator positions. For list-like
- // iterators only invalidate the first position
- if (const auto BeginSym = CData->getBegin()) {
- if (hasSubscriptOperator(State, ContReg)) {
- State = invalidateIteratorPositions(State, BeginSym, BO_LE);
- } else {
- State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
- }
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto newBeginSym =
- SVB.evalBinOp(State, BO_Add,
- nonloc::SymbolVal(BeginSym),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(BeginSym)).getAsSymbol();
- State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
- C.addTransition(State);
- }
- }
- void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
- return;
- // For deque-like containers invalidate all iterator positions. For
- // vector-like containers invalidate iterator positions after the insertion.
- const auto *Cont = Pos->getContainer();
- if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
- if (frontModifiable(State, Cont)) {
- State = invalidateAllIteratorPositions(State, Cont);
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, Cont)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, Cont, CData->newEnd(nullptr));
- }
- }
- C.addTransition(State);
- }
- }
- void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
- return;
- // For deque-like containers invalidate all iterator positions. For
- // vector-like containers invalidate iterator positions at and after the
- // deletion. For list-like containers only invalidate the deleted position.
- const auto *Cont = Pos->getContainer();
- if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
- if (frontModifiable(State, Cont)) {
- State = invalidateAllIteratorPositions(State, Cont);
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, Cont)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, Cont, CData->newEnd(nullptr));
- }
- }
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
- }
- C.addTransition(State);
- }
- void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const {
- auto State = C.getState();
- const auto *Pos1 = getIteratorPosition(State, Iter1);
- const auto *Pos2 = getIteratorPosition(State, Iter2);
- if (!Pos1 || !Pos2)
- return;
- // For deque-like containers invalidate all iterator positions. For
- // vector-like containers invalidate iterator positions at and after the
- // deletion range. For list-like containers only invalidate the deleted
- // position range [first..last].
- const auto *Cont = Pos1->getContainer();
- if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
- if (frontModifiable(State, Cont)) {
- State = invalidateAllIteratorPositions(State, Cont);
- } else {
- State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, Cont)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, Cont, CData->newEnd(nullptr));
- }
- }
- } else {
- State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
- Pos2->getOffset(), BO_LT);
- }
- C.addTransition(State);
- }
- void IteratorChecker::handleEraseAfter(CheckerContext &C,
- const SVal &Iter) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
- return;
- // Invalidate the deleted iterator position, which is the position of the
- // parameter plus one.
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto NextSym =
- SVB.evalBinOp(State, BO_Add,
- nonloc::SymbolVal(Pos->getOffset()),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(Pos->getOffset())).getAsSymbol();
- State = invalidateIteratorPositions(State, NextSym, BO_EQ);
- C.addTransition(State);
- }
- void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const {
- auto State = C.getState();
- const auto *Pos1 = getIteratorPosition(State, Iter1);
- const auto *Pos2 = getIteratorPosition(State, Iter2);
- if (!Pos1 || !Pos2)
- return;
- // Invalidate the deleted iterator position range (first..last)
- State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
- Pos2->getOffset(), BO_LT);
- C.addTransition(State);
- }
- IteratorPosition IteratorChecker::advancePosition(CheckerContext &C,
- OverloadedOperatorKind Op,
- const IteratorPosition &Pos,
- const SVal &Distance) const {
- auto State = C.getState();
- auto &SymMgr = C.getSymbolManager();
- auto &SVB = C.getSValBuilder();
- 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;
- if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
- // For concrete integers we can calculate the new position
- return Pos.setTo(SVB.evalBinOp(State, BinOp,
- nonloc::SymbolVal(Pos.getOffset()), *IntDist,
- SymMgr.getType(Pos.getOffset()))
- .getAsSymbol());
- } else {
- // For other symbols create a new symbol to keep expressions simple
- const auto &LCtx = C.getLocationContext();
- const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx,
- SymMgr.getType(Pos.getOffset()),
- C.blockCount());
- State = assumeNoOverflow(State, NewPosSym, 4);
- return Pos.setTo(NewPosSym);
- }
- }
- void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
- const SVal &Val, CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
- R->markInteresting(Val);
- C.emitReport(std::move(R));
- }
- void IteratorChecker::reportMismatchedBug(const StringRef &Message,
- const SVal &Val1, const SVal &Val2,
- CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
- R->markInteresting(Val1);
- R->markInteresting(Val2);
- C.emitReport(std::move(R));
- }
- void IteratorChecker::reportMismatchedBug(const StringRef &Message,
- const SVal &Val, const MemRegion *Reg,
- CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
- R->markInteresting(Val);
- R->markInteresting(Reg);
- C.emitReport(std::move(R));
- }
- void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
- const SVal &Val, CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
- R->markInteresting(Val);
- C.emitReport(std::move(R));
- }
- namespace {
- bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
- bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
- bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
- bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
- BinaryOperator::Opcode Opc);
- bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
- BinaryOperator::Opcode Opc);
- const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
- const MemRegion *Reg);
- SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
- SymbolRef OldSym, SymbolRef NewSym);
- 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 isBeginCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- return IdInfo->getName().endswith_lower("begin");
- }
- bool isEndCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- return IdInfo->getName().endswith_lower("end");
- }
- bool isAssignCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() > 2)
- return false;
- return IdInfo->getName() == "assign";
- }
- bool isClearCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() > 0)
- return false;
- return IdInfo->getName() == "clear";
- }
- bool isPushBackCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() != 1)
- return false;
- return IdInfo->getName() == "push_back";
- }
- bool isEmplaceBackCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() < 1)
- return false;
- return IdInfo->getName() == "emplace_back";
- }
- bool isPopBackCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() > 0)
- return false;
- return IdInfo->getName() == "pop_back";
- }
- bool isPushFrontCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() != 1)
- return false;
- return IdInfo->getName() == "push_front";
- }
- bool isEmplaceFrontCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() < 1)
- return false;
- return IdInfo->getName() == "emplace_front";
- }
- bool isPopFrontCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() > 0)
- return false;
- return IdInfo->getName() == "pop_front";
- }
- 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 isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
- bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
- return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
- }
- 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;
- }
- bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
- const auto *CRD = getCXXRecordDecl(State, Reg);
- if (!CRD)
- return false;
- for (const auto *Method : CRD->methods()) {
- if (!Method->isOverloadedOperator())
- continue;
- const auto OPK = Method->getOverloadedOperator();
- if (OPK == OO_Subscript) {
- return true;
- }
- }
- return false;
- }
- bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
- const auto *CRD = getCXXRecordDecl(State, Reg);
- if (!CRD)
- return false;
- for (const auto *Method : CRD->methods()) {
- if (!Method->getDeclName().isIdentifier())
- continue;
- if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
- return true;
- }
- }
- return false;
- }
- bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
- const auto *CRD = getCXXRecordDecl(State, Reg);
- if (!CRD)
- return false;
- for (const auto *Method : CRD->methods()) {
- if (!Method->getDeclName().isIdentifier())
- continue;
- if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
- return true;
- }
- }
- return false;
- }
- const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
- const MemRegion *Reg) {
- auto TI = getDynamicTypeInfo(State, Reg);
- if (!TI.isValid())
- return nullptr;
- auto Type = TI.getType();
- if (const auto *RefT = Type->getAs<ReferenceType>()) {
- Type = RefT->getPointeeType();
- }
- return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
- }
- SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
- const auto *CDataPtr = getContainerData(State, Cont);
- if (!CDataPtr)
- return nullptr;
- return CDataPtr->getBegin();
- }
- SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
- const auto *CDataPtr = getContainerData(State, Cont);
- if (!CDataPtr)
- return nullptr;
- return CDataPtr->getEnd();
- }
- ProgramStateRef createContainerBegin(ProgramStateRef State,
- const MemRegion *Cont, const Expr *E,
- QualType T, const LocationContext *LCtx,
- unsigned BlockCount) {
- // Only create if it does not exist
- const auto *CDataPtr = getContainerData(State, Cont);
- if (CDataPtr && CDataPtr->getBegin())
- return State;
- auto &SymMgr = State->getSymbolManager();
- const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
- "begin");
- State = assumeNoOverflow(State, Sym, 4);
- if (CDataPtr) {
- const auto CData = CDataPtr->newBegin(Sym);
- return setContainerData(State, Cont, CData);
- }
- const auto CData = ContainerData::fromBegin(Sym);
- return setContainerData(State, Cont, CData);
- }
- ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
- const Expr *E, QualType T,
- const LocationContext *LCtx,
- unsigned BlockCount) {
- // Only create if it does not exist
- const auto *CDataPtr = getContainerData(State, Cont);
- if (CDataPtr && CDataPtr->getEnd())
- return State;
- auto &SymMgr = State->getSymbolManager();
- const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
- "end");
- State = assumeNoOverflow(State, Sym, 4);
- if (CDataPtr) {
- const auto CData = CDataPtr->newEnd(Sym);
- return setContainerData(State, Cont, CData);
- }
- const auto CData = ContainerData::fromEnd(Sym);
- return setContainerData(State, Cont, CData);
- }
- const ContainerData *getContainerData(ProgramStateRef State,
- const MemRegion *Cont) {
- return State->get<ContainerMap>(Cont);
- }
- ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
- const ContainerData &CData) {
- return State->set<ContainerMap>(Cont, CData);
- }
- 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 removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
- if (auto Reg = Val.getAsRegion()) {
- Reg = Reg->getMostDerivedObjectRegion();
- return State->remove<IteratorRegionMap>(Reg);
- } else if (const auto Sym = Val.getAsSymbol()) {
- return State->remove<IteratorSymbolMap>(Sym);
- } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
- return State->remove<IteratorRegionMap>(LCVal->getRegion());
- }
- return nullptr;
- }
- ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
- SymbolRef Sym2, bool Equal) {
- auto &SVB = State->getStateManager().getSValBuilder();
- // FIXME: This code should be reworked as follows:
- // 1. Subtract the operands using evalBinOp().
- // 2. Assume that the result doesn't overflow.
- // 3. Compare the result to 0.
- // 4. Assume the result of the comparison.
- const auto comparison =
- SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
- nonloc::SymbolVal(Sym2), SVB.getConditionType());
- assert(comparison.getAs<DefinedSVal>() &&
- "Symbol comparison must be a `DefinedSVal`");
- auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
- if (!NewState)
- return nullptr;
- if (const auto CompSym = comparison.getAsSymbol()) {
- assert(isa<SymIntExpr>(CompSym) &&
- "Symbol comparison must be a `SymIntExpr`");
- assert(BinaryOperator::isComparisonOp(
- cast<SymIntExpr>(CompSym)->getOpcode()) &&
- "Symbol comparison must be a comparison");
- return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
- }
- return NewState;
- }
- bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
- auto RegionMap = State->get<IteratorRegionMap>();
- for (const auto Reg : RegionMap) {
- if (Reg.second.getContainer() == Cont)
- return true;
- }
- auto SymbolMap = State->get<IteratorSymbolMap>();
- for (const auto Sym : SymbolMap) {
- if (Sym.second.getContainer() == Cont)
- return true;
- }
- return false;
- }
- bool isBoundThroughLazyCompoundVal(const Environment &Env,
- const MemRegion *Reg) {
- for (const auto Binding: Env) {
- if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
- if (LCVal->getRegion() == Reg)
- return true;
- }
- }
- return false;
- }
- // 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;
- }
- template <typename Condition, typename Process>
- ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
- Process Proc) {
- auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
- auto RegionMap = State->get<IteratorRegionMap>();
- bool Changed = false;
- for (const auto Reg : RegionMap) {
- if (Cond(Reg.second)) {
- RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
- Changed = true;
- }
- }
- if (Changed)
- State = State->set<IteratorRegionMap>(RegionMap);
- auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
- auto SymbolMap = State->get<IteratorSymbolMap>();
- Changed = false;
- for (const auto Sym : SymbolMap) {
- if (Cond(Sym.second)) {
- SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
- Changed = true;
- }
- }
- if (Changed)
- State = State->set<IteratorSymbolMap>(SymbolMap);
- return State;
- }
- ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
- const MemRegion *Cont) {
- auto MatchCont = [&](const IteratorPosition &Pos) {
- return Pos.getContainer() == Cont;
- };
- auto Invalidate = [&](const IteratorPosition &Pos) {
- return Pos.invalidate();
- };
- return processIteratorPositions(State, MatchCont, Invalidate);
- }
- ProgramStateRef
- invalidateAllIteratorPositionsExcept(ProgramStateRef State,
- const MemRegion *Cont, SymbolRef Offset,
- BinaryOperator::Opcode Opc) {
- auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
- return Pos.getContainer() == Cont &&
- !compare(State, Pos.getOffset(), Offset, Opc);
- };
- auto Invalidate = [&](const IteratorPosition &Pos) {
- return Pos.invalidate();
- };
- return processIteratorPositions(State, MatchContAndCompare, Invalidate);
- }
- ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
- SymbolRef Offset,
- BinaryOperator::Opcode Opc) {
- auto Compare = [&](const IteratorPosition &Pos) {
- return compare(State, Pos.getOffset(), Offset, Opc);
- };
- auto Invalidate = [&](const IteratorPosition &Pos) {
- return Pos.invalidate();
- };
- return processIteratorPositions(State, Compare, Invalidate);
- }
- ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
- SymbolRef Offset1,
- BinaryOperator::Opcode Opc1,
- SymbolRef Offset2,
- BinaryOperator::Opcode Opc2) {
- auto Compare = [&](const IteratorPosition &Pos) {
- return compare(State, Pos.getOffset(), Offset1, Opc1) &&
- compare(State, Pos.getOffset(), Offset2, Opc2);
- };
- auto Invalidate = [&](const IteratorPosition &Pos) {
- return Pos.invalidate();
- };
- return processIteratorPositions(State, Compare, Invalidate);
- }
- ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
- const MemRegion *Cont,
- const MemRegion *NewCont) {
- auto MatchCont = [&](const IteratorPosition &Pos) {
- return Pos.getContainer() == Cont;
- };
- auto ReAssign = [&](const IteratorPosition &Pos) {
- return Pos.reAssign(NewCont);
- };
- return processIteratorPositions(State, MatchCont, ReAssign);
- }
- ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
- const MemRegion *Cont,
- const MemRegion *NewCont,
- SymbolRef Offset,
- BinaryOperator::Opcode Opc) {
- auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
- return Pos.getContainer() == Cont &&
- !compare(State, Pos.getOffset(), Offset, Opc);
- };
- auto ReAssign = [&](const IteratorPosition &Pos) {
- return Pos.reAssign(NewCont);
- };
- return processIteratorPositions(State, MatchContAndCompare, ReAssign);
- }
- // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
- // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
- // position offsets where `CondSym` is true.
- ProgramStateRef rebaseSymbolInIteratorPositionsIf(
- ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
- SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
- auto LessThanEnd = [&](const IteratorPosition &Pos) {
- return compare(State, Pos.getOffset(), CondSym, Opc);
- };
- auto RebaseSymbol = [&](const IteratorPosition &Pos) {
- return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
- NewSym));
- };
- return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
- }
- // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
- // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
- // `OrigExpr`.
- SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
- SymbolRef OrigExpr, SymbolRef OldExpr,
- SymbolRef NewSym) {
- auto &SymMgr = SVB.getSymbolManager();
- auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
- nonloc::SymbolVal(OldExpr),
- SymMgr.getType(OrigExpr));
- const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
- if (!DiffInt)
- return OrigExpr;
- return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
- SymMgr.getType(OrigExpr)).getAsSymbol();
- }
- bool isZero(ProgramStateRef State, const NonLoc &Val) {
- auto &BVF = State->getBasicVals();
- return compare(State, Val,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
- BO_EQ);
- }
- bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
- const auto End = CData->getEnd();
- if (End) {
- if (isEqual(State, Pos.getOffset(), End)) {
- return true;
- }
- }
- return false;
- }
- bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
- const auto Beg = CData->getBegin();
- if (Beg) {
- if (isLess(State, Pos.getOffset(), Beg)) {
- return true;
- }
- }
- return false;
- }
- bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
- const auto End = CData->getEnd();
- if (End) {
- if (isGreater(State, Pos.getOffset(), End)) {
- return true;
- }
- }
- return false;
- }
- bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_LT);
- }
- bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_GT);
- }
- bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_EQ);
- }
- 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
- void ento::registerIteratorModeling(CheckerManager &mgr) {
- mgr.registerChecker<IteratorChecker>();
- }
- bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
- return true;
- }
- #define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &Mgr) { \
- auto *checker = Mgr.getChecker<IteratorChecker>(); \
- checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
- checker->CheckNames[IteratorChecker::CK_##name] = \
- Mgr.getCurrentCheckName(); \
- } \
- \
- bool ento::shouldRegister##name(const LangOptions &LO) { \
- return true; \
- }
- REGISTER_CHECKER(IteratorRangeChecker)
- REGISTER_CHECKER(MismatchedIteratorChecker)
- REGISTER_CHECKER(InvalidatedIteratorChecker)
|