]> granicus.if.org Git - clang/commitdiff
[analyzer] Add checker for iterators dereferenced beyond their range.
authorGabor Horvath <xazax.hun@gmail.com>
Mon, 9 Jan 2017 09:52:32 +0000 (09:52 +0000)
committerGabor Horvath <xazax.hun@gmail.com>
Mon, 9 Jan 2017 09:52:32 +0000 (09:52 +0000)
Patch by: Adam Balogh!

Differential Revision: https://reviews.llvm.org/D25660

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@291430 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Checkers/Checkers.td
lib/StaticAnalyzer/Checkers/CMakeLists.txt
lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp [new file with mode: 0644]
lib/StaticAnalyzer/Core/ExprEngine.cpp
test/Analysis/Inputs/system-header-simulator-cxx.h
test/Analysis/diagnostics/explicit-suppression.cpp
test/Analysis/inlining/stl.cpp
test/Analysis/iterator-past-end.cpp [new file with mode: 0644]

index 2e58debf80fc6542d2a08ac53366ada08095b246..69578910499f698ed3d5c2df40a3c86376a6b2bf 100644 (file)
@@ -278,6 +278,14 @@ def VirtualCallChecker : Checker<"VirtualCall">,
 
 } // end: "optin.cplusplus"
 
+let ParentPackage = CplusplusAlpha in {
+
+def IteratorPastEndChecker : Checker<"IteratorPastEnd">,
+  HelpText<"Check iterators used past end">,
+  DescFile<"IteratorPastEndChecker.cpp">;
+
+} // end: "alpha.cplusplus"
+
 
 //===----------------------------------------------------------------------===//
 // Valist checkers.
index 41415f0376c000beb8e21fe853c9d55b7cef4a96..05505ec38600fa32f30d9a5081294838b3340029 100644 (file)
@@ -39,6 +39,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   GenericTaintChecker.cpp
   GTestChecker.cpp
   IdenticalExprChecker.cpp
+  IteratorPastEndChecker.cpp
   IvarInvalidationChecker.cpp
   LLVMConventionsChecker.cpp
   LocalizationChecker.cpp
