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