1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329 |
- //===-- 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/DynamicTypeMap.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 SymbolRef Sym);
- ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
- const SymbolRef Sym);
- 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",
- /*SuppressOnSink=*/true));
- 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",
- /*SuppressOnSink=*/true));
- }
- 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) {
- verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- InstCall->getCXXThisVal(),
- Call.getArgSVal(0));
- }
- } else {
- if (Call.getNumArgs() >= 2) {
- 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)) {
- const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
- if (Func->getParamDecl(0)->getType()->isRValueReferenceType()) {
- handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
- Call.getArgSVal(0));
- } else {
- handleAssign(C, InstCall->getCXXThisVal());
- }
- } 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);
- } else {
- handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getArgSVal(1), Op);
- }
- } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (Call.getNumArgs() >= 1) {
- handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- Call.getReturnValue(),
- InstCall->getCXXThisVal(), Call.getArgSVal(0));
- }
- } else {
- if (Call.getNumArgs() >= 2) {
- handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- Call.getReturnValue(), Call.getArgSVal(0),
- Call.getArgSVal(1));
- }
- }
- } else if (isIncrementOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
- Call.getNumArgs());
- } else {
- handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getNumArgs());
- }
- } else if (isDecrementOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
- Call.getNumArgs());
- } else {
- handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getNumArgs());
- }
- }
- } else {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (isAssignCall(Func)) {
- handleAssign(C, InstCall->getCXXThisVal());
- } else if (isClearCall(Func)) {
- handleClear(C, InstCall->getCXXThisVal());
- } else if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
- handlePushBack(C, InstCall->getCXXThisVal());
- } else if (isPopBackCall(Func)) {
- handlePopBack(C, InstCall->getCXXThisVal());
- } else if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
- handlePushFront(C, InstCall->getCXXThisVal());
- } else if (isPopFrontCall(Func)) {
- handlePopFront(C, InstCall->getCXXThisVal());
- } else if (isInsertCall(Func) || isEmplaceCall(Func)) {
- handleInsert(C, Call.getArgSVal(0));
- } else if (isEraseCall(Func)) {
- if (Call.getNumArgs() == 1) {
- handleErase(C, Call.getArgSVal(0));
- } else if (Call.getNumArgs() == 2) {
- handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
- }
- } else if (isEraseAfterCall(Func)) {
- if (Call.getNumArgs() == 1) {
- handleEraseAfter(C, Call.getArgSVal(0));
- } else if (Call.getNumArgs() == 2) {
- handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
- }
- }
- }
- 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.generateNonFatalErrorNode(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.generateNonFatalErrorNode(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.generateNonFatalErrorNode(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.generateNonFatalErrorNode(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) {
- auto &SymMgr = C.getSymbolManager();
- BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
- C.getASTContext().LongTy, C.blockCount());
- State = assumeNoOverflow(State, BeginSym, 4);
- State = createContainerBegin(State, ContReg, BeginSym);
- }
- 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) {
- auto &SymMgr = C.getSymbolManager();
- EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
- C.getASTContext().LongTy, C.blockCount());
- State = assumeNoOverflow(State, EndSym, 4);
- State = createContainerEnd(State, ContReg, EndSym);
- }
- 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 = llvm::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 = llvm::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 = llvm::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 = llvm::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 SymbolRef Sym) {
- // Only create if it does not exist
- const auto *CDataPtr = getContainerData(State, Cont);
- if (CDataPtr) {
- if (CDataPtr->getBegin()) {
- return State;
- }
- 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 SymbolRef Sym) {
- // Only create if it does not exist
- const auto *CDataPtr = getContainerData(State, Cont);
- if (CDataPtr) {
- if (CDataPtr->getEnd()) {
- return State;
- }
- 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)
|