1 //===-- ContainerModeling.cpp -------------------------------------*- C++ -*--//
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
7 //===----------------------------------------------------------------------===//
9 // Defines a modeling-checker for modeling STL container-like containers.
11 //===----------------------------------------------------------------------===//
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"
27 using namespace clang
;
29 using namespace iterator
;
33 class ContainerModeling
34 : public Checker
<check::PostCall
, check::LiveSymbols
, check::DeadSymbols
> {
36 void handleBegin(CheckerContext
&C
, const Expr
*CE
, SVal RetVal
,
38 void handleEnd(CheckerContext
&C
, const Expr
*CE
, SVal RetVal
,
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
,
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
;
61 ContainerModeling() = default;
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;
67 using NoItParamFn
= void (ContainerModeling::*)(CheckerContext
&, SVal
,
69 using OneItParamFn
= void (ContainerModeling::*)(CheckerContext
&, SVal
,
71 using TwoItParamFn
= void (ContainerModeling::*)(CheckerContext
&, SVal
, SVal
,
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
},
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
},
92 CallDescriptionMap
<TwoItParamFn
> TwoIterParamFunctions
= {
93 {{{"erase"}, 2}, &ContainerModeling::handleErase
},
94 {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter
},
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
);
118 invalidateAllIteratorPositionsExcept(ProgramStateRef State
,
119 const MemRegion
*Cont
, SymbolRef Offset
,
120 BinaryOperator::Opcode Opc
);
121 ProgramStateRef
invalidateIteratorPositions(ProgramStateRef State
,
123 BinaryOperator::Opcode Opc
);
124 ProgramStateRef
invalidateIteratorPositions(ProgramStateRef State
,
126 BinaryOperator::Opcode Opc1
,
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
,
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
);
146 void ContainerModeling::checkPostCall(const CallEvent
&Call
,
147 CheckerContext
&C
) const {
148 const auto *Func
= dyn_cast_or_null
<FunctionDecl
>(Call
.getDecl());
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(),
163 handleAssignment(C
, InstCall
->getCXXThisVal());
167 if (const auto *InstCall
= dyn_cast
<CXXInstanceCall
>(&Call
)) {
168 const NoItParamFn
*Handler0
= NoIterParamFunctions
.lookup(Call
);
170 (this->**Handler0
)(C
, InstCall
->getCXXThisVal(),
171 InstCall
->getCXXThisExpr());
175 const OneItParamFn
*Handler1
= OneIterParamFunctions
.lookup(Call
);
177 (this->**Handler1
)(C
, InstCall
->getCXXThisVal(), Call
.getArgSVal(0));
181 const TwoItParamFn
*Handler2
= TwoIterParamFunctions
.lookup(Call
);
183 (this->**Handler2
)(C
, InstCall
->getCXXThisVal(), Call
.getArgSVal(0),
188 const auto *OrigExpr
= Call
.getOriginExpr();
192 if (isBeginCall(Func
)) {
193 handleBegin(C
, OrigExpr
, Call
.getReturnValue(),
194 InstCall
->getCXXThisVal());
198 if (isEndCall(Func
)) {
199 handleEnd(C
, OrigExpr
, Call
.getReturnValue(),
200 InstCall
->getCXXThisVal());
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());
218 if (CData
.getEnd()) {
219 SR
.markLive(CData
.getEnd());
220 if(const auto *SIE
= dyn_cast
<SymIntExpr
>(CData
.getEnd()))
221 SR
.markLive(SIE
->getLHS());
226 void ContainerModeling::checkDeadSymbols(SymbolReaper
&SR
,
227 CheckerContext
&C
) const {
229 auto State
= C
.getState();
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
);
242 C
.addTransition(State
);
245 void ContainerModeling::handleBegin(CheckerContext
&C
, const Expr
*CE
,
246 SVal RetVal
, SVal Cont
) const {
247 const auto *ContReg
= Cont
.getAsRegion();
251 ContReg
= ContReg
->getMostDerivedObjectRegion();
253 // If the container already has a begin symbol then use it. Otherwise first
255 auto State
= C
.getState();
256 auto BeginSym
= getContainerBegin(State
, ContReg
);
258 State
= createContainerBegin(State
, ContReg
, CE
, C
.getASTContext().LongTy
,
259 C
.getLocationContext(), C
.blockCount());
260 BeginSym
= getContainerBegin(State
, ContReg
);
262 State
= setIteratorPosition(State
, RetVal
,
263 IteratorPosition::getPosition(ContReg
, BeginSym
));
264 C
.addTransition(State
);
267 void ContainerModeling::handleEnd(CheckerContext
&C
, const Expr
*CE
,
268 SVal RetVal
, SVal Cont
) const {
269 const auto *ContReg
= Cont
.getAsRegion();
273 ContReg
= ContReg
->getMostDerivedObjectRegion();
275 // If the container already has an end symbol then use it. Otherwise first
277 auto State
= C
.getState();
278 auto EndSym
= getContainerEnd(State
, ContReg
);
280 State
= createContainerEnd(State
, ContReg
, CE
, C
.getASTContext().LongTy
,
281 C
.getLocationContext(), C
.blockCount());
282 EndSym
= getContainerEnd(State
, ContReg
);
284 State
= setIteratorPosition(State
, RetVal
,
285 IteratorPosition::getPosition(ContReg
, EndSym
));
286 C
.addTransition(State
);
289 void ContainerModeling::handleAssignment(CheckerContext
&C
, SVal Cont
,
290 const Expr
*CE
, SVal OldCont
) const {
291 const auto *ContReg
= Cont
.getAsRegion();
295 ContReg
= ContReg
->getMostDerivedObjectRegion();
297 // Assignment of a new value to a container always invalidates all its
299 auto State
= C
.getState();
300 const auto CData
= getContainerData(State
, ContReg
);
302 State
= invalidateAllIteratorPositions(State
, ContReg
);
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();
310 OldContReg
= OldContReg
->getMostDerivedObjectRegion();
311 const auto OldCData
= getContainerData(State
, OldContReg
);
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
,
320 auto &SymMgr
= C
.getSymbolManager();
321 auto &SVB
= C
.getSValBuilder();
322 // Then generate and assign a new "end" symbol for the new container.
324 SymMgr
.conjureSymbol(CE
, C
.getLocationContext(),
325 C
.getASTContext().LongTy
, C
.blockCount());
326 State
= assumeNoOverflow(State
, NewEndSym
, 4);
328 State
= setContainerData(State
, ContReg
, CData
->newEnd(NewEndSym
));
330 State
= setContainerData(State
, ContReg
,
331 ContainerData::fromEnd(NewEndSym
));
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
);
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
);
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.
347 setContainerData(State
, ContReg
, CData
->newBegin(OldBeginSym
));
349 State
= setContainerData(State
, ContReg
,
350 ContainerData::fromBegin(OldBeginSym
));
353 setContainerData(State
, OldContReg
, OldCData
->newBegin(nullptr));
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
);
362 C
.addTransition(State
);
365 void ContainerModeling::handleAssign(CheckerContext
&C
, SVal Cont
,
366 const Expr
*ContE
) const {
367 const auto *ContReg
= Cont
.getAsRegion();
371 ContReg
= ContReg
->getMostDerivedObjectRegion();
373 // The assign() operation invalidates all the iterators
374 auto State
= C
.getState();
375 State
= invalidateAllIteratorPositions(State
, ContReg
);
376 C
.addTransition(State
);
379 void ContainerModeling::handleClear(CheckerContext
&C
, SVal Cont
,
380 const Expr
*ContE
) const {
381 const auto *ContReg
= Cont
.getAsRegion();
385 ContReg
= ContReg
->getMostDerivedObjectRegion();
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
);
394 if (const auto EndSym
= CData
->getEnd()) {
396 invalidateAllIteratorPositionsExcept(State
, ContReg
, EndSym
, BO_GE
);
397 C
.addTransition(State
);
402 const NoteTag
*ChangeTag
=
403 getChangeTag(C
, "became empty", ContReg
, ContE
);
404 State
= invalidateAllIteratorPositions(State
, ContReg
);
405 C
.addTransition(State
, ChangeTag
);
408 void ContainerModeling::handlePushBack(CheckerContext
&C
, SVal Cont
,
409 const Expr
*ContE
) const {
410 const auto *ContReg
= Cont
.getAsRegion();
414 ContReg
= ContReg
->getMostDerivedObjectRegion();
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
);
424 const auto CData
= getContainerData(State
, ContReg
);
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
);
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
);
448 void ContainerModeling::handlePopBack(CheckerContext
&C
, SVal Cont
,
449 const Expr
*ContE
) const {
450 const auto *ContReg
= Cont
.getAsRegion();
454 ContReg
= ContReg
->getMostDerivedObjectRegion();
456 auto State
= C
.getState();
457 const auto CData
= getContainerData(State
, ContReg
);
461 if (const auto EndSym
= CData
->getEnd()) {
462 auto &SymMgr
= C
.getSymbolManager();
463 auto &BVF
= SymMgr
.getBasicVals();
464 auto &SVB
= C
.getSValBuilder();
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
475 if (hasSubscriptOperator(State
, ContReg
) &&
476 backModifiable(State
, ContReg
)) {
477 State
= invalidateIteratorPositions(State
, BackSym
, BO_GE
);
478 State
= setContainerData(State
, ContReg
, CData
->newEnd(nullptr));
480 State
= invalidateIteratorPositions(State
, BackSym
, BO_EQ
);
482 auto newEndSym
= BackSym
;
483 State
= setContainerData(State
, ContReg
, CData
->newEnd(newEndSym
));
484 C
.addTransition(State
, ChangeTag
);
488 void ContainerModeling::handlePushFront(CheckerContext
&C
, SVal Cont
,
489 const Expr
*ContE
) const {
490 const auto *ContReg
= Cont
.getAsRegion();
494 ContReg
= ContReg
->getMostDerivedObjectRegion();
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
);
502 const auto CData
= getContainerData(State
, ContReg
);
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
);
523 void ContainerModeling::handlePopFront(CheckerContext
&C
, SVal Cont
,
524 const Expr
*ContE
) const {
525 const auto *ContReg
= Cont
.getAsRegion();
529 ContReg
= ContReg
->getMostDerivedObjectRegion();
531 auto State
= C
.getState();
532 const auto CData
= getContainerData(State
, ContReg
);
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
);
542 State
= invalidateIteratorPositions(State
, BeginSym
, BO_EQ
);
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
);
559 void ContainerModeling::handleInsert(CheckerContext
&C
, SVal Cont
,
561 const auto *ContReg
= Cont
.getAsRegion();
565 ContReg
= ContReg
->getMostDerivedObjectRegion();
567 auto State
= C
.getState();
568 const auto *Pos
= getIteratorPosition(State
, Iter
);
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
);
578 State
= invalidateIteratorPositions(State
, Pos
->getOffset(), BO_GE
);
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));
586 C
.addTransition(State
);
590 void ContainerModeling::handleErase(CheckerContext
&C
, SVal Cont
,
592 const auto *ContReg
= Cont
.getAsRegion();
596 ContReg
= ContReg
->getMostDerivedObjectRegion();
598 auto State
= C
.getState();
599 const auto *Pos
= getIteratorPosition(State
, Iter
);
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
);
610 State
= invalidateIteratorPositions(State
, Pos
->getOffset(), BO_GE
);
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));
619 State
= invalidateIteratorPositions(State
, Pos
->getOffset(), BO_EQ
);
621 C
.addTransition(State
);
624 void ContainerModeling::handleErase(CheckerContext
&C
, SVal Cont
, SVal Iter1
,
626 const auto *ContReg
= Cont
.getAsRegion();
630 ContReg
= ContReg
->getMostDerivedObjectRegion();
631 auto State
= C
.getState();
632 const auto *Pos1
= getIteratorPosition(State
, Iter1
);
633 const auto *Pos2
= getIteratorPosition(State
, Iter2
);
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
);
645 State
= invalidateIteratorPositions(State
, Pos1
->getOffset(), BO_GE
);
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));
654 State
= invalidateIteratorPositions(State
, Pos1
->getOffset(), BO_GE
,
655 Pos2
->getOffset(), BO_LT
);
657 C
.addTransition(State
);
660 void ContainerModeling::handleEraseAfter(CheckerContext
&C
, SVal Cont
,
662 auto State
= C
.getState();
663 const auto *Pos
= getIteratorPosition(State
, Iter
);
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();
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
);
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
);
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
);
695 const NoteTag
*ContainerModeling::getChangeTag(CheckerContext
&C
,
697 const MemRegion
*ContReg
,
698 const Expr
*ContE
) const {
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();
710 [Text
, Name
, ContReg
](PathSensitiveBugReport
&BR
) -> std::string
{
711 if (!BR
.isInteresting(ContReg
))
714 SmallString
<256> Msg
;
715 llvm::raw_svector_ostream
Out(Msg
);
716 Out
<< "Container " << (!Name
.empty() ? ("'" + Name
.str() + "' ") : "" )
718 return std::string(Out
.str());
722 void ContainerModeling::printState(raw_ostream
&Out
, ProgramStateRef State
,
723 const char *NL
, const char *Sep
) const {
724 auto ContMap
= State
->get
<ContainerMap
>();
726 if (!ContMap
.isEmpty()) {
727 Out
<< Sep
<< "Container Data :" << NL
;
728 for (const auto &Cont
: ContMap
) {
729 Cont
.first
->dumpToStream(Out
);
731 const auto CData
= Cont
.second
;
732 if (CData
.getBegin())
733 CData
.getBegin()->dumpToStream(Out
);
738 CData
.getEnd()->dumpToStream(Out
);
748 bool isBeginCall(const FunctionDecl
*Func
) {
749 const auto *IdInfo
= Func
->getIdentifier();
752 return IdInfo
->getName().ends_with_insensitive("begin");
755 bool isEndCall(const FunctionDecl
*Func
) {
756 const auto *IdInfo
= Func
->getIdentifier();
759 return IdInfo
->getName().ends_with_insensitive("end");
762 const CXXRecordDecl
*getCXXRecordDecl(ProgramStateRef State
,
763 const MemRegion
*Reg
) {
764 auto TI
= getDynamicTypeInfo(State
, Reg
);
768 auto Type
= TI
.getType();
769 if (const auto *RefT
= Type
->getAs
<ReferenceType
>()) {
770 Type
= RefT
->getPointeeType();
773 return Type
->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
776 bool hasSubscriptOperator(ProgramStateRef State
, const MemRegion
*Reg
) {
777 const auto *CRD
= getCXXRecordDecl(State
, Reg
);
781 for (const auto *Method
: CRD
->methods()) {
782 if (!Method
->isOverloadedOperator())
784 const auto OPK
= Method
->getOverloadedOperator();
785 if (OPK
== OO_Subscript
) {
792 bool frontModifiable(ProgramStateRef State
, const MemRegion
*Reg
) {
793 const auto *CRD
= getCXXRecordDecl(State
, Reg
);
797 for (const auto *Method
: CRD
->methods()) {
798 if (!Method
->getDeclName().isIdentifier())
800 if (Method
->getName() == "push_front" || Method
->getName() == "pop_front") {
807 bool backModifiable(ProgramStateRef State
, const MemRegion
*Reg
) {
808 const auto *CRD
= getCXXRecordDecl(State
, Reg
);
812 for (const auto *Method
: CRD
->methods()) {
813 if (!Method
->getDeclName().isIdentifier())
815 if (Method
->getName() == "push_back" || Method
->getName() == "pop_back") {
822 SymbolRef
getContainerBegin(ProgramStateRef State
, const MemRegion
*Cont
) {
823 const auto *CDataPtr
= getContainerData(State
, Cont
);
827 return CDataPtr
->getBegin();
830 SymbolRef
getContainerEnd(ProgramStateRef State
, const MemRegion
*Cont
) {
831 const auto *CDataPtr
= getContainerData(State
, Cont
);
835 return CDataPtr
->getEnd();
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())
847 auto &SymMgr
= State
->getSymbolManager();
848 const SymbolConjured
*Sym
= SymMgr
.conjureSymbol(E
, LCtx
, T
, BlockCount
,
850 State
= assumeNoOverflow(State
, Sym
, 4);
853 const auto CData
= CDataPtr
->newBegin(Sym
);
854 return setContainerData(State
, Cont
, CData
);
857 const auto CData
= ContainerData::fromBegin(Sym
);
858 return setContainerData(State
, Cont
, CData
);
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())
870 auto &SymMgr
= State
->getSymbolManager();
871 const SymbolConjured
*Sym
= SymMgr
.conjureSymbol(E
, LCtx
, T
, BlockCount
,
873 State
= assumeNoOverflow(State
, Sym
, 4);
876 const auto CData
= CDataPtr
->newEnd(Sym
);
877 return setContainerData(State
, Cont
, CData
);
880 const auto CData
= ContainerData::fromEnd(Sym
);
881 return setContainerData(State
, Cont
, CData
);
884 ProgramStateRef
setContainerData(ProgramStateRef State
, const MemRegion
*Cont
,
885 const ContainerData
&CData
) {
886 return State
->set
<ContainerMap
>(Cont
, CData
);
889 template <typename Condition
, typename Process
>
890 ProgramStateRef
processIteratorPositions(ProgramStateRef State
, Condition Cond
,
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
));
903 State
= State
->set
<IteratorRegionMap
>(RegionMap
);
905 auto &SymbolMapFactory
= State
->get_context
<IteratorSymbolMap
>();
906 auto SymbolMap
= State
->get
<IteratorSymbolMap
>();
908 for (const auto &Sym
: SymbolMap
) {
909 if (Cond(Sym
.second
)) {
910 SymbolMap
= SymbolMapFactory
.add(SymbolMap
, Sym
.first
, Proc(Sym
.second
));
916 State
= State
->set
<IteratorSymbolMap
>(SymbolMap
);
921 ProgramStateRef
invalidateAllIteratorPositions(ProgramStateRef State
,
922 const MemRegion
*Cont
) {
923 auto MatchCont
= [&](const IteratorPosition
&Pos
) {
924 return Pos
.getContainer() == Cont
;
926 auto Invalidate
= [&](const IteratorPosition
&Pos
) {
927 return Pos
.invalidate();
929 return processIteratorPositions(State
, MatchCont
, Invalidate
);
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
);
940 auto Invalidate
= [&](const IteratorPosition
&Pos
) {
941 return Pos
.invalidate();
943 return processIteratorPositions(State
, MatchContAndCompare
, Invalidate
);
946 ProgramStateRef
invalidateIteratorPositions(ProgramStateRef State
,
948 BinaryOperator::Opcode Opc
) {
949 auto Compare
= [&](const IteratorPosition
&Pos
) {
950 return compare(State
, Pos
.getOffset(), Offset
, Opc
);
952 auto Invalidate
= [&](const IteratorPosition
&Pos
) {
953 return Pos
.invalidate();
955 return processIteratorPositions(State
, Compare
, Invalidate
);
958 ProgramStateRef
invalidateIteratorPositions(ProgramStateRef State
,
960 BinaryOperator::Opcode Opc1
,
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
);
967 auto Invalidate
= [&](const IteratorPosition
&Pos
) {
968 return Pos
.invalidate();
970 return processIteratorPositions(State
, Compare
, Invalidate
);
973 ProgramStateRef
reassignAllIteratorPositions(ProgramStateRef State
,
974 const MemRegion
*Cont
,
975 const MemRegion
*NewCont
) {
976 auto MatchCont
= [&](const IteratorPosition
&Pos
) {
977 return Pos
.getContainer() == Cont
;
979 auto ReAssign
= [&](const IteratorPosition
&Pos
) {
980 return Pos
.reAssign(NewCont
);
982 return processIteratorPositions(State
, MatchCont
, ReAssign
);
985 ProgramStateRef
reassignAllIteratorPositionsUnless(ProgramStateRef State
,
986 const MemRegion
*Cont
,
987 const MemRegion
*NewCont
,
989 BinaryOperator::Opcode Opc
) {
990 auto MatchContAndCompare
= [&](const IteratorPosition
&Pos
) {
991 return Pos
.getContainer() == Cont
&&
992 !compare(State
, Pos
.getOffset(), Offset
, Opc
);
994 auto ReAssign
= [&](const IteratorPosition
&Pos
) {
995 return Pos
.reAssign(NewCont
);
997 return processIteratorPositions(State
, MatchContAndCompare
, ReAssign
);
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
);
1009 auto RebaseSymbol
= [&](const IteratorPosition
&Pos
) {
1010 return Pos
.setTo(rebaseSymbol(State
, SVB
, Pos
.getOffset(), OldSym
,
1013 return processIteratorPositions(State
, LessThanEnd
, RebaseSymbol
);
1016 // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
1017 // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
1019 SymbolRef
rebaseSymbol(ProgramStateRef State
, SValBuilder
&SVB
,
1020 SymbolRef OrigExpr
, SymbolRef OldExpr
,
1022 auto &SymMgr
= SVB
.getSymbolManager();
1023 auto Diff
= SVB
.evalBinOpNN(State
, BO_Sub
, nonloc::SymbolVal(OrigExpr
),
1024 nonloc::SymbolVal(OldExpr
),
1025 SymMgr
.getType(OrigExpr
));
1027 const auto DiffInt
= Diff
.getAs
<nonloc::ConcreteInt
>();
1031 return SVB
.evalBinOpNN(State
, BO_Add
, *DiffInt
, nonloc::SymbolVal(NewSym
),
1032 SymMgr
.getType(OrigExpr
)).getAsSymbol();
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
)
1042 auto SymbolMap
= State
->get
<IteratorSymbolMap
>();
1043 for (const auto &Sym
: SymbolMap
) {
1044 if (Sym
.second
.getContainer() == Cont
)
1053 void ento::registerContainerModeling(CheckerManager
&mgr
) {
1054 mgr
.registerChecker
<ContainerModeling
>();
1057 bool ento::shouldRegisterContainerModeling(const CheckerManager
&mgr
) {
1058 if (!mgr
.getLangOpts().CPlusPlus
)
1061 if (!mgr
.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation
) {
1062 mgr
.getASTContext().getDiagnostics().Report(
1063 diag::err_analyzer_checker_incompatible_analyzer_option
)
1064 << "aggressive-binary-operation-simplification" << "false";