Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
35266 views
//===-- ContainerModeling.cpp -------------------------------------*- C++ -*--//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Defines a modeling-checker for modeling STL container-like containers.9//10//===----------------------------------------------------------------------===//1112#include "clang/AST/DeclTemplate.h"13#include "clang/Driver/DriverDiagnostic.h"14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"15#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"16#include "clang/StaticAnalyzer/Core/Checker.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"2122#include "Iterator.h"2324#include <utility>2526using namespace clang;27using namespace ento;28using namespace iterator;2930namespace {3132class ContainerModeling33: public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {3435void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,36SVal Cont) const;37void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,38SVal Cont) const;39void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,40SVal OldCont = UndefinedVal()) const;41void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const;42void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const;43void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;44void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;45void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;46void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;47void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const;48void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const;49void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const;50void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const;51void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1,52SVal Iter2) const;53const NoteTag *getChangeTag(CheckerContext &C, StringRef Text,54const MemRegion *ContReg,55const Expr *ContE) const;56void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,57const char *Sep) const override;5859public:60ContainerModeling() = default;6162void checkPostCall(const CallEvent &Call, CheckerContext &C) const;63void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;64void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;6566using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,67const Expr *) const;68using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,69SVal) const;70using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal,71SVal) const;7273CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {74{{CDM::CXXMethod, {"clear"}, 0}, &ContainerModeling::handleClear},75{{CDM::CXXMethod, {"assign"}, 2}, &ContainerModeling::handleAssign},76{{CDM::CXXMethod, {"push_back"}, 1}, &ContainerModeling::handlePushBack},77{{CDM::CXXMethod, {"emplace_back"}, 1},78&ContainerModeling::handlePushBack},79{{CDM::CXXMethod, {"pop_back"}, 0}, &ContainerModeling::handlePopBack},80{{CDM::CXXMethod, {"push_front"}, 1},81&ContainerModeling::handlePushFront},82{{CDM::CXXMethod, {"emplace_front"}, 1},83&ContainerModeling::handlePushFront},84{{CDM::CXXMethod, {"pop_front"}, 0}, &ContainerModeling::handlePopFront},85};8687CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {88{{CDM::CXXMethod, {"insert"}, 2}, &ContainerModeling::handleInsert},89{{CDM::CXXMethod, {"emplace"}, 2}, &ContainerModeling::handleInsert},90{{CDM::CXXMethod, {"erase"}, 1}, &ContainerModeling::handleErase},91{{CDM::CXXMethod, {"erase_after"}, 1},92&ContainerModeling::handleEraseAfter},93};9495CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {96{{CDM::CXXMethod, {"erase"}, 2}, &ContainerModeling::handleErase},97{{CDM::CXXMethod, {"erase_after"}, 2},98&ContainerModeling::handleEraseAfter},99};100};101102bool isBeginCall(const FunctionDecl *Func);103bool isEndCall(const FunctionDecl *Func);104bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);105bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);106bool backModifiable(ProgramStateRef State, const MemRegion *Reg);107SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);108SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);109ProgramStateRef createContainerBegin(ProgramStateRef State,110const MemRegion *Cont, const Expr *E,111QualType T, const LocationContext *LCtx,112unsigned BlockCount);113ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,114const Expr *E, QualType T,115const LocationContext *LCtx,116unsigned BlockCount);117ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,118const ContainerData &CData);119ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,120const MemRegion *Cont);121ProgramStateRef122invalidateAllIteratorPositionsExcept(ProgramStateRef State,123const MemRegion *Cont, SymbolRef Offset,124BinaryOperator::Opcode Opc);125ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,126SymbolRef Offset,127BinaryOperator::Opcode Opc);128ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,129SymbolRef Offset1,130BinaryOperator::Opcode Opc1,131SymbolRef Offset2,132BinaryOperator::Opcode Opc2);133ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,134const MemRegion *Cont,135const MemRegion *NewCont);136ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,137const MemRegion *Cont,138const MemRegion *NewCont,139SymbolRef Offset,140BinaryOperator::Opcode Opc);141ProgramStateRef rebaseSymbolInIteratorPositionsIf(142ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,143SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);144SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,145SymbolRef OldSym, SymbolRef NewSym);146bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);147148} // namespace149150void ContainerModeling::checkPostCall(const CallEvent &Call,151CheckerContext &C) const {152const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());153if (!Func)154return;155156if (Func->isOverloadedOperator()) {157const auto Op = Func->getOverloadedOperator();158if (Op == OO_Equal) {159// Overloaded 'operator=' must be a non-static member function.160const auto *InstCall = cast<CXXInstanceCall>(&Call);161if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {162handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),163Call.getArgSVal(0));164return;165}166167handleAssignment(C, InstCall->getCXXThisVal());168return;169}170} else {171if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {172const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);173if (Handler0) {174(this->**Handler0)(C, InstCall->getCXXThisVal(),175InstCall->getCXXThisExpr());176return;177}178179const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);180if (Handler1) {181(this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));182return;183}184185const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);186if (Handler2) {187(this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),188Call.getArgSVal(1));189return;190}191192const auto *OrigExpr = Call.getOriginExpr();193if (!OrigExpr)194return;195196if (isBeginCall(Func)) {197handleBegin(C, OrigExpr, Call.getReturnValue(),198InstCall->getCXXThisVal());199return;200}201202if (isEndCall(Func)) {203handleEnd(C, OrigExpr, Call.getReturnValue(),204InstCall->getCXXThisVal());205return;206}207}208}209}210211void ContainerModeling::checkLiveSymbols(ProgramStateRef State,212SymbolReaper &SR) const {213// Keep symbolic expressions of container begins and ends alive214auto ContMap = State->get<ContainerMap>();215for (const auto &Cont : ContMap) {216const auto CData = Cont.second;217if (CData.getBegin()) {218SR.markLive(CData.getBegin());219if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))220SR.markLive(SIE->getLHS());221}222if (CData.getEnd()) {223SR.markLive(CData.getEnd());224if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))225SR.markLive(SIE->getLHS());226}227}228}229230void ContainerModeling::checkDeadSymbols(SymbolReaper &SR,231CheckerContext &C) const {232// Cleanup233auto State = C.getState();234235auto ContMap = State->get<ContainerMap>();236for (const auto &Cont : ContMap) {237if (!SR.isLiveRegion(Cont.first)) {238// We must keep the container data while it has live iterators to be able239// to compare them to the begin and the end of the container.240if (!hasLiveIterators(State, Cont.first)) {241State = State->remove<ContainerMap>(Cont.first);242}243}244}245246C.addTransition(State);247}248249void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,250SVal RetVal, SVal Cont) const {251const auto *ContReg = Cont.getAsRegion();252if (!ContReg)253return;254255ContReg = ContReg->getMostDerivedObjectRegion();256257// If the container already has a begin symbol then use it. Otherwise first258// create a new one.259auto State = C.getState();260auto BeginSym = getContainerBegin(State, ContReg);261if (!BeginSym) {262State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,263C.getLocationContext(), C.blockCount());264BeginSym = getContainerBegin(State, ContReg);265}266State = setIteratorPosition(State, RetVal,267IteratorPosition::getPosition(ContReg, BeginSym));268C.addTransition(State);269}270271void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,272SVal RetVal, SVal Cont) const {273const auto *ContReg = Cont.getAsRegion();274if (!ContReg)275return;276277ContReg = ContReg->getMostDerivedObjectRegion();278279// If the container already has an end symbol then use it. Otherwise first280// create a new one.281auto State = C.getState();282auto EndSym = getContainerEnd(State, ContReg);283if (!EndSym) {284State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,285C.getLocationContext(), C.blockCount());286EndSym = getContainerEnd(State, ContReg);287}288State = setIteratorPosition(State, RetVal,289IteratorPosition::getPosition(ContReg, EndSym));290C.addTransition(State);291}292293void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,294const Expr *CE, SVal OldCont) const {295const auto *ContReg = Cont.getAsRegion();296if (!ContReg)297return;298299ContReg = ContReg->getMostDerivedObjectRegion();300301// Assignment of a new value to a container always invalidates all its302// iterators303auto State = C.getState();304const auto CData = getContainerData(State, ContReg);305if (CData) {306State = invalidateAllIteratorPositions(State, ContReg);307}308309// In case of move, iterators of the old container (except the past-end310// iterators) remain valid but refer to the new container311if (!OldCont.isUndef()) {312const auto *OldContReg = OldCont.getAsRegion();313if (OldContReg) {314OldContReg = OldContReg->getMostDerivedObjectRegion();315const auto OldCData = getContainerData(State, OldContReg);316if (OldCData) {317if (const auto OldEndSym = OldCData->getEnd()) {318// If we already assigned an "end" symbol to the old container, then319// first reassign all iterator positions to the new container which320// are not past the container (thus not greater or equal to the321// current "end" symbol).322State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,323OldEndSym, BO_GE);324auto &SymMgr = C.getSymbolManager();325auto &SVB = C.getSValBuilder();326// Then generate and assign a new "end" symbol for the new container.327auto NewEndSym =328SymMgr.conjureSymbol(CE, C.getLocationContext(),329C.getASTContext().LongTy, C.blockCount());330State = assumeNoOverflow(State, NewEndSym, 4);331if (CData) {332State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));333} else {334State = setContainerData(State, ContReg,335ContainerData::fromEnd(NewEndSym));336}337// Finally, replace the old "end" symbol in the already reassigned338// iterator positions with the new "end" symbol.339State = rebaseSymbolInIteratorPositionsIf(340State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);341} else {342// There was no "end" symbol assigned yet to the old container,343// so reassign all iterator positions to the new container.344State = reassignAllIteratorPositions(State, OldContReg, ContReg);345}346if (const auto OldBeginSym = OldCData->getBegin()) {347// If we already assigned a "begin" symbol to the old container, then348// assign it to the new container and remove it from the old one.349if (CData) {350State =351setContainerData(State, ContReg, CData->newBegin(OldBeginSym));352} else {353State = setContainerData(State, ContReg,354ContainerData::fromBegin(OldBeginSym));355}356State =357setContainerData(State, OldContReg, OldCData->newBegin(nullptr));358}359} else {360// There was neither "begin" nor "end" symbol assigned yet to the old361// container, so reassign all iterator positions to the new container.362State = reassignAllIteratorPositions(State, OldContReg, ContReg);363}364}365}366C.addTransition(State);367}368369void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,370const Expr *ContE) const {371const auto *ContReg = Cont.getAsRegion();372if (!ContReg)373return;374375ContReg = ContReg->getMostDerivedObjectRegion();376377// The assign() operation invalidates all the iterators378auto State = C.getState();379State = invalidateAllIteratorPositions(State, ContReg);380C.addTransition(State);381}382383void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,384const Expr *ContE) const {385const auto *ContReg = Cont.getAsRegion();386if (!ContReg)387return;388389ContReg = ContReg->getMostDerivedObjectRegion();390391// The clear() operation invalidates all the iterators, except the past-end392// iterators of list-like containers393auto State = C.getState();394if (!hasSubscriptOperator(State, ContReg) ||395!backModifiable(State, ContReg)) {396const auto CData = getContainerData(State, ContReg);397if (CData) {398if (const auto EndSym = CData->getEnd()) {399State =400invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);401C.addTransition(State);402return;403}404}405}406const NoteTag *ChangeTag =407getChangeTag(C, "became empty", ContReg, ContE);408State = invalidateAllIteratorPositions(State, ContReg);409C.addTransition(State, ChangeTag);410}411412void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,413const Expr *ContE) const {414const auto *ContReg = Cont.getAsRegion();415if (!ContReg)416return;417418ContReg = ContReg->getMostDerivedObjectRegion();419420// For deque-like containers invalidate all iterator positions421auto State = C.getState();422if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {423State = invalidateAllIteratorPositions(State, ContReg);424C.addTransition(State);425return;426}427428const auto CData = getContainerData(State, ContReg);429if (!CData)430return;431432// For vector-like containers invalidate the past-end iterator positions433if (const auto EndSym = CData->getEnd()) {434if (hasSubscriptOperator(State, ContReg)) {435State = invalidateIteratorPositions(State, EndSym, BO_GE);436}437auto &SymMgr = C.getSymbolManager();438auto &BVF = SymMgr.getBasicVals();439auto &SVB = C.getSValBuilder();440const auto newEndSym =441SVB.evalBinOp(State, BO_Add,442nonloc::SymbolVal(EndSym),443nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),444SymMgr.getType(EndSym)).getAsSymbol();445const NoteTag *ChangeTag =446getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);447State = setContainerData(State, ContReg, CData->newEnd(newEndSym));448C.addTransition(State, ChangeTag);449}450}451452void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,453const Expr *ContE) const {454const auto *ContReg = Cont.getAsRegion();455if (!ContReg)456return;457458ContReg = ContReg->getMostDerivedObjectRegion();459460auto State = C.getState();461const auto CData = getContainerData(State, ContReg);462if (!CData)463return;464465if (const auto EndSym = CData->getEnd()) {466auto &SymMgr = C.getSymbolManager();467auto &BVF = SymMgr.getBasicVals();468auto &SVB = C.getSValBuilder();469const auto BackSym =470SVB.evalBinOp(State, BO_Sub,471nonloc::SymbolVal(EndSym),472nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),473SymMgr.getType(EndSym)).getAsSymbol();474const NoteTag *ChangeTag =475getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE);476// For vector-like and deque-like containers invalidate the last and the477// past-end iterator positions. For list-like containers only invalidate478// the last position479if (hasSubscriptOperator(State, ContReg) &&480backModifiable(State, ContReg)) {481State = invalidateIteratorPositions(State, BackSym, BO_GE);482State = setContainerData(State, ContReg, CData->newEnd(nullptr));483} else {484State = invalidateIteratorPositions(State, BackSym, BO_EQ);485}486auto newEndSym = BackSym;487State = setContainerData(State, ContReg, CData->newEnd(newEndSym));488C.addTransition(State, ChangeTag);489}490}491492void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,493const Expr *ContE) const {494const auto *ContReg = Cont.getAsRegion();495if (!ContReg)496return;497498ContReg = ContReg->getMostDerivedObjectRegion();499500// For deque-like containers invalidate all iterator positions501auto State = C.getState();502if (hasSubscriptOperator(State, ContReg)) {503State = invalidateAllIteratorPositions(State, ContReg);504C.addTransition(State);505} else {506const auto CData = getContainerData(State, ContReg);507if (!CData)508return;509510if (const auto BeginSym = CData->getBegin()) {511auto &SymMgr = C.getSymbolManager();512auto &BVF = SymMgr.getBasicVals();513auto &SVB = C.getSValBuilder();514const auto newBeginSym =515SVB.evalBinOp(State, BO_Sub,516nonloc::SymbolVal(BeginSym),517nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),518SymMgr.getType(BeginSym)).getAsSymbol();519const NoteTag *ChangeTag =520getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);521State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));522C.addTransition(State, ChangeTag);523}524}525}526527void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,528const Expr *ContE) const {529const auto *ContReg = Cont.getAsRegion();530if (!ContReg)531return;532533ContReg = ContReg->getMostDerivedObjectRegion();534535auto State = C.getState();536const auto CData = getContainerData(State, ContReg);537if (!CData)538return;539540// For deque-like containers invalidate all iterator positions. For list-like541// iterators only invalidate the first position542if (const auto BeginSym = CData->getBegin()) {543if (hasSubscriptOperator(State, ContReg)) {544State = invalidateIteratorPositions(State, BeginSym, BO_LE);545} else {546State = invalidateIteratorPositions(State, BeginSym, BO_EQ);547}548auto &SymMgr = C.getSymbolManager();549auto &BVF = SymMgr.getBasicVals();550auto &SVB = C.getSValBuilder();551const auto newBeginSym =552SVB.evalBinOp(State, BO_Add,553nonloc::SymbolVal(BeginSym),554nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),555SymMgr.getType(BeginSym)).getAsSymbol();556const NoteTag *ChangeTag =557getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);558State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));559C.addTransition(State, ChangeTag);560}561}562563void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,564SVal Iter) const {565const auto *ContReg = Cont.getAsRegion();566if (!ContReg)567return;568569ContReg = ContReg->getMostDerivedObjectRegion();570571auto State = C.getState();572const auto *Pos = getIteratorPosition(State, Iter);573if (!Pos)574return;575576// For deque-like containers invalidate all iterator positions. For577// vector-like containers invalidate iterator positions after the insertion.578if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {579if (frontModifiable(State, ContReg)) {580State = invalidateAllIteratorPositions(State, ContReg);581} else {582State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);583}584if (const auto *CData = getContainerData(State, ContReg)) {585if (const auto EndSym = CData->getEnd()) {586State = invalidateIteratorPositions(State, EndSym, BO_GE);587State = setContainerData(State, ContReg, CData->newEnd(nullptr));588}589}590C.addTransition(State);591}592}593594void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,595SVal Iter) const {596const auto *ContReg = Cont.getAsRegion();597if (!ContReg)598return;599600ContReg = ContReg->getMostDerivedObjectRegion();601602auto State = C.getState();603const auto *Pos = getIteratorPosition(State, Iter);604if (!Pos)605return;606607// For deque-like containers invalidate all iterator positions. For608// vector-like containers invalidate iterator positions at and after the609// deletion. For list-like containers only invalidate the deleted position.610if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {611if (frontModifiable(State, ContReg)) {612State = invalidateAllIteratorPositions(State, ContReg);613} else {614State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);615}616if (const auto *CData = getContainerData(State, ContReg)) {617if (const auto EndSym = CData->getEnd()) {618State = invalidateIteratorPositions(State, EndSym, BO_GE);619State = setContainerData(State, ContReg, CData->newEnd(nullptr));620}621}622} else {623State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);624}625C.addTransition(State);626}627628void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,629SVal Iter2) const {630const auto *ContReg = Cont.getAsRegion();631if (!ContReg)632return;633634ContReg = ContReg->getMostDerivedObjectRegion();635auto State = C.getState();636const auto *Pos1 = getIteratorPosition(State, Iter1);637const auto *Pos2 = getIteratorPosition(State, Iter2);638if (!Pos1 || !Pos2)639return;640641// For deque-like containers invalidate all iterator positions. For642// vector-like containers invalidate iterator positions at and after the643// deletion range. For list-like containers only invalidate the deleted644// position range [first..last].645if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {646if (frontModifiable(State, ContReg)) {647State = invalidateAllIteratorPositions(State, ContReg);648} else {649State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);650}651if (const auto *CData = getContainerData(State, ContReg)) {652if (const auto EndSym = CData->getEnd()) {653State = invalidateIteratorPositions(State, EndSym, BO_GE);654State = setContainerData(State, ContReg, CData->newEnd(nullptr));655}656}657} else {658State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,659Pos2->getOffset(), BO_LT);660}661C.addTransition(State);662}663664void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,665SVal Iter) const {666auto State = C.getState();667const auto *Pos = getIteratorPosition(State, Iter);668if (!Pos)669return;670671// Invalidate the deleted iterator position, which is the position of the672// parameter plus one.673auto &SymMgr = C.getSymbolManager();674auto &BVF = SymMgr.getBasicVals();675auto &SVB = C.getSValBuilder();676const auto NextSym =677SVB.evalBinOp(State, BO_Add,678nonloc::SymbolVal(Pos->getOffset()),679nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),680SymMgr.getType(Pos->getOffset())).getAsSymbol();681State = invalidateIteratorPositions(State, NextSym, BO_EQ);682C.addTransition(State);683}684685void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,686SVal Iter1, SVal Iter2) const {687auto State = C.getState();688const auto *Pos1 = getIteratorPosition(State, Iter1);689const auto *Pos2 = getIteratorPosition(State, Iter2);690if (!Pos1 || !Pos2)691return;692693// Invalidate the deleted iterator position range (first..last)694State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,695Pos2->getOffset(), BO_LT);696C.addTransition(State);697}698699const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,700StringRef Text,701const MemRegion *ContReg,702const Expr *ContE) const {703StringRef Name;704// First try to get the name of the variable from the region705if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {706Name = DR->getDecl()->getName();707// If the region is not a `DeclRegion` then use the expression instead708} else if (const auto *DRE =709dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {710Name = DRE->getDecl()->getName();711}712713return C.getNoteTag(714[Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {715if (!BR.isInteresting(ContReg))716return "";717718SmallString<256> Msg;719llvm::raw_svector_ostream Out(Msg);720Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )721<< Text;722return std::string(Out.str());723});724}725726void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,727const char *NL, const char *Sep) const {728auto ContMap = State->get<ContainerMap>();729730if (!ContMap.isEmpty()) {731Out << Sep << "Container Data :" << NL;732for (const auto &Cont : ContMap) {733Cont.first->dumpToStream(Out);734Out << " : [ ";735const auto CData = Cont.second;736if (CData.getBegin())737CData.getBegin()->dumpToStream(Out);738else739Out << "<Unknown>";740Out << " .. ";741if (CData.getEnd())742CData.getEnd()->dumpToStream(Out);743else744Out << "<Unknown>";745Out << " ]";746}747}748}749750namespace {751752bool isBeginCall(const FunctionDecl *Func) {753const auto *IdInfo = Func->getIdentifier();754if (!IdInfo)755return false;756return IdInfo->getName().ends_with_insensitive("begin");757}758759bool isEndCall(const FunctionDecl *Func) {760const auto *IdInfo = Func->getIdentifier();761if (!IdInfo)762return false;763return IdInfo->getName().ends_with_insensitive("end");764}765766const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,767const MemRegion *Reg) {768auto TI = getDynamicTypeInfo(State, Reg);769if (!TI.isValid())770return nullptr;771772auto Type = TI.getType();773if (const auto *RefT = Type->getAs<ReferenceType>()) {774Type = RefT->getPointeeType();775}776777if (const auto *PtrT = Type->getAs<PointerType>()) {778Type = PtrT->getPointeeType();779}780781return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();782}783784bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {785const auto *CRD = getCXXRecordDecl(State, Reg);786if (!CRD)787return false;788789for (const auto *Method : CRD->methods()) {790if (!Method->isOverloadedOperator())791continue;792const auto OPK = Method->getOverloadedOperator();793if (OPK == OO_Subscript) {794return true;795}796}797return false;798}799800bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {801const auto *CRD = getCXXRecordDecl(State, Reg);802if (!CRD)803return false;804805for (const auto *Method : CRD->methods()) {806if (!Method->getDeclName().isIdentifier())807continue;808if (Method->getName() == "push_front" || Method->getName() == "pop_front") {809return true;810}811}812return false;813}814815bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {816const auto *CRD = getCXXRecordDecl(State, Reg);817if (!CRD)818return false;819820for (const auto *Method : CRD->methods()) {821if (!Method->getDeclName().isIdentifier())822continue;823if (Method->getName() == "push_back" || Method->getName() == "pop_back") {824return true;825}826}827return false;828}829830SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {831const auto *CDataPtr = getContainerData(State, Cont);832if (!CDataPtr)833return nullptr;834835return CDataPtr->getBegin();836}837838SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {839const auto *CDataPtr = getContainerData(State, Cont);840if (!CDataPtr)841return nullptr;842843return CDataPtr->getEnd();844}845846ProgramStateRef createContainerBegin(ProgramStateRef State,847const MemRegion *Cont, const Expr *E,848QualType T, const LocationContext *LCtx,849unsigned BlockCount) {850// Only create if it does not exist851const auto *CDataPtr = getContainerData(State, Cont);852if (CDataPtr && CDataPtr->getBegin())853return State;854855auto &SymMgr = State->getSymbolManager();856const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,857"begin");858State = assumeNoOverflow(State, Sym, 4);859860if (CDataPtr) {861const auto CData = CDataPtr->newBegin(Sym);862return setContainerData(State, Cont, CData);863}864865const auto CData = ContainerData::fromBegin(Sym);866return setContainerData(State, Cont, CData);867}868869ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,870const Expr *E, QualType T,871const LocationContext *LCtx,872unsigned BlockCount) {873// Only create if it does not exist874const auto *CDataPtr = getContainerData(State, Cont);875if (CDataPtr && CDataPtr->getEnd())876return State;877878auto &SymMgr = State->getSymbolManager();879const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,880"end");881State = assumeNoOverflow(State, Sym, 4);882883if (CDataPtr) {884const auto CData = CDataPtr->newEnd(Sym);885return setContainerData(State, Cont, CData);886}887888const auto CData = ContainerData::fromEnd(Sym);889return setContainerData(State, Cont, CData);890}891892ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,893const ContainerData &CData) {894return State->set<ContainerMap>(Cont, CData);895}896897template <typename Condition, typename Process>898ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,899Process Proc) {900auto &RegionMapFactory = State->get_context<IteratorRegionMap>();901auto RegionMap = State->get<IteratorRegionMap>();902bool Changed = false;903for (const auto &Reg : RegionMap) {904if (Cond(Reg.second)) {905RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));906Changed = true;907}908}909910if (Changed)911State = State->set<IteratorRegionMap>(RegionMap);912913auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();914auto SymbolMap = State->get<IteratorSymbolMap>();915Changed = false;916for (const auto &Sym : SymbolMap) {917if (Cond(Sym.second)) {918SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));919Changed = true;920}921}922923if (Changed)924State = State->set<IteratorSymbolMap>(SymbolMap);925926return State;927}928929ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,930const MemRegion *Cont) {931auto MatchCont = [&](const IteratorPosition &Pos) {932return Pos.getContainer() == Cont;933};934auto Invalidate = [&](const IteratorPosition &Pos) {935return Pos.invalidate();936};937return processIteratorPositions(State, MatchCont, Invalidate);938}939940ProgramStateRef941invalidateAllIteratorPositionsExcept(ProgramStateRef State,942const MemRegion *Cont, SymbolRef Offset,943BinaryOperator::Opcode Opc) {944auto MatchContAndCompare = [&](const IteratorPosition &Pos) {945return Pos.getContainer() == Cont &&946!compare(State, Pos.getOffset(), Offset, Opc);947};948auto Invalidate = [&](const IteratorPosition &Pos) {949return Pos.invalidate();950};951return processIteratorPositions(State, MatchContAndCompare, Invalidate);952}953954ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,955SymbolRef Offset,956BinaryOperator::Opcode Opc) {957auto Compare = [&](const IteratorPosition &Pos) {958return compare(State, Pos.getOffset(), Offset, Opc);959};960auto Invalidate = [&](const IteratorPosition &Pos) {961return Pos.invalidate();962};963return processIteratorPositions(State, Compare, Invalidate);964}965966ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,967SymbolRef Offset1,968BinaryOperator::Opcode Opc1,969SymbolRef Offset2,970BinaryOperator::Opcode Opc2) {971auto Compare = [&](const IteratorPosition &Pos) {972return compare(State, Pos.getOffset(), Offset1, Opc1) &&973compare(State, Pos.getOffset(), Offset2, Opc2);974};975auto Invalidate = [&](const IteratorPosition &Pos) {976return Pos.invalidate();977};978return processIteratorPositions(State, Compare, Invalidate);979}980981ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,982const MemRegion *Cont,983const MemRegion *NewCont) {984auto MatchCont = [&](const IteratorPosition &Pos) {985return Pos.getContainer() == Cont;986};987auto ReAssign = [&](const IteratorPosition &Pos) {988return Pos.reAssign(NewCont);989};990return processIteratorPositions(State, MatchCont, ReAssign);991}992993ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,994const MemRegion *Cont,995const MemRegion *NewCont,996SymbolRef Offset,997BinaryOperator::Opcode Opc) {998auto MatchContAndCompare = [&](const IteratorPosition &Pos) {999return Pos.getContainer() == Cont &&1000!compare(State, Pos.getOffset(), Offset, Opc);1001};1002auto ReAssign = [&](const IteratorPosition &Pos) {1003return Pos.reAssign(NewCont);1004};1005return processIteratorPositions(State, MatchContAndCompare, ReAssign);1006}10071008// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,1009// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator1010// position offsets where `CondSym` is true.1011ProgramStateRef rebaseSymbolInIteratorPositionsIf(1012ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,1013SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {1014auto LessThanEnd = [&](const IteratorPosition &Pos) {1015return compare(State, Pos.getOffset(), CondSym, Opc);1016};1017auto RebaseSymbol = [&](const IteratorPosition &Pos) {1018return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,1019NewSym));1020};1021return processIteratorPositions(State, LessThanEnd, RebaseSymbol);1022}10231024// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,1025// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression1026// `OrigExpr`.1027SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,1028SymbolRef OrigExpr, SymbolRef OldExpr,1029SymbolRef NewSym) {1030auto &SymMgr = SVB.getSymbolManager();1031auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),1032nonloc::SymbolVal(OldExpr),1033SymMgr.getType(OrigExpr));10341035const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();1036if (!DiffInt)1037return OrigExpr;10381039return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),1040SymMgr.getType(OrigExpr)).getAsSymbol();1041}10421043bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {1044auto RegionMap = State->get<IteratorRegionMap>();1045for (const auto &Reg : RegionMap) {1046if (Reg.second.getContainer() == Cont)1047return true;1048}10491050auto SymbolMap = State->get<IteratorSymbolMap>();1051for (const auto &Sym : SymbolMap) {1052if (Sym.second.getContainer() == Cont)1053return true;1054}10551056return false;1057}10581059} // namespace10601061void ento::registerContainerModeling(CheckerManager &mgr) {1062mgr.registerChecker<ContainerModeling>();1063}10641065bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {1066if (!mgr.getLangOpts().CPlusPlus)1067return false;10681069if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {1070mgr.getASTContext().getDiagnostics().Report(1071diag::err_analyzer_checker_incompatible_analyzer_option)1072<< "aggressive-binary-operation-simplification" << "false";1073return false;1074}10751076return true;1077}107810791080