blob: 531054aa7887d7c933f461df0f70ef4620c7f0d6 [file] [log] [blame]
Gabor Horvath3d574572017-01-09 09:52:32 +00001//===-- IteratorPastEndChecker.cpp --------------------------------*- C++ -*--//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Defines a checker for using iterators outside their range (past end). Usage
11// means here dereferencing, incrementing etc.
12//
13//===----------------------------------------------------------------------===//
14
15#include "ClangSACheckers.h"
16#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17#include "clang/StaticAnalyzer/Core/Checker.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20
21#include <utility>
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27struct IteratorPosition {
28private:
29 enum Kind { InRange, OutofRange } K;
30 IteratorPosition(Kind InK) : K(InK) {}
31
32public:
33 bool isInRange() const { return K == InRange; }
34 bool isOutofRange() const { return K == OutofRange; }
35
36 static IteratorPosition getInRange() { return IteratorPosition(InRange); }
37 static IteratorPosition getOutofRange() {
38 return IteratorPosition(OutofRange);
39 }
40
41 bool operator==(const IteratorPosition &X) const { return K == X.K; }
42 bool operator!=(const IteratorPosition &X) const { return K != X.K; }
43 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
44};
45
46typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
47
48struct IteratorComparison {
49private:
50 RegionOrSymbol Left, Right;
51 bool Equality;
52
53public:
54 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
55 : Left(L), Right(R), Equality(Eq) {}
56
57 RegionOrSymbol getLeft() const { return Left; }
58 RegionOrSymbol getRight() const { return Right; }
59 bool isEquality() const { return Equality; }
60 bool operator==(const IteratorComparison &X) const {
61 return Left == X.Left && Right == X.Right && Equality == X.Equality;
62 }
63 bool operator!=(const IteratorComparison &X) const {
64 return Left != X.Left || Right != X.Right || Equality != X.Equality;
65 }
66 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
67};
68
69class IteratorPastEndChecker
70 : public Checker<
71 check::PreCall, check::PostCall, check::PreStmt<CXXOperatorCallExpr>,
72 check::PostStmt<CXXConstructExpr>, check::PostStmt<DeclStmt>,
73 check::PostStmt<MaterializeTemporaryExpr>, check::BeginFunction,
74 check::DeadSymbols, eval::Assume, eval::Call> {
75 mutable IdentifierInfo *II_find = nullptr,
76 *II_find_end = nullptr, *II_find_first_of = nullptr,
77 *II_find_if = nullptr, *II_find_if_not = nullptr,
78 *II_lower_bound = nullptr, *II_upper_bound = nullptr,
79 *II_search = nullptr, *II_search_n = nullptr;
80
81 std::unique_ptr<BugType> PastEndBugType;
82
83 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
84 const SVal &RVal, OverloadedOperatorKind Op) const;
85 void handleAccess(CheckerContext &C, const SVal &Val) const;
86 void handleDecrement(CheckerContext &C, const SVal &Val) const;
87 void handleEnd(CheckerContext &C, const SVal &RetVal) const;
88
89 bool evalFind(CheckerContext &C, const CallExpr *CE) const;
90 bool evalFindEnd(CheckerContext &C, const CallExpr *CE) const;
91 bool evalFindFirstOf(CheckerContext &C, const CallExpr *CE) const;
92 bool evalFindIf(CheckerContext &C, const CallExpr *CE) const;
93 bool evalFindIfNot(CheckerContext &C, const CallExpr *CE) const;
94 bool evalLowerBound(CheckerContext &C, const CallExpr *CE) const;
95 bool evalUpperBound(CheckerContext &C, const CallExpr *CE) const;
96 bool evalSearch(CheckerContext &C, const CallExpr *CE) const;
97 bool evalSearchN(CheckerContext &C, const CallExpr *CE) const;
98 void Find(CheckerContext &C, const CallExpr *CE) const;
99
100 void reportPastEndBug(const StringRef &Message, const SVal &Val,
101 CheckerContext &C, ExplodedNode *ErrNode) const;
102 void initIdentifiers(ASTContext &Ctx) const;
103
104public:
105 IteratorPastEndChecker();
106
107 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
108 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
109 void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
110 void checkBeginFunction(CheckerContext &C) const;
111 void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
112 void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
113 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
114 CheckerContext &C) const;
115 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
116 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
117 bool Assumption) const;
118 bool evalCall(const CallExpr *CE, CheckerContext &C) const;
119};
120}
121
122REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
123REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
124 IteratorPosition)
125
126REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
127 IteratorComparison)
128
129#define INIT_ID(Id) \
130 if (!II_##Id) \
131 II_##Id = &Ctx.Idents.get(#Id)
132
133namespace {
134
135bool isIteratorType(const QualType &Type);
136bool isIterator(const CXXRecordDecl *CRD);
137bool isEndCall(const FunctionDecl *Func);
138bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
139bool isAccessOperator(OverloadedOperatorKind OK);
140bool isDecrementOperator(OverloadedOperatorKind OK);
141BinaryOperator::Opcode getOpcode(const SymExpr *SE);
142const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
143const ProgramStateRef processComparison(ProgramStateRef State,
144 RegionOrSymbol LVal,
145 RegionOrSymbol RVal, bool Equal);
146const ProgramStateRef saveComparison(ProgramStateRef State,
147 const SymExpr *Condition, const SVal &LVal,
148 const SVal &RVal, bool Eq);
149const IteratorComparison *loadComparison(ProgramStateRef State,
150 const SymExpr *Condition);
151const IteratorPosition *getIteratorPosition(ProgramStateRef State,
152 const SVal &Val);
153const IteratorPosition *getIteratorPosition(ProgramStateRef State,
154 RegionOrSymbol RegOrSym);
155ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
156 IteratorPosition Pos);
157ProgramStateRef setIteratorPosition(ProgramStateRef State,
158 RegionOrSymbol RegOrSym,
159 IteratorPosition Pos);
160ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
161 RegionOrSymbol RegOrSym,
162 IteratorPosition Pos, bool Equal);
163bool contradictingIteratorPositions(IteratorPosition Pos1,
164 IteratorPosition Pos2, bool Equal);
165}
166
167IteratorPastEndChecker::IteratorPastEndChecker() {
168 PastEndBugType.reset(
169 new BugType(this, "Iterator Past End", "Misuse of STL APIs"));
170 PastEndBugType->setSuppressOnSink(true);
171}
172
173void IteratorPastEndChecker::checkPreCall(const CallEvent &Call,
174 CheckerContext &C) const {
175 // Check for access past end
176 const auto *Func = Call.getDecl()->getAsFunction();
177 if (!Func)
178 return;
179 if (Func->isOverloadedOperator()) {
180 if (isAccessOperator(Func->getOverloadedOperator())) {
181 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
182 handleAccess(C, InstCall->getCXXThisVal());
183 } else {
184 handleAccess(C, Call.getArgSVal(0));
185 }
186 }
187 }
188}
189
190void IteratorPastEndChecker::checkPostCall(const CallEvent &Call,
191 CheckerContext &C) const {
192 // Record end() iterators, iterator decrementation and comparison
193 const auto *Func = Call.getDecl()->getAsFunction();
194 if (!Func)
195 return;
196 if (Func->isOverloadedOperator()) {
197 const auto Op = Func->getOverloadedOperator();
198 if (isSimpleComparisonOperator(Op)) {
199 if (Func->isCXXInstanceMember()) {
200 const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
201 handleComparison(C, InstCall.getReturnValue(), InstCall.getCXXThisVal(),
202 InstCall.getArgSVal(0), Op);
203 } else {
204 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
205 Call.getArgSVal(1), Op);
206 }
207 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
208 if (Func->isCXXInstanceMember()) {
209 const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
210 handleDecrement(C, InstCall.getCXXThisVal());
211 } else {
212 handleDecrement(C, Call.getArgSVal(0));
213 }
214 }
215 } else if (Func->isCXXInstanceMember()) {
216 if (!isEndCall(Func))
217 return;
218 if (!isIteratorType(Call.getResultType()))
219 return;
220 handleEnd(C, Call.getReturnValue());
221 }
222}
223
224void IteratorPastEndChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
225 CheckerContext &C) const {
226 const auto *ThisExpr = COCE->getArg(0);
227
228 auto State = C.getState();
229 const auto *LCtx = C.getPredecessor()->getLocationContext();
230
231 const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
232 if (const auto *Reg = CurrentThis.getAsRegion()) {
233 if (!Reg->getAs<CXXTempObjectRegion>())
234 return;
235 const auto OldState = C.getPredecessor()->getFirstPred()->getState();
236 const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
237 const auto *Pos = getIteratorPosition(OldState, OldThis);
238 if (!Pos)
239 return;
240 State = setIteratorPosition(State, CurrentThis, *Pos);
241 C.addTransition(State);
242 }
243}
244
245void IteratorPastEndChecker::checkBeginFunction(CheckerContext &C) const {
246 // Copy state of iterator arguments to iterator parameters
247 auto State = C.getState();
248 const auto *LCtx = C.getLocationContext();
249
250 const auto *Site = cast<StackFrameContext>(LCtx)->getCallSite();
251 if (!Site)
252 return;
253
254 const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
255 if (!FD)
256 return;
257
258 const auto *CE = dyn_cast<CallExpr>(Site);
259 if (!CE)
260 return;
261
262 bool Change = false;
263 int idx = 0;
264 for (const auto P : FD->parameters()) {
265 auto Param = State->getLValue(P, LCtx);
266 auto Arg = State->getSVal(CE->getArg(idx++), LCtx->getParent());
267 const auto *Pos = getIteratorPosition(State, Arg);
268 if (!Pos)
269 continue;
270 State = setIteratorPosition(State, Param, *Pos);
271 Change = true;
272 }
273 if (Change) {
274 C.addTransition(State);
275 }
276}
277
278void IteratorPastEndChecker::checkPostStmt(const CXXConstructExpr *CCE,
279 CheckerContext &C) const {
280 // Transfer iterator state in case of copy or move by constructor
281 const auto *ctr = CCE->getConstructor();
282 if (!ctr->isCopyOrMoveConstructor())
283 return;
284 const auto *RHSExpr = CCE->getArg(0);
285
286 auto State = C.getState();
287 const auto *LCtx = C.getLocationContext();
288
289 const auto RetVal = State->getSVal(CCE, LCtx);
290
291 const auto RHSVal = State->getSVal(RHSExpr, LCtx);
292 const auto *RHSPos = getIteratorPosition(State, RHSVal);
293 if (!RHSPos)
294 return;
295 State = setIteratorPosition(State, RetVal, *RHSPos);
296 C.addTransition(State);
297}
298
299void IteratorPastEndChecker::checkPostStmt(const DeclStmt *DS,
300 CheckerContext &C) const {
301 // Transfer iterator state to new variable declaration
302 for (const auto *D : DS->decls()) {
303 const auto *VD = dyn_cast<VarDecl>(D);
304 if (!VD || !VD->hasInit())
305 continue;
306
307 auto State = C.getState();
308 const auto *LCtx = C.getPredecessor()->getLocationContext();
309 const auto *Pos =
310 getIteratorPosition(State, State->getSVal(VD->getInit(), LCtx));
311 if (!Pos)
312 continue;
313 State = setIteratorPosition(State, State->getLValue(VD, LCtx), *Pos);
314 C.addTransition(State);
315 }
316}
317
318void IteratorPastEndChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
319 CheckerContext &C) const {
320 /* Transfer iterator state for to temporary objects */
321 auto State = C.getState();
322 const auto *LCtx = C.getPredecessor()->getLocationContext();
323 const auto *Pos =
324 getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
325 if (!Pos)
326 return;
327 State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
328 C.addTransition(State);
329}
330
331void IteratorPastEndChecker::checkDeadSymbols(SymbolReaper &SR,
332 CheckerContext &C) const {
333 auto State = C.getState();
334
335 auto RegionMap = State->get<IteratorRegionMap>();
336 for (const auto Reg : RegionMap) {
337 if (!SR.isLiveRegion(Reg.first)) {
338 State = State->remove<IteratorRegionMap>(Reg.first);
339 }
340 }
341
342 auto SymbolMap = State->get<IteratorSymbolMap>();
343 for (const auto Sym : SymbolMap) {
344 if (SR.isDead(Sym.first)) {
345 State = State->remove<IteratorSymbolMap>(Sym.first);
346 }
347 }
348
349 auto ComparisonMap = State->get<IteratorComparisonMap>();
350 for (const auto Comp : ComparisonMap) {
351 if (SR.isDead(Comp.first)) {
352 State = State->remove<IteratorComparisonMap>(Comp.first);
353 }
354 }
355}
356
357ProgramStateRef IteratorPastEndChecker::evalAssume(ProgramStateRef State,
358 SVal Cond,
359 bool Assumption) const {
360 // Load recorded comparison and transfer iterator state between sides
361 // according to comparison operator and assumption
362 const auto *SE = Cond.getAsSymExpr();
363 if (!SE)
364 return State;
365
366 auto Opc = getOpcode(SE);
367 if (Opc != BO_EQ && Opc != BO_NE)
368 return State;
369
370 bool Negated = false;
371 const auto *Comp = loadComparison(State, SE);
372 if (!Comp) {
373 // Try negated comparison, which is a SymExpr to 0 integer comparison
374 const auto *SIE = dyn_cast<SymIntExpr>(SE);
375 if (!SIE)
376 return State;
377
378 if (SIE->getRHS() != 0)
379 return State;
380
381 SE = SIE->getLHS();
382 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
383 Opc = getOpcode(SE);
384 if (Opc != BO_EQ && Opc != BO_NE)
385 return State;
386
387 Comp = loadComparison(State, SE);
388 if (!Comp)
389 return State;
390 }
391
392 return processComparison(State, Comp->getLeft(), Comp->getRight(),
393 (Comp->isEquality() == Assumption) != Negated);
394}
395
396// FIXME: Evaluation of these STL calls should be moved to StdCLibraryFunctions
397// checker (see patch r284960) or another similar checker for C++ STL
398// functions (e.g. StdCXXLibraryFunctions or StdCppLibraryFunctions).
399bool IteratorPastEndChecker::evalCall(const CallExpr *CE,
400 CheckerContext &C) const {
401 const FunctionDecl *FD = C.getCalleeDecl(CE);
402 if (!FD)
403 return false;
404
405 ASTContext &Ctx = C.getASTContext();
406 initIdentifiers(Ctx);
407
408 if (FD->getKind() == Decl::Function) {
409 if (FD->isInStdNamespace()) {
410 if (FD->getIdentifier() == II_find) {
411 return evalFind(C, CE);
412 } else if (FD->getIdentifier() == II_find_end) {
413 return evalFindEnd(C, CE);
414 } else if (FD->getIdentifier() == II_find_first_of) {
415 return evalFindFirstOf(C, CE);
416 } else if (FD->getIdentifier() == II_find_if) {
417 return evalFindIf(C, CE);
418 } else if (FD->getIdentifier() == II_find_if) {
419 return evalFindIf(C, CE);
420 } else if (FD->getIdentifier() == II_find_if_not) {
421 return evalFindIfNot(C, CE);
422 } else if (FD->getIdentifier() == II_upper_bound) {
423 return evalUpperBound(C, CE);
424 } else if (FD->getIdentifier() == II_lower_bound) {
425 return evalLowerBound(C, CE);
426 } else if (FD->getIdentifier() == II_search) {
427 return evalSearch(C, CE);
428 } else if (FD->getIdentifier() == II_search_n) {
429 return evalSearchN(C, CE);
430 }
431 }
432 }
433
434 return false;
435}
436
437void IteratorPastEndChecker::handleComparison(CheckerContext &C,
438 const SVal &RetVal,
439 const SVal &LVal,
440 const SVal &RVal,
441 OverloadedOperatorKind Op) const {
442 // Record the operands and the operator of the comparison for the next
443 // evalAssume, if the result is a symbolic expression. If it is a concrete
444 // value (only one branch is possible), then transfer the state between
445 // the operands according to the operator and the result
446 auto State = C.getState();
447 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
448 const auto *LPos = getIteratorPosition(State, LVal);
449 const auto *RPos = getIteratorPosition(State, RVal);
450 if (!LPos && !RPos)
451 return;
452 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
453 C.addTransition(State);
454 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
455 if ((State = processComparison(
456 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
457 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
458 C.addTransition(State);
459 } else {
460 C.generateSink(State, C.getPredecessor());
461 }
462 }
463}
464
465void IteratorPastEndChecker::handleAccess(CheckerContext &C,
466 const SVal &Val) const {
467 auto State = C.getState();
468 const auto *Pos = getIteratorPosition(State, Val);
469 if (Pos && Pos->isOutofRange()) {
470 auto *N = C.generateNonFatalErrorNode(State);
471 if (!N) {
472 return;
473 }
474 reportPastEndBug("Iterator accessed past its end.", Val, C, N);
475 }
476}
477
478void IteratorPastEndChecker::handleDecrement(CheckerContext &C,
479 const SVal &Val) const {
480 auto State = C.getState();
481 const auto *Pos = getIteratorPosition(State, Val);
482 if (Pos && Pos->isOutofRange()) {
483 State = setIteratorPosition(State, Val, IteratorPosition::getInRange());
484 // FIXME: We could also check for iterators ahead of their beginnig in the
485 // future, but currently we do not care for such errors. We also
486 // assume that the iterator is not past its end by more then one
487 // position.
488 C.addTransition(State);
489 }
490}
491
492void IteratorPastEndChecker::handleEnd(CheckerContext &C,
493 const SVal &RetVal) const {
494 auto State = C.getState();
495 State = setIteratorPosition(State, RetVal, IteratorPosition::getOutofRange());
496 C.addTransition(State);
497}
498
499bool IteratorPastEndChecker::evalFind(CheckerContext &C,
500 const CallExpr *CE) const {
501 if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
502 isIteratorType(CE->getArg(1)->getType())) {
503 Find(C, CE);
504 return true;
505 }
506 return false;
507}
508
509bool IteratorPastEndChecker::evalFindEnd(CheckerContext &C,
510 const CallExpr *CE) const {
511 if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
512 isIteratorType(CE->getArg(0)->getType()) &&
513 isIteratorType(CE->getArg(1)->getType()) &&
514 isIteratorType(CE->getArg(2)->getType()) &&
515 isIteratorType(CE->getArg(3)->getType())) {
516 Find(C, CE);
517 return true;
518 }
519 return false;
520}
521
522bool IteratorPastEndChecker::evalFindFirstOf(CheckerContext &C,
523 const CallExpr *CE) const {
524 if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
525 isIteratorType(CE->getArg(0)->getType()) &&
526 isIteratorType(CE->getArg(1)->getType()) &&
527 isIteratorType(CE->getArg(2)->getType()) &&
528 isIteratorType(CE->getArg(3)->getType())) {
529 Find(C, CE);
530 return true;
531 }
532 return false;
533}
534
535bool IteratorPastEndChecker::evalFindIf(CheckerContext &C,
536 const CallExpr *CE) const {
537 if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
538 isIteratorType(CE->getArg(1)->getType())) {
539 Find(C, CE);
540 return true;
541 }
542 return false;
543}
544
545bool IteratorPastEndChecker::evalFindIfNot(CheckerContext &C,
546 const CallExpr *CE) const {
547 if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
548 isIteratorType(CE->getArg(1)->getType())) {
549 Find(C, CE);
550 return true;
551 }
552 return false;
553}
554
555bool IteratorPastEndChecker::evalLowerBound(CheckerContext &C,
556 const CallExpr *CE) const {
557 if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
558 isIteratorType(CE->getArg(0)->getType()) &&
559 isIteratorType(CE->getArg(1)->getType())) {
560 Find(C, CE);
561 return true;
562 }
563 return false;
564}
565
566bool IteratorPastEndChecker::evalUpperBound(CheckerContext &C,
567 const CallExpr *CE) const {
568 if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
569 isIteratorType(CE->getArg(0)->getType()) &&
570 isIteratorType(CE->getArg(1)->getType())) {
571 Find(C, CE);
572 return true;
573 }
574 return false;
575}
576
577bool IteratorPastEndChecker::evalSearch(CheckerContext &C,
578 const CallExpr *CE) const {
579 if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
580 isIteratorType(CE->getArg(0)->getType()) &&
581 isIteratorType(CE->getArg(1)->getType()) &&
582 isIteratorType(CE->getArg(2)->getType()) &&
583 isIteratorType(CE->getArg(3)->getType())) {
584 Find(C, CE);
585 return true;
586 }
587 return false;
588}
589
590bool IteratorPastEndChecker::evalSearchN(CheckerContext &C,
591 const CallExpr *CE) const {
592 if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
593 isIteratorType(CE->getArg(0)->getType()) &&
594 isIteratorType(CE->getArg(1)->getType())) {
595 Find(C, CE);
596 return true;
597 }
598 return false;
599}
600
601void IteratorPastEndChecker::Find(CheckerContext &C, const CallExpr *CE) const {
602 auto state = C.getState();
603 auto &svalBuilder = C.getSValBuilder();
604 const auto *LCtx = C.getLocationContext();
605
606 auto RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
607 auto SecondParam = state->getSVal(CE->getArg(1), LCtx);
608
609 auto stateFound = state->BindExpr(CE, LCtx, RetVal);
610 auto stateNotFound = state->BindExpr(CE, LCtx, SecondParam);
611
612 C.addTransition(stateFound);
613 C.addTransition(stateNotFound);
614}
615
616void IteratorPastEndChecker::reportPastEndBug(const StringRef &Message,
617 const SVal &Val,
618 CheckerContext &C,
619 ExplodedNode *ErrNode) const {
620 auto R = llvm::make_unique<BugReport>(*PastEndBugType, Message, ErrNode);
621 R->markInteresting(Val);
622 C.emitReport(std::move(R));
623}
624
625void IteratorPastEndChecker::initIdentifiers(ASTContext &Ctx) const {
626 INIT_ID(find);
627 INIT_ID(find_end);
628 INIT_ID(find_first_of);
629 INIT_ID(find_if);
630 INIT_ID(find_if_not);
631 INIT_ID(lower_bound);
632 INIT_ID(upper_bound);
633 INIT_ID(search);
634 INIT_ID(search_n);
635}
636
637namespace {
638
639bool isIteratorType(const QualType &Type) {
640 if (Type->isPointerType())
641 return true;
642
643 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
644 return isIterator(CRD);
645}
646
647bool isIterator(const CXXRecordDecl *CRD) {
648 if (!CRD)
649 return false;
650
651 const auto Name = CRD->getName();
652 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
653 Name.endswith_lower("it")))
654 return false;
655
656 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
657 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
658 for (const auto *Method : CRD->methods()) {
659 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
660 if (Ctor->isCopyConstructor()) {
661 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
662 }
663 continue;
664 }
665 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
666 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
667 continue;
668 }
669 if (Method->isCopyAssignmentOperator()) {
670 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
671 continue;
672 }
673 if (!Method->isOverloadedOperator())
674 continue;
675 const auto OPK = Method->getOverloadedOperator();
676 if (OPK == OO_PlusPlus) {
677 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
678 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
679 continue;
680 }
681 if (OPK == OO_Star) {
682 HasDerefOp = (Method->getNumParams() == 0);
683 continue;
684 }
685 }
686
687 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
688 HasPostIncrOp && HasDerefOp;
689}
690
691bool isEndCall(const FunctionDecl *Func) {
692 const auto *IdInfo = Func->getIdentifier();
693 if (!IdInfo)
694 return false;
695 return IdInfo->getName().endswith_lower("end");
696}
697
698bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
699 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
700}
701
702bool isAccessOperator(OverloadedOperatorKind OK) {
703 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
704 OK == OO_Plus || OK == OO_PlusEqual || OK == OO_PlusPlus ||
705 OK == OO_Subscript;
706}
707
708bool isDecrementOperator(OverloadedOperatorKind OK) {
709 return OK == OO_MinusEqual || OK == OO_MinusMinus;
710}
711
712BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
713 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
714 return BSE->getOpcode();
715 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
716 const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
717 if (!COE)
718 return BO_Comma; // Extremal value, neither EQ nor NE
719 if (COE->getOperator() == OO_EqualEqual) {
720 return BO_EQ;
721 } else if (COE->getOperator() == OO_ExclaimEqual) {
722 return BO_NE;
723 }
724 return BO_Comma; // Extremal value, neither EQ nor NE
725 }
726 return BO_Comma; // Extremal value, neither EQ nor NE
727}
728
729const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
730 if (const auto Reg = Val.getAsRegion()) {
731 return Reg;
732 } else if (const auto Sym = Val.getAsSymbol()) {
733 return Sym;
734 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
735 return LCVal->getRegion();
736 }
737 return RegionOrSymbol();
738}
739
740const ProgramStateRef processComparison(ProgramStateRef State,
741 RegionOrSymbol LVal,
742 RegionOrSymbol RVal, bool Equal) {
743 const auto *LPos = getIteratorPosition(State, LVal);
744 const auto *RPos = getIteratorPosition(State, RVal);
745 if (LPos && !RPos) {
746 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
747 } else if (!LPos && RPos) {
748 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
749 } else if (LPos && RPos) {
750 if (contradictingIteratorPositions(*LPos, *RPos, Equal)) {
751 return nullptr;
752 }
753 }
754 return State;
755}
756
757const ProgramStateRef saveComparison(ProgramStateRef State,
758 const SymExpr *Condition, const SVal &LVal,
759 const SVal &RVal, bool Eq) {
760 const auto Left = getRegionOrSymbol(LVal);
761 const auto Right = getRegionOrSymbol(RVal);
762 if (!Left || !Right)
763 return State;
764 return State->set<IteratorComparisonMap>(Condition,
765 IteratorComparison(Left, Right, Eq));
766}
767
768const IteratorComparison *loadComparison(ProgramStateRef State,
769 const SymExpr *Condition) {
770 return State->get<IteratorComparisonMap>(Condition);
771}
772
773const IteratorPosition *getIteratorPosition(ProgramStateRef State,
774 const SVal &Val) {
775 if (const auto Reg = Val.getAsRegion()) {
776 return State->get<IteratorRegionMap>(Reg);
777 } else if (const auto Sym = Val.getAsSymbol()) {
778 return State->get<IteratorSymbolMap>(Sym);
779 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
780 return State->get<IteratorRegionMap>(LCVal->getRegion());
781 }
782 return nullptr;
783}
784
785const IteratorPosition *getIteratorPosition(ProgramStateRef State,
786 RegionOrSymbol RegOrSym) {
787 if (RegOrSym.is<const MemRegion *>()) {
788 return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
789 } else if (RegOrSym.is<SymbolRef>()) {
790 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
791 }
792 return nullptr;
793}
794
795ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
796 IteratorPosition Pos) {
797 if (const auto Reg = Val.getAsRegion()) {
798 return State->set<IteratorRegionMap>(Reg, Pos);
799 } else if (const auto Sym = Val.getAsSymbol()) {
800 return State->set<IteratorSymbolMap>(Sym, Pos);
801 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
802 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
803 }
804 return nullptr;
805}
806
807ProgramStateRef setIteratorPosition(ProgramStateRef State,
808 RegionOrSymbol RegOrSym,
809 IteratorPosition Pos) {
810 if (RegOrSym.is<const MemRegion *>()) {
811 return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
812 Pos);
813 } else if (RegOrSym.is<SymbolRef>()) {
814 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
815 }
816 return nullptr;
817}
818
819ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
820 RegionOrSymbol RegOrSym,
821 IteratorPosition Pos, bool Equal) {
822
823 if ((Pos.isInRange() && Equal) || (Pos.isOutofRange() && !Equal)) {
824 return setIteratorPosition(State, RegOrSym, IteratorPosition::getInRange());
825 } else if (Pos.isOutofRange() && Equal) {
826 return setIteratorPosition(State, RegOrSym,
827 IteratorPosition::getOutofRange());
828 } else {
829 return State;
830 }
831}
832
833bool contradictingIteratorPositions(IteratorPosition Pos1,
834 IteratorPosition Pos2, bool Equal) {
835 return ((Pos1 != Pos2) && Equal) ||
836 ((Pos1.isOutofRange() && Pos2.isOutofRange()) && !Equal);
837}
838}
839
840void ento::registerIteratorPastEndChecker(CheckerManager &Mgr) {
841 Mgr.registerChecker<IteratorPastEndChecker>();
842}