diff --git a/lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp
new file mode 100644 (file)
index 0000000..531054a
--- /dev/null
@@ -0,0 +1,842 @@
+//===-- IteratorPastEndChecker.cpp --------------------------------*- C++ -*--//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for using iterators outside their range (past end). Usage
+// means here dereferencing, incrementing etc.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct IteratorPosition {
+private:
+  enum Kind { InRange, OutofRange } K;
+  IteratorPosition(Kind InK) : K(InK) {}
+
+public:
+  bool isInRange() const { return K == InRange; }
+  bool isOutofRange() const { return K == OutofRange; }
+
+  static IteratorPosition getInRange() { return IteratorPosition(InRange); }
+  static IteratorPosition getOutofRange() {
+    return IteratorPosition(OutofRange);
+  }
+
+  bool operator==(const IteratorPosition &X) const { return K == X.K; }
+  bool operator!=(const IteratorPosition &X) const { return K != X.K; }
+  void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
+};
+
+typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
+
+struct IteratorComparison {
+private:
+  RegionOrSymbol Left, Right;
+  bool Equality;
+
+public:
+  IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
+      : Left(L), Right(R), Equality(Eq) {}
+
+  RegionOrSymbol getLeft() const { return Left; }
+  RegionOrSymbol getRight() const { return Right; }
+  bool isEquality() const { return Equality; }
+  bool operator==(const IteratorComparison &X) const {
+    return Left == X.Left && Right == X.Right && Equality == X.Equality;
+  }
+  bool operator!=(const IteratorComparison &X) const {
+    return Left != X.Left || Right != X.Right || Equality != X.Equality;
+  }
+  void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
+};
+
+class IteratorPastEndChecker
+    : public Checker<
+          check::PreCall, check::PostCall, check::PreStmt<CXXOperatorCallExpr>,
+          check::PostStmt<CXXConstructExpr>, check::PostStmt<DeclStmt>,
+          check::PostStmt<MaterializeTemporaryExpr>, check::BeginFunction,
+          check::DeadSymbols, eval::Assume, eval::Call> {
+  mutable IdentifierInfo *II_find = nullptr,
+                         *II_find_end = nullptr, *II_find_first_of = nullptr,
+                         *II_find_if = nullptr, *II_find_if_not = nullptr,
+                         *II_lower_bound = nullptr, *II_upper_bound = nullptr,
+                         *II_search = nullptr, *II_search_n = nullptr;
+
+  std::unique_ptr<BugType> PastEndBugType;
+
+  void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
+                        const SVal &RVal, OverloadedOperatorKind Op) const;
+  void handleAccess(CheckerContext &C, const SVal &Val) const;
+  void handleDecrement(CheckerContext &C, const SVal &Val) const;
+  void handleEnd(CheckerContext &C, const SVal &RetVal) const;
+
+  bool evalFind(CheckerContext &C, const CallExpr *CE) const;
+  bool evalFindEnd(CheckerContext &C, const CallExpr *CE) const;
+  bool evalFindFirstOf(CheckerContext &C, const CallExpr *CE) const;
+  bool evalFindIf(CheckerContext &C, const CallExpr *CE) const;
+  bool evalFindIfNot(CheckerContext &C, const CallExpr *CE) const;
+  bool evalLowerBound(CheckerContext &C, const CallExpr *CE) const;
+  bool evalUpperBound(CheckerContext &C, const CallExpr *CE) const;
+  bool evalSearch(CheckerContext &C, const CallExpr *CE) const;
+  bool evalSearchN(CheckerContext &C, const CallExpr *CE) const;
+  void Find(CheckerContext &C, const CallExpr *CE) const;
+
+  void reportPastEndBug(const StringRef &Message, const SVal &Val,
+                        CheckerContext &C, ExplodedNode *ErrNode) const;
+  void initIdentifiers(ASTContext &Ctx) const;
+
+public:
+  IteratorPastEndChecker();
+
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
+  void checkBeginFunction(CheckerContext &C) const;
+  void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
+  void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
+  void checkPostStmt(const MaterializeTemporaryExpr *MTE,
+                     CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+  ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+                             bool Assumption) const;
+  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+};
+}
+
+REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
+REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
+                               IteratorPosition)
+
+REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
+                               IteratorComparison)
+
+#define INIT_ID(Id)                                                            \
+  if (!II_##Id)                                                                \
+  II_##Id = &Ctx.Idents.get(#Id)
+
+namespace {
+
+bool isIteratorType(const QualType &Type);
+bool isIterator(const CXXRecordDecl *CRD);
+bool isEndCall(const FunctionDecl *Func);
+bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
+bool isAccessOperator(OverloadedOperatorKind OK);
+bool isDecrementOperator(OverloadedOperatorKind OK);
+BinaryOperator::Opcode getOpcode(const SymExpr *SE);
+const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
+const ProgramStateRef processComparison(ProgramStateRef State,
+                                        RegionOrSymbol LVal,
+                                        RegionOrSymbol RVal, bool Equal);
+const ProgramStateRef saveComparison(ProgramStateRef State,
+                                     const SymExpr *Condition, const SVal &LVal,
+                                     const SVal &RVal, bool Eq);
+const IteratorComparison *loadComparison(ProgramStateRef State,
+                                         const SymExpr *Condition);
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+                                            const SVal &Val);
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+                                            RegionOrSymbol RegOrSym);
+ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
+                                    IteratorPosition Pos);
+ProgramStateRef setIteratorPosition(ProgramStateRef State,
+                                    RegionOrSymbol RegOrSym,
+                                    IteratorPosition Pos);
+ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
+                                       RegionOrSymbol RegOrSym,
+                                       IteratorPosition Pos, bool Equal);
+bool contradictingIteratorPositions(IteratorPosition Pos1,
+                                    IteratorPosition Pos2, bool Equal);
+}
+
+IteratorPastEndChecker::IteratorPastEndChecker() {
+  PastEndBugType.reset(
+      new BugType(this, "Iterator Past End", "Misuse of STL APIs"));
+  PastEndBugType->setSuppressOnSink(true);
+}
+
+void IteratorPastEndChecker::checkPreCall(const CallEvent &Call,
+                                          CheckerContext &C) const {
+  // Check for access past end
+  const auto *Func = Call.getDecl()->getAsFunction();
+  if (!Func)
+    return;
+  if (Func->isOverloadedOperator()) {
+    if (isAccessOperator(Func->getOverloadedOperator())) {
+      if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+        handleAccess(C, InstCall->getCXXThisVal());
+      } else {
+        handleAccess(C, Call.getArgSVal(0));
+      }
+    }
+  }
+}
+
+void IteratorPastEndChecker::checkPostCall(const CallEvent &Call,
+                                           CheckerContext &C) const {
+  // Record end() iterators, iterator decrementation and comparison
+  const auto *Func = Call.getDecl()->getAsFunction();
+  if (!Func)
+    return;
+  if (Func->isOverloadedOperator()) {
+    const auto Op = Func->getOverloadedOperator();
+    if (isSimpleComparisonOperator(Op)) {
+      if (Func->isCXXInstanceMember()) {
+        const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
+        handleComparison(C, InstCall.getReturnValue(), InstCall.getCXXThisVal(),
+                         InstCall.getArgSVal(0), Op);
+      } else {
+        handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
+                         Call.getArgSVal(1), Op);
+      }
+    } else if (isDecrementOperator(Func->getOverloadedOperator())) {
+      if (Func->isCXXInstanceMember()) {
+        const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
+        handleDecrement(C, InstCall.getCXXThisVal());
+      } else {
+        handleDecrement(C, Call.getArgSVal(0));
+      }
+    }
+  } else if (Func->isCXXInstanceMember()) {
+    if (!isEndCall(Func))
+      return;
+    if (!isIteratorType(Call.getResultType()))
+      return;
+    handleEnd(C, Call.getReturnValue());
+  }
+}
+
+void IteratorPastEndChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
+                                          CheckerContext &C) const {
+  const auto *ThisExpr = COCE->getArg(0);
+
+  auto State = C.getState();
+  const auto *LCtx = C.getPredecessor()->getLocationContext();
+
+  const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
+  if (const auto *Reg = CurrentThis.getAsRegion()) {
+    if (!Reg->getAs<CXXTempObjectRegion>())
+      return;
+    const auto OldState = C.getPredecessor()->getFirstPred()->getState();
+    const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
+    const auto *Pos = getIteratorPosition(OldState, OldThis);
+    if (!Pos)
+      return;
+    State = setIteratorPosition(State, CurrentThis, *Pos);
+    C.addTransition(State);
+  }
+}
+
+void IteratorPastEndChecker::checkBeginFunction(CheckerContext &C) const {
+  // Copy state of iterator arguments to iterator parameters
+  auto State = C.getState();
+  const auto *LCtx = C.getLocationContext();
+
+  const auto *Site = cast<StackFrameContext>(LCtx)->getCallSite();
+  if (!Site)
+    return;
+
+  const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
+  if (!FD)
+    return;
+
+  const auto *CE = dyn_cast<CallExpr>(Site);
+  if (!CE)
+    return;
+
+  bool Change = false;
+  int idx = 0;
+  for (const auto P : FD->parameters()) {
+    auto Param = State->getLValue(P, LCtx);
+    auto Arg = State->getSVal(CE->getArg(idx++), LCtx->getParent());
+    const auto *Pos = getIteratorPosition(State, Arg);
+    if (!Pos)
+      continue;
+    State = setIteratorPosition(State, Param, *Pos);
+    Change = true;
+  }
+  if (Change) {
+    C.addTransition(State);
+  }
+}
+
+void IteratorPastEndChecker::checkPostStmt(const CXXConstructExpr *CCE,
+                                           CheckerContext &C) const {
+  // Transfer iterator state in case of copy or move by constructor
+  const auto *ctr = CCE->getConstructor();
+  if (!ctr->isCopyOrMoveConstructor())
+    return;
+  const auto *RHSExpr = CCE->getArg(0);
+
+  auto State = C.getState();
+  const auto *LCtx = C.getLocationContext();
+
+  const auto RetVal = State->getSVal(CCE, LCtx);
+
+  const auto RHSVal = State->getSVal(RHSExpr, LCtx);
+  const auto *RHSPos = getIteratorPosition(State, RHSVal);
+  if (!RHSPos)
+    return;
+  State = setIteratorPosition(State, RetVal, *RHSPos);
+  C.addTransition(State);
+}
+
+void IteratorPastEndChecker::checkPostStmt(const DeclStmt *DS,
+                                           CheckerContext &C) const {
+  // Transfer iterator state to new variable declaration
+  for (const auto *D : DS->decls()) {
+    const auto *VD = dyn_cast<VarDecl>(D);
+    if (!VD || !VD->hasInit())
+      continue;
+
+    auto State = C.getState();
+    const auto *LCtx = C.getPredecessor()->getLocationContext();
+    const auto *Pos =
+        getIteratorPosition(State, State->getSVal(VD->getInit(), LCtx));
+    if (!Pos)
+      continue;
+    State = setIteratorPosition(State, State->getLValue(VD, LCtx), *Pos);
+    C.addTransition(State);
+  }
+}
+
+void IteratorPastEndChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
+                                           CheckerContext &C) const {
+  /* Transfer iterator state for to temporary objects */
+  auto State = C.getState();
+  const auto *LCtx = C.getPredecessor()->getLocationContext();
+  const auto *Pos =
+      getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
+  if (!Pos)
+    return;
+  State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
+  C.addTransition(State);
+}
+
+void IteratorPastEndChecker::checkDeadSymbols(SymbolReaper &SR,
+                                              CheckerContext &C) const {
+  auto State = C.getState();
+
+  auto RegionMap = State->get<IteratorRegionMap>();
+  for (const auto Reg : RegionMap) {
+    if (!SR.isLiveRegion(Reg.first)) {
+      State = State->remove<IteratorRegionMap>(Reg.first);
+    }
+  }
+
+  auto SymbolMap = State->get<IteratorSymbolMap>();
+  for (const auto Sym : SymbolMap) {
+    if (SR.isDead(Sym.first)) {
+      State = State->remove<IteratorSymbolMap>(Sym.first);
+    }
+  }
+
+  auto ComparisonMap = State->get<IteratorComparisonMap>();
+  for (const auto Comp : ComparisonMap) {
+    if (SR.isDead(Comp.first)) {
+      State = State->remove<IteratorComparisonMap>(Comp.first);
+    }
+  }
+}
+
+ProgramStateRef IteratorPastEndChecker::evalAssume(ProgramStateRef State,
+                                                   SVal Cond,
+                                                   bool Assumption) const {
+  // Load recorded comparison and transfer iterator state between sides
+  // according to comparison operator and assumption
+  const auto *SE = Cond.getAsSymExpr();
+  if (!SE)
+    return State;
+
+  auto Opc = getOpcode(SE);
+  if (Opc != BO_EQ && Opc != BO_NE)
+    return State;
+
+  bool Negated = false;
+  const auto *Comp = loadComparison(State, SE);
+  if (!Comp) {
+    // Try negated comparison, which is a SymExpr to 0 integer comparison
+    const auto *SIE = dyn_cast<SymIntExpr>(SE);
+    if (!SIE)
+      return State;
+
+    if (SIE->getRHS() != 0)
+      return State;
+
+    SE = SIE->getLHS();
+    Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
+    Opc = getOpcode(SE);
+    if (Opc != BO_EQ && Opc != BO_NE)
+      return State;
+
+    Comp = loadComparison(State, SE);
+    if (!Comp)
+      return State;
+  }
+
+  return processComparison(State, Comp->getLeft(), Comp->getRight(),
+                           (Comp->isEquality() == Assumption) != Negated);
+}
+
+// FIXME: Evaluation of these STL calls should be moved to StdCLibraryFunctions
+//       checker (see patch r284960) or another similar checker for C++ STL
+//       functions (e.g. StdCXXLibraryFunctions or StdCppLibraryFunctions).
+bool IteratorPastEndChecker::evalCall(const CallExpr *CE,
+                                      CheckerContext &C) const {
+  const FunctionDecl *FD = C.getCalleeDecl(CE);
+  if (!FD)
+    return false;
+
+  ASTContext &Ctx = C.getASTContext();
+  initIdentifiers(Ctx);
+
+  if (FD->getKind() == Decl::Function) {
+    if (FD->isInStdNamespace()) {
+      if (FD->getIdentifier() == II_find) {
+        return evalFind(C, CE);
+      } else if (FD->getIdentifier() == II_find_end) {
+        return evalFindEnd(C, CE);
+      } else if (FD->getIdentifier() == II_find_first_of) {
+        return evalFindFirstOf(C, CE);
+      } else if (FD->getIdentifier() == II_find_if) {
+        return evalFindIf(C, CE);
+      } else if (FD->getIdentifier() == II_find_if) {
+        return evalFindIf(C, CE);
+      } else if (FD->getIdentifier() == II_find_if_not) {
+        return evalFindIfNot(C, CE);
+      } else if (FD->getIdentifier() == II_upper_bound) {
+        return evalUpperBound(C, CE);
+      } else if (FD->getIdentifier() == II_lower_bound) {
+        return evalLowerBound(C, CE);
+      } else if (FD->getIdentifier() == II_search) {
+        return evalSearch(C, CE);
+      } else if (FD->getIdentifier() == II_search_n) {
+        return evalSearchN(C, CE);
+      }
+    }
+  }
+
+  return false;
+}
+
+void IteratorPastEndChecker::handleComparison(CheckerContext &C,
+                                              const SVal &RetVal,
+                                              const SVal &LVal,
+                                              const SVal &RVal,
+                                              OverloadedOperatorKind Op) const {
+  // Record the operands and the operator of the comparison for the next
+  // evalAssume, if the result is a symbolic expression. If it is a concrete
+  // value (only one branch is possible), then transfer the state between
+  // the operands according to the operator and the result
+  auto State = C.getState();
+  if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
+    const auto *LPos = getIteratorPosition(State, LVal);
+    const auto *RPos = getIteratorPosition(State, RVal);
+    if (!LPos && !RPos)
+      return;
+    State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
+    C.addTransition(State);
+  } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
+    if ((State = processComparison(
+             State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
+             (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
+      C.addTransition(State);
+    } else {
+      C.generateSink(State, C.getPredecessor());
+    }
+  }
+}
+
+void IteratorPastEndChecker::handleAccess(CheckerContext &C,
+                                          const SVal &Val) const {
+  auto State = C.getState();
+  const auto *Pos = getIteratorPosition(State, Val);
+  if (Pos && Pos->isOutofRange()) {
+    auto *N = C.generateNonFatalErrorNode(State);
+    if (!N) {
+      return;
+    }
+    reportPastEndBug("Iterator accessed past its end.", Val, C, N);
+  }
+}
+
+void IteratorPastEndChecker::handleDecrement(CheckerContext &C,
+                                             const SVal &Val) const {
+  auto State = C.getState();
+  const auto *Pos = getIteratorPosition(State, Val);
+  if (Pos && Pos->isOutofRange()) {
+    State = setIteratorPosition(State, Val, IteratorPosition::getInRange());
+    // FIXME: We could also check for iterators ahead of their beginnig in the
+    //       future, but currently we do not care for such errors. We also
+    //       assume that the iterator is not past its end by more then one
+    //       position.
+    C.addTransition(State);
+  }
+}
+
+void IteratorPastEndChecker::handleEnd(CheckerContext &C,
+                                       const SVal &RetVal) const {
+  auto State = C.getState();
+  State = setIteratorPosition(State, RetVal, IteratorPosition::getOutofRange());
+  C.addTransition(State);
+}
+
+bool IteratorPastEndChecker::evalFind(CheckerContext &C,
+                                      const CallExpr *CE) const {
+  if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+bool IteratorPastEndChecker::evalFindEnd(CheckerContext &C,
+                                         const CallExpr *CE) const {
+  if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
+      isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType()) &&
+      isIteratorType(CE->getArg(2)->getType()) &&
+      isIteratorType(CE->getArg(3)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+bool IteratorPastEndChecker::evalFindFirstOf(CheckerContext &C,
+                                             const CallExpr *CE) const {
+  if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
+      isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType()) &&
+      isIteratorType(CE->getArg(2)->getType()) &&
+      isIteratorType(CE->getArg(3)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+bool IteratorPastEndChecker::evalFindIf(CheckerContext &C,
+                                        const CallExpr *CE) const {
+  if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+bool IteratorPastEndChecker::evalFindIfNot(CheckerContext &C,
+                                           const CallExpr *CE) const {
+  if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+bool IteratorPastEndChecker::evalLowerBound(CheckerContext &C,
+                                            const CallExpr *CE) const {
+  if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
+      isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+bool IteratorPastEndChecker::evalUpperBound(CheckerContext &C,
+                                            const CallExpr *CE) const {
+  if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
+      isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+bool IteratorPastEndChecker::evalSearch(CheckerContext &C,
+                                        const CallExpr *CE) const {
+  if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
+      isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType()) &&
+      isIteratorType(CE->getArg(2)->getType()) &&
+      isIteratorType(CE->getArg(3)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+bool IteratorPastEndChecker::evalSearchN(CheckerContext &C,
+                                         const CallExpr *CE) const {
+  if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
+      isIteratorType(CE->getArg(0)->getType()) &&
+      isIteratorType(CE->getArg(1)->getType())) {
+    Find(C, CE);
+    return true;
+  }
+  return false;
+}
+
+void IteratorPastEndChecker::Find(CheckerContext &C, const CallExpr *CE) const {
+  auto state = C.getState();
+  auto &svalBuilder = C.getSValBuilder();
+  const auto *LCtx = C.getLocationContext();
+
+  auto RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+  auto SecondParam = state->getSVal(CE->getArg(1), LCtx);
+
+  auto stateFound = state->BindExpr(CE, LCtx, RetVal);
+  auto stateNotFound = state->BindExpr(CE, LCtx, SecondParam);
+
+  C.addTransition(stateFound);
+  C.addTransition(stateNotFound);
+}
+
+void IteratorPastEndChecker::reportPastEndBug(const StringRef &Message,
+                                              const SVal &Val,
+                                              CheckerContext &C,
+                                              ExplodedNode *ErrNode) const {
+  auto R = llvm::make_unique<BugReport>(*PastEndBugType, Message, ErrNode);
+  R->markInteresting(Val);
+  C.emitReport(std::move(R));
+}
+
+void IteratorPastEndChecker::initIdentifiers(ASTContext &Ctx) const {
+  INIT_ID(find);
+  INIT_ID(find_end);
+  INIT_ID(find_first_of);
+  INIT_ID(find_if);
+  INIT_ID(find_if_not);
+  INIT_ID(lower_bound);
+  INIT_ID(upper_bound);
+  INIT_ID(search);
+  INIT_ID(search_n);
+}
+
+namespace {
+
+bool isIteratorType(const QualType &Type) {
+  if (Type->isPointerType())
+    return true;
+
+  const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+  return isIterator(CRD);
+}
+
+bool isIterator(const CXXRecordDecl *CRD) {
+  if (!CRD)
+    return false;
+
+  const auto Name = CRD->getName();
+  if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
+        Name.endswith_lower("it")))
+    return false;
+
+  bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
+       HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
+  for (const auto *Method : CRD->methods()) {
+    if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
+      if (Ctor->isCopyConstructor()) {
+        HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
+      }
+      continue;
+    }
+    if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
+      HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
+      continue;
+    }
+    if (Method->isCopyAssignmentOperator()) {
+      HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
+      continue;
+    }
+    if (!Method->isOverloadedOperator())
+      continue;
+    const auto OPK = Method->getOverloadedOperator();
+    if (OPK == OO_PlusPlus) {
+      HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
+      HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
+      continue;
+    }
+    if (OPK == OO_Star) {
+      HasDerefOp = (Method->getNumParams() == 0);
+      continue;
+    }
+  }
+
+  return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
+         HasPostIncrOp && HasDerefOp;
+}
+
+bool isEndCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  return IdInfo->getName().endswith_lower("end");
+}
+
+bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
+  return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
+}
+
+bool isAccessOperator(OverloadedOperatorKind OK) {
+  return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
+         OK == OO_Plus || OK == OO_PlusEqual || OK == OO_PlusPlus ||
+         OK == OO_Subscript;
+}
+
+bool isDecrementOperator(OverloadedOperatorKind OK) {
+  return OK == OO_MinusEqual || OK == OO_MinusMinus;
+}
+
+BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
+  if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
+    return BSE->getOpcode();
+  } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
+    const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
+    if (!COE)
+      return BO_Comma; // Extremal value, neither EQ nor NE
+    if (COE->getOperator() == OO_EqualEqual) {
+      return BO_EQ;
+    } else if (COE->getOperator() == OO_ExclaimEqual) {
+      return BO_NE;
+    }
+    return BO_Comma; // Extremal value, neither EQ nor NE
+  }
+  return BO_Comma; // Extremal value, neither EQ nor NE
+}
+
+const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
+  if (const auto Reg = Val.getAsRegion()) {
+    return Reg;
+  } else if (const auto Sym = Val.getAsSymbol()) {
+    return Sym;
+  } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+    return LCVal->getRegion();
+  }
+  return RegionOrSymbol();
+}
+
+const ProgramStateRef processComparison(ProgramStateRef State,
+                                        RegionOrSymbol LVal,
+                                        RegionOrSymbol RVal, bool Equal) {
+  const auto *LPos = getIteratorPosition(State, LVal);
+  const auto *RPos = getIteratorPosition(State, RVal);
+  if (LPos && !RPos) {
+    State = adjustIteratorPosition(State, RVal, *LPos, Equal);
+  } else if (!LPos && RPos) {
+    State = adjustIteratorPosition(State, LVal, *RPos, Equal);
+  } else if (LPos && RPos) {
+    if (contradictingIteratorPositions(*LPos, *RPos, Equal)) {
+      return nullptr;
+    }
+  }
+  return State;
+}
+
+const ProgramStateRef saveComparison(ProgramStateRef State,
+                                     const SymExpr *Condition, const SVal &LVal,
+                                     const SVal &RVal, bool Eq) {
+  const auto Left = getRegionOrSymbol(LVal);
+  const auto Right = getRegionOrSymbol(RVal);
+  if (!Left || !Right)
+    return State;
+  return State->set<IteratorComparisonMap>(Condition,
+                                           IteratorComparison(Left, Right, Eq));
+}
+
+const IteratorComparison *loadComparison(ProgramStateRef State,
+                                         const SymExpr *Condition) {
+  return State->get<IteratorComparisonMap>(Condition);
+}
+
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+                                            const SVal &Val) {
+  if (const auto Reg = Val.getAsRegion()) {
+    return State->get<IteratorRegionMap>(Reg);
+  } else if (const auto Sym = Val.getAsSymbol()) {
+    return State->get<IteratorSymbolMap>(Sym);
+  } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+    return State->get<IteratorRegionMap>(LCVal->getRegion());
+  }
+  return nullptr;
+}
+
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+                                            RegionOrSymbol RegOrSym) {
+  if (RegOrSym.is<const MemRegion *>()) {
+    return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
+  } else if (RegOrSym.is<SymbolRef>()) {
+    return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
+  }
+  return nullptr;
+}
+
+ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
+                                    IteratorPosition Pos) {
+  if (const auto Reg = Val.getAsRegion()) {
+    return State->set<IteratorRegionMap>(Reg, Pos);
+  } else if (const auto Sym = Val.getAsSymbol()) {
+    return State->set<IteratorSymbolMap>(Sym, Pos);
+  } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+    return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
+  }
+  return nullptr;
+}
+
+ProgramStateRef setIteratorPosition(ProgramStateRef State,
+                                    RegionOrSymbol RegOrSym,
+                                    IteratorPosition Pos) {
+  if (RegOrSym.is<const MemRegion *>()) {
+    return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
+                                         Pos);
+  } else if (RegOrSym.is<SymbolRef>()) {
+    return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
+  }
+  return nullptr;
+}
+
+ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
+                                       RegionOrSymbol RegOrSym,
+                                       IteratorPosition Pos, bool Equal) {
+
+  if ((Pos.isInRange() && Equal) || (Pos.isOutofRange() && !Equal)) {
+    return setIteratorPosition(State, RegOrSym, IteratorPosition::getInRange());
+  } else if (Pos.isOutofRange() && Equal) {
+    return setIteratorPosition(State, RegOrSym,
+                               IteratorPosition::getOutofRange());
+  } else {
+    return State;
+  }
+}
+
+bool contradictingIteratorPositions(IteratorPosition Pos1,
+                                    IteratorPosition Pos2, bool Equal) {
+  return ((Pos1 != Pos2) && Equal) ||
+         ((Pos1.isOutofRange() && Pos2.isOutofRange()) && !Equal);
+}
+}
+
+void ento::registerIteratorPastEndChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<IteratorPastEndChecker>();
+}
index 707168b4de0ab012381becd5db775280959b2f99..7e7e329dc4d78c080d43498c565f060a834e8912 100644 (file)
@@ -1248,7 +1248,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Expr::MaterializeTemporaryExprClass: {
       Bldr.takeNodes(Pred);
       const MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S);
