1 //===-- ContainerModeling.cpp -------------------------------------*- C++ -*--//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Defines a modeling-checker for modeling STL container-like containers.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "clang/AST/DeclTemplate.h"
14 #include "clang/Driver/DriverDiagnostic.h"
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
22 
23 #include "Iterator.h"
24 
25 #include <utility>
26 
27 using namespace clang;
28 using namespace ento;
29 using namespace iterator;
30 
31 namespace {
32 
33 class ContainerModeling
34   : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
35 
36   void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,
37                    SVal Cont) const;
38   void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,
39                  SVal Cont) const;
40   void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,
41                         SVal OldCont = UndefinedVal()) const;
42   void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const;
43   void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const;
44   void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
45   void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
46   void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
47   void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
48   void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const;
49   void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const;
50   void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const;
51   void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const;
52   void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1,
53                         SVal Iter2) const;
54   const NoteTag *getChangeTag(CheckerContext &C, StringRef Text,
55                               const MemRegion *ContReg,
56                               const Expr *ContE) const;
57   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
58                   const char *Sep) const override;
59 
60 public:
61   ContainerModeling() = default;
62 
63   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
64   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
65   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
66 
67   using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
68                                                   const Expr *) const;
69   using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
70                                                    SVal) const;
71   using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal,
72                                                    SVal) const;
73 
74   CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {
75       {{{"clear"}, 0}, &ContainerModeling::handleClear},
76       {{{"assign"}, 2}, &ContainerModeling::handleAssign},
77       {{{"push_back"}, 1}, &ContainerModeling::handlePushBack},
78       {{{"emplace_back"}, 1}, &ContainerModeling::handlePushBack},
79       {{{"pop_back"}, 0}, &ContainerModeling::handlePopBack},
80       {{{"push_front"}, 1}, &ContainerModeling::handlePushFront},
81       {{{"emplace_front"}, 1}, &ContainerModeling::handlePushFront},
82       {{{"pop_front"}, 0}, &ContainerModeling::handlePopFront},
83   };
84 
85   CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
86       {{{"insert"}, 2}, &ContainerModeling::handleInsert},
87       {{{"emplace"}, 2}, &ContainerModeling::handleInsert},
88       {{{"erase"}, 1}, &ContainerModeling::handleErase},
89       {{{"erase_after"}, 1}, &ContainerModeling::handleEraseAfter},
90   };
91 
92   CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
93       {{{"erase"}, 2}, &ContainerModeling::handleErase},
94       {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter},
95   };
96 };
97 
98 bool isBeginCall(const FunctionDecl *Func);
99 bool isEndCall(const FunctionDecl *Func);
100 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
101 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
102 bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
103 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
104 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
105 ProgramStateRef createContainerBegin(ProgramStateRef State,
106                                      const MemRegion *Cont, const Expr *E,
107                                      QualType T, const LocationContext *LCtx,
108                                      unsigned BlockCount);
109 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
110                                    const Expr *E, QualType T,
111                                    const LocationContext *LCtx,
112                                    unsigned BlockCount);
113 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
114                                  const ContainerData &CData);
115 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
116                                                const MemRegion *Cont);
117 ProgramStateRef
118 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
119                                      const MemRegion *Cont, SymbolRef Offset,
120                                      BinaryOperator::Opcode Opc);
121 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
122                                             SymbolRef Offset,
123                                             BinaryOperator::Opcode Opc);
124 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
125                                             SymbolRef Offset1,
126                                             BinaryOperator::Opcode Opc1,
127                                             SymbolRef Offset2,
128                                             BinaryOperator::Opcode Opc2);
129 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
130                                              const MemRegion *Cont,
131                                              const MemRegion *NewCont);
132 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
133                                                    const MemRegion *Cont,
134                                                    const MemRegion *NewCont,
135                                                    SymbolRef Offset,
136                                                    BinaryOperator::Opcode Opc);
137 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
138     ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
139     SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
140 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
141                         SymbolRef OldSym, SymbolRef NewSym);
142 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
143 
144 } // namespace
145 
146 void ContainerModeling::checkPostCall(const CallEvent &Call,
147                                      CheckerContext &C) const {
148   const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
149   if (!Func)
150     return;
151 
152   if (Func->isOverloadedOperator()) {
153     const auto Op = Func->getOverloadedOperator();
154     if (Op == OO_Equal) {
155       // Overloaded 'operator=' must be a non-static member function.
156       const auto *InstCall = cast<CXXInstanceCall>(&Call);
157       if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
158         handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
159                      Call.getArgSVal(0));
160         return;
161       }
162 
163       handleAssignment(C, InstCall->getCXXThisVal());
164       return;
165     }
166   } else {
167     if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
168       const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
169       if (Handler0) {
170         (this->**Handler0)(C, InstCall->getCXXThisVal(),
171                            InstCall->getCXXThisExpr());
172         return;
173       }
174 
175       const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
176       if (Handler1) {
177         (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
178         return;
179       }
180 
181       const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
182       if (Handler2) {
183         (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
184                            Call.getArgSVal(1));
185         return;
186       }
187 
188       const auto *OrigExpr = Call.getOriginExpr();
189       if (!OrigExpr)
190         return;
191 
192       if (isBeginCall(Func)) {
193         handleBegin(C, OrigExpr, Call.getReturnValue(),
194                     InstCall->getCXXThisVal());
195         return;
196       }
197 
198       if (isEndCall(Func)) {
199         handleEnd(C, OrigExpr, Call.getReturnValue(),
200                   InstCall->getCXXThisVal());
201         return;
202       }
203     }
204   }
205 }
206 
207 void ContainerModeling::checkLiveSymbols(ProgramStateRef State,
208                                          SymbolReaper &SR) const {
209   // Keep symbolic expressions of container begins and ends alive
210   auto ContMap = State->get<ContainerMap>();
211   for (const auto &Cont : ContMap) {
212     const auto CData = Cont.second;
213     if (CData.getBegin()) {
214       SR.markLive(CData.getBegin());
215       if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
216         SR.markLive(SIE->getLHS());
217     }
218     if (CData.getEnd()) {
219       SR.markLive(CData.getEnd());
220       if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
221         SR.markLive(SIE->getLHS());
222     }
223   }
224 }
225 
226 void ContainerModeling::checkDeadSymbols(SymbolReaper &SR,
227                                          CheckerContext &C) const {
228   // Cleanup
229   auto State = C.getState();
230 
231   auto ContMap = State->get<ContainerMap>();
232   for (const auto &Cont : ContMap) {
233     if (!SR.isLiveRegion(Cont.first)) {
234       // We must keep the container data while it has live iterators to be able
235       // to compare them to the begin and the end of the container.
236       if (!hasLiveIterators(State, Cont.first)) {
237         State = State->remove<ContainerMap>(Cont.first);
238       }
239     }
240   }
241 
242   C.addTransition(State);
243 }
244 
245 void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
246                                    SVal RetVal, SVal Cont) const {
247   const auto *ContReg = Cont.getAsRegion();
248   if (!ContReg)
249     return;
250 
251   ContReg = ContReg->getMostDerivedObjectRegion();
252 
253   // If the container already has a begin symbol then use it. Otherwise first
254   // create a new one.
255   auto State = C.getState();
256   auto BeginSym = getContainerBegin(State, ContReg);
257   if (!BeginSym) {
258     State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
259                                  C.getLocationContext(), C.blockCount());
260     BeginSym = getContainerBegin(State, ContReg);
261   }
262   State = setIteratorPosition(State, RetVal,
263                               IteratorPosition::getPosition(ContReg, BeginSym));
264   C.addTransition(State);
265 }
266 
267 void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
268                                  SVal RetVal, SVal Cont) const {
269   const auto *ContReg = Cont.getAsRegion();
270   if (!ContReg)
271     return;
272 
273   ContReg = ContReg->getMostDerivedObjectRegion();
274 
275   // If the container already has an end symbol then use it. Otherwise first
276   // create a new one.
277   auto State = C.getState();
278   auto EndSym = getContainerEnd(State, ContReg);
279   if (!EndSym) {
280     State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
281                                C.getLocationContext(), C.blockCount());
282     EndSym = getContainerEnd(State, ContReg);
283   }
284   State = setIteratorPosition(State, RetVal,
285                               IteratorPosition::getPosition(ContReg, EndSym));
286   C.addTransition(State);
287 }
288 
289 void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
290                                          const Expr *CE, SVal OldCont) const {
291   const auto *ContReg = Cont.getAsRegion();
292   if (!ContReg)
293     return;
294 
295   ContReg = ContReg->getMostDerivedObjectRegion();
296 
297   // Assignment of a new value to a container always invalidates all its
298   // iterators
299   auto State = C.getState();
300   const auto CData = getContainerData(State, ContReg);
301   if (CData) {
302     State = invalidateAllIteratorPositions(State, ContReg);
303   }
304 
305   // In case of move, iterators of the old container (except the past-end
306   // iterators) remain valid but refer to the new container
307   if (!OldCont.isUndef()) {
308     const auto *OldContReg = OldCont.getAsRegion();
309     if (OldContReg) {
310       OldContReg = OldContReg->getMostDerivedObjectRegion();
311       const auto OldCData = getContainerData(State, OldContReg);
312       if (OldCData) {
313         if (const auto OldEndSym = OldCData->getEnd()) {
314           // If we already assigned an "end" symbol to the old container, then
315           // first reassign all iterator positions to the new container which
316           // are not past the container (thus not greater or equal to the
317           // current "end" symbol).
318           State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
319                                                      OldEndSym, BO_GE);
320           auto &SymMgr = C.getSymbolManager();
321           auto &SVB = C.getSValBuilder();
322           // Then generate and assign a new "end" symbol for the new container.
323           auto NewEndSym =
324               SymMgr.conjureSymbol(CE, C.getLocationContext(),
325                                    C.getASTContext().LongTy, C.blockCount());
326           State = assumeNoOverflow(State, NewEndSym, 4);
327           if (CData) {
328             State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
329           } else {
330             State = setContainerData(State, ContReg,
331                                      ContainerData::fromEnd(NewEndSym));
332           }
333           // Finally, replace the old "end" symbol in the already reassigned
334           // iterator positions with the new "end" symbol.
335           State = rebaseSymbolInIteratorPositionsIf(
336               State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
337         } else {
338           // There was no "end" symbol assigned yet to the old container,
339           // so reassign all iterator positions to the new container.
340           State = reassignAllIteratorPositions(State, OldContReg, ContReg);
341         }
342         if (const auto OldBeginSym = OldCData->getBegin()) {
343           // If we already assigned a "begin" symbol to the old container, then
344           // assign it to the new container and remove it from the old one.
345           if (CData) {
346             State =
347                 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
348           } else {
349             State = setContainerData(State, ContReg,
350                                      ContainerData::fromBegin(OldBeginSym));
351           }
352           State =
353               setContainerData(State, OldContReg, OldCData->newBegin(nullptr));
354         }
355       } else {
356         // There was neither "begin" nor "end" symbol assigned yet to the old
357         // container, so reassign all iterator positions to the new container.
358         State = reassignAllIteratorPositions(State, OldContReg, ContReg);
359       }
360     }
361   }
362   C.addTransition(State);
363 }
364 
365 void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,
366                                      const Expr *ContE) const {
367   const auto *ContReg = Cont.getAsRegion();
368   if (!ContReg)
369     return;
370 
371   ContReg = ContReg->getMostDerivedObjectRegion();
372 
373   // The assign() operation invalidates all the iterators
374   auto State = C.getState();
375   State = invalidateAllIteratorPositions(State, ContReg);
376   C.addTransition(State);
377 }
378 
379 void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,
380                                     const Expr *ContE) const {
381   const auto *ContReg = Cont.getAsRegion();
382   if (!ContReg)
383     return;
384 
385   ContReg = ContReg->getMostDerivedObjectRegion();
386 
387   // The clear() operation invalidates all the iterators, except the past-end
388   // iterators of list-like containers
389   auto State = C.getState();
390   if (!hasSubscriptOperator(State, ContReg) ||
391       !backModifiable(State, ContReg)) {
392     const auto CData = getContainerData(State, ContReg);
393     if (CData) {
394       if (const auto EndSym = CData->getEnd()) {
395         State =
396             invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
397         C.addTransition(State);
398         return;
399       }
400     }
401   }
402   const NoteTag *ChangeTag =
403     getChangeTag(C, "became empty", ContReg, ContE);
404   State = invalidateAllIteratorPositions(State, ContReg);
405   C.addTransition(State, ChangeTag);
406 }
407 
408 void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
409                                        const Expr *ContE) const {
410   const auto *ContReg = Cont.getAsRegion();
411   if (!ContReg)
412     return;
413 
414   ContReg = ContReg->getMostDerivedObjectRegion();
415 
416   // For deque-like containers invalidate all iterator positions
417   auto State = C.getState();
418   if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
419     State = invalidateAllIteratorPositions(State, ContReg);
420     C.addTransition(State);
421     return;
422   }
423 
424   const auto CData = getContainerData(State, ContReg);
425   if (!CData)
426     return;
427 
428   // For vector-like containers invalidate the past-end iterator positions
429   if (const auto EndSym = CData->getEnd()) {
430     if (hasSubscriptOperator(State, ContReg)) {
431       State = invalidateIteratorPositions(State, EndSym, BO_GE);
432     }
433     auto &SymMgr = C.getSymbolManager();
434     auto &BVF = SymMgr.getBasicVals();
435     auto &SVB = C.getSValBuilder();
436     const auto newEndSym =
437       SVB.evalBinOp(State, BO_Add,
438                     nonloc::SymbolVal(EndSym),
439                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
440                     SymMgr.getType(EndSym)).getAsSymbol();
441     const NoteTag *ChangeTag =
442       getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);
443     State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
444     C.addTransition(State, ChangeTag);
445   }
446 }
447 
448 void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
449                                       const Expr *ContE) const {
450   const auto *ContReg = Cont.getAsRegion();
451   if (!ContReg)
452     return;
453 
454   ContReg = ContReg->getMostDerivedObjectRegion();
455 
456   auto State = C.getState();
457   const auto CData = getContainerData(State, ContReg);
458   if (!CData)
459     return;
460 
461   if (const auto EndSym = CData->getEnd()) {
462     auto &SymMgr = C.getSymbolManager();
463     auto &BVF = SymMgr.getBasicVals();
464     auto &SVB = C.getSValBuilder();
465     const auto BackSym =
466       SVB.evalBinOp(State, BO_Sub,
467                     nonloc::SymbolVal(EndSym),
468                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
469                     SymMgr.getType(EndSym)).getAsSymbol();
470     const NoteTag *ChangeTag =
471       getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE);
472     // For vector-like and deque-like containers invalidate the last and the
473     // past-end iterator positions. For list-like containers only invalidate
474     // the last position
475     if (hasSubscriptOperator(State, ContReg) &&
476         backModifiable(State, ContReg)) {
477       State = invalidateIteratorPositions(State, BackSym, BO_GE);
478       State = setContainerData(State, ContReg, CData->newEnd(nullptr));
479     } else {
480       State = invalidateIteratorPositions(State, BackSym, BO_EQ);
481     }
482     auto newEndSym = BackSym;
483     State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
484     C.addTransition(State, ChangeTag);
485   }
486 }
487 
488 void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
489                                         const Expr *ContE) const {
490   const auto *ContReg = Cont.getAsRegion();
491   if (!ContReg)
492     return;
493 
494   ContReg = ContReg->getMostDerivedObjectRegion();
495 
496   // For deque-like containers invalidate all iterator positions
497   auto State = C.getState();
498   if (hasSubscriptOperator(State, ContReg)) {
499     State = invalidateAllIteratorPositions(State, ContReg);
500     C.addTransition(State);
501   } else {
502     const auto CData = getContainerData(State, ContReg);
503     if (!CData)
504       return;
505 
506     if (const auto BeginSym = CData->getBegin()) {
507       auto &SymMgr = C.getSymbolManager();
508       auto &BVF = SymMgr.getBasicVals();
509       auto &SVB = C.getSValBuilder();
510       const auto newBeginSym =
511         SVB.evalBinOp(State, BO_Sub,
512                       nonloc::SymbolVal(BeginSym),
513                       nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
514                       SymMgr.getType(BeginSym)).getAsSymbol();
515       const NoteTag *ChangeTag =
516         getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);
517       State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
518       C.addTransition(State, ChangeTag);
519     }
520   }
521 }
522 
523 void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
524                                        const Expr *ContE) const {
525   const auto *ContReg = Cont.getAsRegion();
526   if (!ContReg)
527     return;
528 
529   ContReg = ContReg->getMostDerivedObjectRegion();
530 
531   auto State = C.getState();
532   const auto CData = getContainerData(State, ContReg);
533   if (!CData)
534     return;
535 
536   // For deque-like containers invalidate all iterator positions. For list-like
537   // iterators only invalidate the first position
538   if (const auto BeginSym = CData->getBegin()) {
539     if (hasSubscriptOperator(State, ContReg)) {
540       State = invalidateIteratorPositions(State, BeginSym, BO_LE);
541     } else {
542       State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
543     }
544     auto &SymMgr = C.getSymbolManager();
545     auto &BVF = SymMgr.getBasicVals();
546     auto &SVB = C.getSValBuilder();
547     const auto newBeginSym =
548       SVB.evalBinOp(State, BO_Add,
549                     nonloc::SymbolVal(BeginSym),
550                     nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
551                     SymMgr.getType(BeginSym)).getAsSymbol();
552     const NoteTag *ChangeTag =
553       getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);
554     State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
555     C.addTransition(State, ChangeTag);
556   }
557 }
558 
559 void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,
560                                      SVal Iter) const {
561   const auto *ContReg = Cont.getAsRegion();
562   if (!ContReg)
563     return;
564 
565   ContReg = ContReg->getMostDerivedObjectRegion();
566 
567   auto State = C.getState();
568   const auto *Pos = getIteratorPosition(State, Iter);
569   if (!Pos)
570     return;
571 
572   // For deque-like containers invalidate all iterator positions. For
573   // vector-like containers invalidate iterator positions after the insertion.
574   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
575     if (frontModifiable(State, ContReg)) {
576       State = invalidateAllIteratorPositions(State, ContReg);
577     } else {
578       State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
579     }
580     if (const auto *CData = getContainerData(State, ContReg)) {
581       if (const auto EndSym = CData->getEnd()) {
582         State = invalidateIteratorPositions(State, EndSym, BO_GE);
583         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
584       }
585     }
586     C.addTransition(State);
587   }
588 }
589 
590 void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,
591                                     SVal Iter) const {
592   const auto *ContReg = Cont.getAsRegion();
593   if (!ContReg)
594     return;
595 
596   ContReg = ContReg->getMostDerivedObjectRegion();
597 
598   auto State = C.getState();
599   const auto *Pos = getIteratorPosition(State, Iter);
600   if (!Pos)
601     return;
602 
603   // For deque-like containers invalidate all iterator positions. For
604   // vector-like containers invalidate iterator positions at and after the
605   // deletion. For list-like containers only invalidate the deleted position.
606   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
607     if (frontModifiable(State, ContReg)) {
608       State = invalidateAllIteratorPositions(State, ContReg);
609     } else {
610       State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
611     }
612     if (const auto *CData = getContainerData(State, ContReg)) {
613       if (const auto EndSym = CData->getEnd()) {
614         State = invalidateIteratorPositions(State, EndSym, BO_GE);
615         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
616       }
617     }
618   } else {
619     State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
620   }
621   C.addTransition(State);
622 }
623 
624 void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,
625                                     SVal Iter2) const {
626   const auto *ContReg = Cont.getAsRegion();
627   if (!ContReg)
628     return;
629 
630   ContReg = ContReg->getMostDerivedObjectRegion();
631   auto State = C.getState();
632   const auto *Pos1 = getIteratorPosition(State, Iter1);
633   const auto *Pos2 = getIteratorPosition(State, Iter2);
634   if (!Pos1 || !Pos2)
635     return;
636 
637   // For deque-like containers invalidate all iterator positions. For
638   // vector-like containers invalidate iterator positions at and after the
639   // deletion range. For list-like containers only invalidate the deleted
640   // position range [first..last].
641   if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
642     if (frontModifiable(State, ContReg)) {
643       State = invalidateAllIteratorPositions(State, ContReg);
644     } else {
645       State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
646     }
647     if (const auto *CData = getContainerData(State, ContReg)) {
648       if (const auto EndSym = CData->getEnd()) {
649         State = invalidateIteratorPositions(State, EndSym, BO_GE);
650         State = setContainerData(State, ContReg, CData->newEnd(nullptr));
651       }
652     }
653   } else {
654     State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
655                                         Pos2->getOffset(), BO_LT);
656   }
657   C.addTransition(State);
658 }
659 
660 void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
661                                         SVal Iter) const {
662   auto State = C.getState();
663   const auto *Pos = getIteratorPosition(State, Iter);
664   if (!Pos)
665     return;
666 
667   // Invalidate the deleted iterator position, which is the position of the
668   // parameter plus one.
669   auto &SymMgr = C.getSymbolManager();
670   auto &BVF = SymMgr.getBasicVals();
671   auto &SVB = C.getSValBuilder();
672   const auto NextSym =
673     SVB.evalBinOp(State, BO_Add,
674                   nonloc::SymbolVal(Pos->getOffset()),
675                   nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
676                   SymMgr.getType(Pos->getOffset())).getAsSymbol();
677   State = invalidateIteratorPositions(State, NextSym, BO_EQ);
678   C.addTransition(State);
679 }
680 
681 void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
682                                          SVal Iter1, SVal Iter2) const {
683   auto State = C.getState();
684   const auto *Pos1 = getIteratorPosition(State, Iter1);
685   const auto *Pos2 = getIteratorPosition(State, Iter2);
686   if (!Pos1 || !Pos2)
687     return;
688 
689   // Invalidate the deleted iterator position range (first..last)
690   State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
691                                       Pos2->getOffset(), BO_LT);
692   C.addTransition(State);
693 }
694 
695 const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
696                                                StringRef Text,
697                                                const MemRegion *ContReg,
698                                                const Expr *ContE) const {
699   StringRef Name;
700   // First try to get the name of the variable from the region
701   if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {
702     Name = DR->getDecl()->getName();
703   // If the region is not a `DeclRegion` then use the expression instead
704   } else if (const auto *DRE =
705              dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {
706     Name = DRE->getDecl()->getName();
707   }
708 
709   return C.getNoteTag(
710       [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
711         if (!BR.isInteresting(ContReg))
712           return "";
713 
714         SmallString<256> Msg;
715         llvm::raw_svector_ostream Out(Msg);
716         Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )
717             << Text;
718         return std::string(Out.str());
719       });
720 }
721 
722 void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,
723                                   const char *NL, const char *Sep) const {
724   auto ContMap = State->get<ContainerMap>();
725 
726   if (!ContMap.isEmpty()) {
727     Out << Sep << "Container Data :" << NL;
728     for (const auto &Cont : ContMap) {
729       Cont.first->dumpToStream(Out);
730       Out << " : [ ";
731       const auto CData = Cont.second;
732       if (CData.getBegin())
733         CData.getBegin()->dumpToStream(Out);
734       else
735         Out << "<Unknown>";
736       Out << " .. ";
737       if (CData.getEnd())
738         CData.getEnd()->dumpToStream(Out);
739       else
740         Out << "<Unknown>";
741       Out << " ]";
742     }
743   }
744 }
745 
746 namespace {
747 
748 bool isBeginCall(const FunctionDecl *Func) {
749   const auto *IdInfo = Func->getIdentifier();
750   if (!IdInfo)
751     return false;
752   return IdInfo->getName().ends_with_insensitive("begin");
753 }
754 
755 bool isEndCall(const FunctionDecl *Func) {
756   const auto *IdInfo = Func->getIdentifier();
757   if (!IdInfo)
758     return false;
759   return IdInfo->getName().ends_with_insensitive("end");
760 }
761 
762 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
763                                       const MemRegion *Reg) {
764   auto TI = getDynamicTypeInfo(State, Reg);
765   if (!TI.isValid())
766     return nullptr;
767 
768   auto Type = TI.getType();
769   if (const auto *RefT = Type->getAs<ReferenceType>()) {
770     Type = RefT->getPointeeType();
771   }
772 
773   return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
774 }
775 
776 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
777   const auto *CRD = getCXXRecordDecl(State, Reg);
778   if (!CRD)
779     return false;
780 
781   for (const auto *Method : CRD->methods()) {
782     if (!Method->isOverloadedOperator())
783       continue;
784     const auto OPK = Method->getOverloadedOperator();
785     if (OPK == OO_Subscript) {
786       return true;
787     }
788   }
789   return false;
790 }
791 
792 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
793   const auto *CRD = getCXXRecordDecl(State, Reg);
794   if (!CRD)
795     return false;
796 
797   for (const auto *Method : CRD->methods()) {
798     if (!Method->getDeclName().isIdentifier())
799       continue;
800     if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
801       return true;
802     }
803   }
804   return false;
805 }
806 
807 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
808   const auto *CRD = getCXXRecordDecl(State, Reg);
809   if (!CRD)
810     return false;
811 
812   for (const auto *Method : CRD->methods()) {
813     if (!Method->getDeclName().isIdentifier())
814       continue;
815     if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
816       return true;
817     }
818   }
819   return false;
820 }
821 
822 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
823   const auto *CDataPtr = getContainerData(State, Cont);
824   if (!CDataPtr)
825     return nullptr;
826 
827   return CDataPtr->getBegin();
828 }
829 
830 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
831   const auto *CDataPtr = getContainerData(State, Cont);
832   if (!CDataPtr)
833     return nullptr;
834 
835   return CDataPtr->getEnd();
836 }
837 
838 ProgramStateRef createContainerBegin(ProgramStateRef State,
839                                      const MemRegion *Cont, const Expr *E,
840                                      QualType T, const LocationContext *LCtx,
841                                      unsigned BlockCount) {
842   // Only create if it does not exist
843   const auto *CDataPtr = getContainerData(State, Cont);
844   if (CDataPtr && CDataPtr->getBegin())
845     return State;
846 
847   auto &SymMgr = State->getSymbolManager();
848   const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
849                                                    "begin");
850   State = assumeNoOverflow(State, Sym, 4);
851 
852   if (CDataPtr) {
853     const auto CData = CDataPtr->newBegin(Sym);
854     return setContainerData(State, Cont, CData);
855   }
856 
857   const auto CData = ContainerData::fromBegin(Sym);
858   return setContainerData(State, Cont, CData);
859 }
860 
861 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
862                                    const Expr *E, QualType T,
863                                    const LocationContext *LCtx,
864                                    unsigned BlockCount) {
865   // Only create if it does not exist
866   const auto *CDataPtr = getContainerData(State, Cont);
867   if (CDataPtr && CDataPtr->getEnd())
868     return State;
869 
870   auto &SymMgr = State->getSymbolManager();
871   const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
872                                                   "end");
873   State = assumeNoOverflow(State, Sym, 4);
874 
875   if (CDataPtr) {
876     const auto CData = CDataPtr->newEnd(Sym);
877     return setContainerData(State, Cont, CData);
878   }
879 
880   const auto CData = ContainerData::fromEnd(Sym);
881   return setContainerData(State, Cont, CData);
882 }
883 
884 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
885                                  const ContainerData &CData) {
886   return State->set<ContainerMap>(Cont, CData);
887 }
888 
889 template <typename Condition, typename Process>
890 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
891                                          Process Proc) {
892   auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
893   auto RegionMap = State->get<IteratorRegionMap>();
894   bool Changed = false;
895   for (const auto &Reg : RegionMap) {
896     if (Cond(Reg.second)) {
897       RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
898       Changed = true;
899     }
900   }
901 
902   if (Changed)
903     State = State->set<IteratorRegionMap>(RegionMap);
904 
905   auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
906   auto SymbolMap = State->get<IteratorSymbolMap>();
907   Changed = false;
908   for (const auto &Sym : SymbolMap) {
909     if (Cond(Sym.second)) {
910       SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
911       Changed = true;
912     }
913   }
914 
915   if (Changed)
916     State = State->set<IteratorSymbolMap>(SymbolMap);
917 
918   return State;
919 }
920 
921 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
922                                                const MemRegion *Cont) {
923   auto MatchCont = [&](const IteratorPosition &Pos) {
924     return Pos.getContainer() == Cont;
925   };
926   auto Invalidate = [&](const IteratorPosition &Pos) {
927     return Pos.invalidate();
928   };
929   return processIteratorPositions(State, MatchCont, Invalidate);
930 }
931 
932 ProgramStateRef
933 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
934                                      const MemRegion *Cont, SymbolRef Offset,
935                                      BinaryOperator::Opcode Opc) {
936   auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
937     return Pos.getContainer() == Cont &&
938            !compare(State, Pos.getOffset(), Offset, Opc);
939   };
940   auto Invalidate = [&](const IteratorPosition &Pos) {
941     return Pos.invalidate();
942   };
943   return processIteratorPositions(State, MatchContAndCompare, Invalidate);
944 }
945 
946 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
947                                             SymbolRef Offset,
948                                             BinaryOperator::Opcode Opc) {
949   auto Compare = [&](const IteratorPosition &Pos) {
950     return compare(State, Pos.getOffset(), Offset, Opc);
951   };
952   auto Invalidate = [&](const IteratorPosition &Pos) {
953     return Pos.invalidate();
954   };
955   return processIteratorPositions(State, Compare, Invalidate);
956 }
957 
958 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
959                                             SymbolRef Offset1,
960                                             BinaryOperator::Opcode Opc1,
961                                             SymbolRef Offset2,
962                                             BinaryOperator::Opcode Opc2) {
963   auto Compare = [&](const IteratorPosition &Pos) {
964     return compare(State, Pos.getOffset(), Offset1, Opc1) &&
965            compare(State, Pos.getOffset(), Offset2, Opc2);
966   };
967   auto Invalidate = [&](const IteratorPosition &Pos) {
968     return Pos.invalidate();
969   };
970   return processIteratorPositions(State, Compare, Invalidate);
971 }
972 
973 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
974                                              const MemRegion *Cont,
975                                              const MemRegion *NewCont) {
976   auto MatchCont = [&](const IteratorPosition &Pos) {
977     return Pos.getContainer() == Cont;
978   };
979   auto ReAssign = [&](const IteratorPosition &Pos) {
980     return Pos.reAssign(NewCont);
981   };
982   return processIteratorPositions(State, MatchCont, ReAssign);
983 }
984 
985 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
986                                                    const MemRegion *Cont,
987                                                    const MemRegion *NewCont,
988                                                    SymbolRef Offset,
989                                                    BinaryOperator::Opcode Opc) {
990   auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
991     return Pos.getContainer() == Cont &&
992     !compare(State, Pos.getOffset(), Offset, Opc);
993   };
994   auto ReAssign = [&](const IteratorPosition &Pos) {
995     return Pos.reAssign(NewCont);
996   };
997   return processIteratorPositions(State, MatchContAndCompare, ReAssign);
998 }
999 
1000 // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
1001 // `OldSym - Int` to `NewSym - Int` and  `OldSym` to `NewSym` in any iterator
1002 // position offsets where `CondSym` is true.
1003 ProgramStateRef rebaseSymbolInIteratorPositionsIf(
1004     ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
1005     SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
1006   auto LessThanEnd = [&](const IteratorPosition &Pos) {
1007     return compare(State, Pos.getOffset(), CondSym, Opc);
1008   };
1009   auto RebaseSymbol = [&](const IteratorPosition &Pos) {
1010     return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
1011                                    NewSym));
1012   };
1013   return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
1014 }
1015 
1016 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
1017 // `OldExpr - Int` to `NewExpr - Int` and  `OldExpr` to `NewExpr` in expression
1018 // `OrigExpr`.
1019 SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
1020                        SymbolRef OrigExpr, SymbolRef OldExpr,
1021                        SymbolRef NewSym) {
1022   auto &SymMgr = SVB.getSymbolManager();
1023   auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
1024                               nonloc::SymbolVal(OldExpr),
1025                               SymMgr.getType(OrigExpr));
1026 
1027   const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
1028   if (!DiffInt)
1029     return OrigExpr;
1030 
1031   return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
1032                          SymMgr.getType(OrigExpr)).getAsSymbol();
1033 }
1034 
1035 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1036   auto RegionMap = State->get<IteratorRegionMap>();
1037   for (const auto &Reg : RegionMap) {
1038     if (Reg.second.getContainer() == Cont)
1039       return true;
1040   }
1041 
1042   auto SymbolMap = State->get<IteratorSymbolMap>();
1043   for (const auto &Sym : SymbolMap) {
1044     if (Sym.second.getContainer() == Cont)
1045       return true;
1046   }
1047 
1048   return false;
1049 }
1050 
1051 } // namespace
1052 
1053 void ento::registerContainerModeling(CheckerManager &mgr) {
1054   mgr.registerChecker<ContainerModeling>();
1055 }
1056 
1057 bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {
1058   if (!mgr.getLangOpts().CPlusPlus)
1059     return false;
1060 
1061   if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {
1062     mgr.getASTContext().getDiagnostics().Report(
1063         diag::err_analyzer_checker_incompatible_analyzer_option)
1064       << "aggressive-binary-operation-simplification" << "false";
1065     return false;
1066   }
1067 
1068   return true;
1069 }
1070