1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069 |
- //===-- ContainerModeling.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 modeling-checker for modeling STL container-like containers.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/AST/DeclTemplate.h"
- #include "clang/Driver/DriverDiagnostic.h"
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
- #include "Iterator.h"
- #include <utility>
- using namespace clang;
- using namespace ento;
- using namespace iterator;
- namespace {
- class ContainerModeling
- : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
- void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,
- SVal Cont) const;
- void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,
- SVal Cont) const;
- void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,
- SVal OldCont = UndefinedVal()) const;
- void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const;
- void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const;
- void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
- void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
- void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
- void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
- void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const;
- void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const;
- void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const;
- void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const;
- void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1,
- SVal Iter2) const;
- const NoteTag *getChangeTag(CheckerContext &C, StringRef Text,
- const MemRegion *ContReg,
- const Expr *ContE) const;
- void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
- const char *Sep) const override;
- public:
- ContainerModeling() = default;
- void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
- void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
- using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
- const Expr *) const;
- using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
- SVal) const;
- using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal,
- SVal) const;
- CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {
- {{"clear", 0}, &ContainerModeling::handleClear},
- {{"assign", 2}, &ContainerModeling::handleAssign},
- {{"push_back", 1}, &ContainerModeling::handlePushBack},
- {{"emplace_back", 1}, &ContainerModeling::handlePushBack},
- {{"pop_back", 0}, &ContainerModeling::handlePopBack},
- {{"push_front", 1}, &ContainerModeling::handlePushFront},
- {{"emplace_front", 1}, &ContainerModeling::handlePushFront},
- {{"pop_front", 0}, &ContainerModeling::handlePopFront},
- };
- CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
- {{"insert", 2}, &ContainerModeling::handleInsert},
- {{"emplace", 2}, &ContainerModeling::handleInsert},
- {{"erase", 1}, &ContainerModeling::handleErase},
- {{"erase_after", 1}, &ContainerModeling::handleEraseAfter},
- };
- CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
- {{"erase", 2}, &ContainerModeling::handleErase},
- {{"erase_after", 2}, &ContainerModeling::handleEraseAfter},
- };
- };
- bool isBeginCall(const FunctionDecl *Func);
- bool isEndCall(const FunctionDecl *Func);
- 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);
- ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
- const ContainerData &CData);
- 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);
- SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
- SymbolRef OldSym, SymbolRef NewSym);
- bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
- } // namespace
- void ContainerModeling::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
- const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!Func)
- return;
- if (Func->isOverloadedOperator()) {
- const auto Op = Func->getOverloadedOperator();
- if (Op == OO_Equal) {
- // Overloaded 'operator=' must be a non-static member function.
- const auto *InstCall = cast<CXXInstanceCall>(&Call);
- if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
- handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
- Call.getArgSVal(0));
- return;
- }
- handleAssignment(C, InstCall->getCXXThisVal());
- return;
- }
- } else {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
- if (Handler0) {
- (this->**Handler0)(C, InstCall->getCXXThisVal(),
- InstCall->getCXXThisExpr());
- return;
- }
- const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
- if (Handler1) {
- (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
- return;
- }
- const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
- if (Handler2) {
- (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
- Call.getArgSVal(1));
- return;
- }
- const auto *OrigExpr = Call.getOriginExpr();
- if (!OrigExpr)
- return;
- if (isBeginCall(Func)) {
- handleBegin(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal());
- return;
- }
- if (isEndCall(Func)) {
- handleEnd(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal());
- return;
- }
- }
- }
- }
- void ContainerModeling::checkLiveSymbols(ProgramStateRef State,
- SymbolReaper &SR) const {
- // Keep symbolic expressions of container begins and ends alive
- 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 ContainerModeling::checkDeadSymbols(SymbolReaper &SR,
- CheckerContext &C) const {
- // Cleanup
- auto State = C.getState();
-
- 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 ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
- SVal RetVal, 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 ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
- SVal RetVal, 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 ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
- const Expr *CE, 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->newBegin(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 ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,
- const Expr *ContE) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- // The assign() operation invalidates all the iterators
- auto State = C.getState();
- State = invalidateAllIteratorPositions(State, ContReg);
- C.addTransition(State);
- }
- void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,
- const Expr *ContE) 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;
- }
- }
- }
- const NoteTag *ChangeTag =
- getChangeTag(C, "became empty", ContReg, ContE);
- State = invalidateAllIteratorPositions(State, ContReg);
- C.addTransition(State, ChangeTag);
- }
- void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
- const Expr *ContE) 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();
- const NoteTag *ChangeTag =
- getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);
- State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
- C.addTransition(State, ChangeTag);
- }
- }
- void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
- const Expr *ContE) 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();
- const NoteTag *ChangeTag =
- getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE);
- // 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, ChangeTag);
- }
- }
- void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
- const Expr *ContE) 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();
- const NoteTag *ChangeTag =
- getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);
- State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
- C.addTransition(State, ChangeTag);
- }
- }
- }
- void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
- const Expr *ContE) 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();
- const NoteTag *ChangeTag =
- getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);
- State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
- C.addTransition(State, ChangeTag);
- }
- }
- void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,
- SVal Iter) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- 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.
- if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
- if (frontModifiable(State, ContReg)) {
- State = invalidateAllIteratorPositions(State, ContReg);
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, ContReg)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, ContReg, CData->newEnd(nullptr));
- }
- }
- C.addTransition(State);
- }
- }
- void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,
- SVal Iter) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- 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.
- if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
- if (frontModifiable(State, ContReg)) {
- State = invalidateAllIteratorPositions(State, ContReg);
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, ContReg)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, ContReg, CData->newEnd(nullptr));
- }
- }
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
- }
- C.addTransition(State);
- }
- void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,
- SVal Iter2) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
- ContReg = ContReg->getMostDerivedObjectRegion();
- 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].
- if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
- if (frontModifiable(State, ContReg)) {
- State = invalidateAllIteratorPositions(State, ContReg);
- } else {
- State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, ContReg)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, ContReg, CData->newEnd(nullptr));
- }
- }
- } else {
- State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
- Pos2->getOffset(), BO_LT);
- }
- C.addTransition(State);
- }
- void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
- 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 ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
- SVal Iter1, 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);
- }
- const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
- StringRef Text,
- const MemRegion *ContReg,
- const Expr *ContE) const {
- StringRef Name;
- // First try to get the name of the variable from the region
- if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {
- Name = DR->getDecl()->getName();
- // If the region is not a `DeclRegion` then use the expression instead
- } else if (const auto *DRE =
- dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {
- Name = DRE->getDecl()->getName();
- }
- return C.getNoteTag(
- [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
- if (!BR.isInteresting(ContReg))
- return "";
- SmallString<256> Msg;
- llvm::raw_svector_ostream Out(Msg);
- Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )
- << Text;
- return std::string(Out.str());
- });
- }
- void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const {
- auto ContMap = State->get<ContainerMap>();
- if (!ContMap.isEmpty()) {
- Out << Sep << "Container Data :" << NL;
- for (const auto &Cont : ContMap) {
- Cont.first->dumpToStream(Out);
- Out << " : [ ";
- const auto CData = Cont.second;
- if (CData.getBegin())
- CData.getBegin()->dumpToStream(Out);
- else
- Out << "<Unknown>";
- Out << " .. ";
- if (CData.getEnd())
- CData.getEnd()->dumpToStream(Out);
- else
- Out << "<Unknown>";
- Out << " ]";
- }
- }
- }
- namespace {
- bool isBeginCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- return IdInfo->getName().endswith_insensitive("begin");
- }
- bool isEndCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- return IdInfo->getName().endswith_insensitive("end");
- }
- 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();
- }
- 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;
- }
- 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);
- }
- ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
- const ContainerData &CData) {
- return State->set<ContainerMap>(Cont, CData);
- }
- 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 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;
- }
- } // namespace
- void ento::registerContainerModeling(CheckerManager &mgr) {
- mgr.registerChecker<ContainerModeling>();
- }
- bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {
- if (!mgr.getLangOpts().CPlusPlus)
- return false;
- if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {
- mgr.getASTContext().getDiagnostics().Report(
- diag::err_analyzer_checker_incompatible_analyzer_option)
- << "aggressive-binary-operation-simplification" << "false";
- return false;
- }
- return true;
- }
|