-      CreateCXXTemporaryObject(MTE, Pred, Dst);
+      ExplodedNodeSet dstPrevisit;
+      getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this);
+      ExplodedNodeSet dstExpr;
+      for (ExplodedNodeSet::iterator i = dstPrevisit.begin(),
+                                     e = dstPrevisit.end(); i != e ; ++i) {
+        CreateCXXTemporaryObject(MTE, *i, dstExpr);
+      }
+      getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this);
       Bldr.addNodes(Dst);
       break;
     }
index 04f1000dbc3fd1ed2b62a735344b2edb7c3b8733..005e7f57af6fe9a864c5897ff8b795b0cda5ba74 100644 (file)
@@ -10,6 +10,29 @@ typedef unsigned char uint8_t;
 typedef __typeof__(sizeof(int)) size_t;
 void *memmove(void *s1, const void *s2, size_t n);
 
+template <typename T, typename Ptr, typename Ref> struct __iterator {
+  typedef __iterator<T, T *, T &> iterator;
+  typedef __iterator<T, const T *, const T &> const_iterator;
+
+  __iterator(const Ptr p) : ptr(p) {}
+
+  __iterator<T, Ptr, Ref> operator++() { return *this; }
+  __iterator<T, Ptr, Ref> operator++(int) { return *this; }
+  __iterator<T, Ptr, Ref> operator--() { return *this; }
+  __iterator<T, Ptr, Ref> operator--(int) { return *this; }
+  Ref operator*() const { return *ptr; }
+  Ptr operator->() const { return *ptr; }
+
+  bool operator==(const iterator &rhs) const { return ptr == rhs.ptr; }
+  bool operator==(const const_iterator &rhs) const { return ptr == rhs.ptr; }
+
+  bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; }
+  bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; }
+
+private:
+  Ptr ptr;
+};
+
 namespace std {
   template <class T1, class T2>
   struct pair {
@@ -27,6 +50,9 @@ namespace std {
   
   template<typename T>
   class vector {
+    typedef __iterator<T, T *, T &> iterator;
+    typedef __iterator<T, const T *, const T &> const_iterator;
+
     T *_start;
     T *_finish;
     T *_end_of_storage;
@@ -49,11 +75,10 @@ namespace std {
       return _start[n];
     }
     
-    T *begin() { return _start; }
-    const T *begin() const { return _start; }
-
-    T *end() { return _finish; }
-    const T *end() const { return _finish; }
+    iterator begin() { return iterator(_start); }
+    const_iterator begin() const { return const_iterator(_start); }
+    iterator end() { return iterator(_finish); }
+    const_iterator end() const { return const_iterator(_finish); }
   };
   
   class exception {
@@ -223,6 +248,35 @@ namespace std {
     return __copy_backward(II, IE, OI);
   }
 
+  template <class InputIterator, class T>
+  InputIterator find(InputIterator first, InputIterator last, const T &val);
+  template <class ForwardIterator1, class ForwardIterator2>
+  ForwardIterator1 find_end(ForwardIterator1 first1, ForwardIterator1 last1,
+                            ForwardIterator2 first2, ForwardIterator2 last2);
+  template <class ForwardIterator1, class ForwardIterator2>
+  ForwardIterator1 find_first_of(ForwardIterator1 first1,
+                                 ForwardIterator1 last1,
+                                 ForwardIterator2 first2,
+                                 ForwardIterator2 last2);
+  template <class InputIterator, class UnaryPredicate>
+  InputIterator find_if(InputIterator first, InputIterator last,
+                        UnaryPredicate pred);
+  template <class InputIterator, class UnaryPredicate>
+  InputIterator find_if_not(InputIterator first, InputIterator last,
+                            UnaryPredicate pred);
+  template <class InputIterator, class T>
+  InputIterator lower_bound(InputIterator first, InputIterator last,
+                            const T &val);
+  template <class InputIterator, class T>
+  InputIterator upper_bound(InputIterator first, InputIterator last,
+                            const T &val);
+  template <class ForwardIterator1, class ForwardIterator2>
+  ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1,
+                          ForwardIterator2 first2, ForwardIterator2 last2);
+  template <class ForwardIterator1, class ForwardIterator2>
+  ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1,
+                            ForwardIterator2 first2, ForwardIterator2 last2);
+
   struct input_iterator_tag { };
   struct output_iterator_tag { };
   struct forward_iterator_tag : public input_iterator_tag { };
index 67a47d02b6e9541d01591158c19577590009ede6..d36def20f25fd6244db4cc8d90405fd871905930 100644 (file)
@@ -18,6 +18,6 @@ class C {
 void testCopyNull(C *I, C *E) {
   std::copy(I, E, (C *)0);
 #ifndef SUPPRESSED
-  // expected-warning@../Inputs/system-header-simulator-cxx.h:166 {{Called C++ object pointer is null}}
+  // expected-warning@../Inputs/system-header-simulator-cxx.h:191 {{Called C++ object pointer is null}}
 #endif
 }
index 95ac3f8172dbdf6f046ab82c4ded7bcce5c2fae8..d89a10983041ca8eea8a3738e17869ade0b979a1 100644 (file)
@@ -6,8 +6,7 @@
 void clang_analyzer_eval(bool);
 
 void testVector(std::vector<int> &nums) {
-  if (nums.begin()) return;
-  if (nums.end()) return;
+  if (nums.begin() != nums.end()) return;
   
   clang_analyzer_eval(nums.size() == 0);
 #if INLINE
diff --git a/test/Analysis/iterator-past-end.cpp b/test/Analysis/iterator-past-end.cpp
new file mode 100644 (file)
index 0000000..4d9ed0c
--- /dev/null
@@ -0,0 +1,205 @@
+// RUN: %clang_cc1 -std=c++11 -analyze -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=false %s -verify
+// RUN: %clang_cc1 -std=c++11 -analyze -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void simple_good(const std::vector<int> &v) {
+  auto i = v.end();
+  if (i != v.end())
+    *i; // no-warning
+}
+
+void simple_good_negated(const std::vector<int> &v) {
+  auto i = v.end();
+  if (!(i == v.end()))
+    *i; // no-warning
+}
+
+void simple_bad(const std::vector<int> &v) {
+  auto i = v.end();
+  *i; // expected-warning{{Iterator accessed past its end}}
+}
+
+void copy(const std::vector<int> &v) {
+  auto i1 = v.end();
+  auto i2 = i1;
+  *i2; // expected-warning{{Iterator accessed past its end}}
+}
+
+void decrease(const std::vector<int> &v) {
+  auto i = v.end();
+  --i;
+  *i; // no-warning
+}
+
+void copy_and_decrease1(const std::vector<int> &v) {
+  auto i1 = v.end();
+  auto i2 = i1;
+  --i1;
+  *i1; // no-warning
+}
+
+void copy_and_decrease2(const std::vector<int> &v) {
+  auto i1 = v.end();
+  auto i2 = i1;
+  --i1;
+  *i2; // expected-warning{{Iterator accessed past its end}}
+}
+
+void copy_and_increase1(const std::vector<int> &v) {
+  auto i1 = v.begin();
+  auto i2 = i1;
+  ++i1;
+  if (i1 == v.end())
+    *i2; // no-warning
+}
+
+void copy_and_increase2(const std::vector<int> &v) {
+  auto i1 = v.begin();
+  auto i2 = i1;
+  ++i1;
+  if (i2 == v.end())
+    *i2; // expected-warning{{Iterator accessed past its end}}
+}
+
+void good_find(std::vector<int> &vec, int e) {
+  auto first = std::find(vec.begin(), vec.end(), e);
+  if (vec.end() != first)
+    *first; // no-warning
+}
+
+void bad_find(std::vector<int> &vec, int e) {
+  auto first = std::find(vec.begin(), vec.end(), e);
+  *first; // expected-warning{{Iterator accessed past its end}}
+}
+
+void good_find_end(std::vector<int> &vec, std::vector<int> &seq) {
+  auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
+  if (vec.end() != last)
+    *last; // no-warning
+}
+
+void bad_find_end(std::vector<int> &vec, std::vector<int> &seq) {
+  auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
+  *last; // expected-warning{{Iterator accessed past its end}}
+}
+
+void good_find_first_of(std::vector<int> &vec, std::vector<int> &seq) {
+  auto first =
+      std::find_first_of(vec.begin(), vec.end(), seq.begin(), seq.end());
+  if (vec.end() != first)
+    *first; // no-warning
+}
+
+void bad_find_first_of(std::vector<int> &vec, std::vector<int> &seq) {
+  auto first = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end());
+  *first; // expected-warning{{Iterator accessed past its end}}
+}
+
+bool odd(int i) { return i % 2; }
+
+void good_find_if(std::vector<int> &vec) {
+  auto first = std::find_if(vec.begin(), vec.end(), odd);
+  if (vec.end() != first)
+    *first; // no-warning
+}
+
+void bad_find_if(std::vector<int> &vec, int e) {
+  auto first = std::find_if(vec.begin(), vec.end(), odd);
+  *first; // expected-warning{{Iterator accessed past its end}}
+}
+
+void good_find_if_not(std::vector<int> &vec) {
+  auto first = std::find_if_not(vec.begin(), vec.end(), odd);
+  if (vec.end() != first)
+    *first; // no-warning
+}
+
+void bad_find_if_not(std::vector<int> &vec, int e) {
+  auto first = std::find_if_not(vec.begin(), vec.end(), odd);
+  *first; // expected-warning{{Iterator accessed past its end}}
+}
+
+void good_lower_bound(std::vector<int> &vec, int e) {
+  auto first = std::lower_bound(vec.begin(), vec.end(), e);
+  if (vec.end() != first)
+    *first; // no-warning
+}
+
+void bad_lower_bound(std::vector<int> &vec, int e) {
+  auto first = std::lower_bound(vec.begin(), vec.end(), e);
+  *first; // expected-warning{{Iterator accessed past its end}}
+}
+
+void good_upper_bound(std::vector<int> &vec, int e) {
+  auto last = std::lower_bound(vec.begin(), vec.end(), e);
+  if (vec.end() != last)
+    *last; // no-warning
+}
+
+void bad_upper_bound(std::vector<int> &vec, int e) {
+  auto last = std::lower_bound(vec.begin(), vec.end(), e);
+  *last; // expected-warning{{Iterator accessed past its end}}
+}
+
+void good_search(std::vector<int> &vec, std::vector<int> &seq) {
+  auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end());
+  if (vec.end() != first)
+    *first; // no-warning
+}
+
+void bad_search(std::vector<int> &vec, std::vector<int> &seq) {
+  auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end());
+  *first; // expected-warning{{Iterator accessed past its end}}
+}
+
+void good_search_n(std::vector<int> &vec, std::vector<int> &seq) {
+  auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end());
+  if (vec.end() != nth)
+    *nth; // no-warning
+}
+
+void bad_search_n(std::vector<int> &vec, std::vector<int> &seq) {
+  auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end());
+  *nth; // expected-warning{{Iterator accessed past its end}}
+}
+
+template <class InputIterator, class T>
+InputIterator nonStdFind(InputIterator first, InputIterator last,
+                         const T &val) {
+  for (auto i = first; i != last; ++i) {
+    if (*i == val) {
+      return i;
+    }
+  }
+  return last;
+}
+
+void good_non_std_find(std::vector<int> &vec, int e) {
+  auto first = nonStdFind(vec.begin(), vec.end(), e);
+  if (vec.end() != first)
+    *first; // no-warning
+}
+
+void bad_non_std_find(std::vector<int> &vec, int e) {
+  auto first = nonStdFind(vec.begin(), vec.end(), e);
+  *first; // expected-warning{{Iterator accessed past its end}}
+}
+
+void tricky(std::vector<int> &vec, int e) {
+  const auto first = vec.begin();
+  const auto comp1 = (first != vec.end()), comp2 = (first == vec.end());
+  if (comp1)
+    *first;
+}
+
+void loop(std::vector<int> &vec, int e) {
+  auto start = vec.begin();
+  while (true) {
+    auto item = std::find(start, vec.end(), e);
+    if (item == vec.end())
+      break;
+    *item;          // no-warning
+    start = ++item; // no-warning
+  }
+}