[clang] 0f88cae - [Analyzer] Checker for Debugging Iterator Checkers

Adam Balogh via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 7 23:59:08 PST 2019


Author: Adam Balogh
Date: 2019-11-08T08:59:50+01:00
New Revision: 0f88caeef8f2d4708f442d03db7723396712a143

URL: https://github.com/llvm/llvm-project/commit/0f88caeef8f2d4708f442d03db7723396712a143
DIFF: https://github.com/llvm/llvm-project/commit/0f88caeef8f2d4708f442d03db7723396712a143.diff

LOG: [Analyzer] Checker for Debugging Iterator Checkers

For white-box testing correct container and iterator modelling it is essential
to access the internal data structures stored for container and iterators. This
patch introduces a simple debug checkers called debug.IteratorDebugging to
achieve this.

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

Added: 
    clang/test/Analysis/debug-iterator-modeling.cpp

Modified: 
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 4d52655045b3..be455ae14f57 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1343,6 +1343,11 @@ def ReportStmts : Checker<"ReportStmts">,
   HelpText<"Emits a warning for every statement.">,
   Documentation<NotDocumented>;
 
+def DebugIteratorModeling : Checker<"DebugIteratorModeling">,
+  HelpText<"Check the analyzer's understanding of C++ iterators">,
+  Dependencies<[IteratorModeling]>,
+  Documentation<NotDocumented>;
+
 } // end "debug"
 
 

diff  --git a/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
index 97ace68569ef..a84327f07a52 100644
--- a/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -173,11 +173,12 @@ struct ContainerData {
 class IteratorChecker
     : public Checker<check::PreCall, check::PostCall,
                      check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
-                     check::LiveSymbols, check::DeadSymbols> {
+                     check::LiveSymbols, check::DeadSymbols, eval::Call> {
 
   std::unique_ptr<BugType> OutOfRangeBugType;
   std::unique_ptr<BugType> MismatchedBugType;
   std::unique_ptr<BugType> InvalidatedBugType;
+  std::unique_ptr<BugType> DebugMsgBugType;
 
   void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
                         const SVal &LVal, const SVal &RVal,
@@ -236,7 +237,35 @@ class IteratorChecker
                            ExplodedNode *ErrNode) const;
   void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
                             CheckerContext &C, ExplodedNode *ErrNode) const;
-
+  template <typename Getter>
+  void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
+                                  Getter get) const;
+  void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
+  template <typename Getter>
+  void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
+                                 Getter get, SVal Default) const;
+  void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
+  ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
+
+  typedef void (IteratorChecker::*FnCheck)(const CallExpr *,
+                                           CheckerContext &) const;
+
+  CallDescriptionMap<FnCheck> Callbacks = {
+    {{0, "clang_analyzer_container_begin", 1},
+     &IteratorChecker::analyzerContainerBegin},
+    {{0, "clang_analyzer_container_end", 1},
+     &IteratorChecker::analyzerContainerEnd},
+    {{0, "clang_analyzer_iterator_position", 1},
+     &IteratorChecker::analyzerIteratorPosition},
+    {{0, "clang_analyzer_iterator_container", 1},
+     &IteratorChecker::analyzerIteratorContainer},
+    {{0, "clang_analyzer_iterator_validity", 1},
+     &IteratorChecker::analyzerIteratorValidity},
+  };
+  
 public:
   IteratorChecker();
 
@@ -244,6 +273,7 @@ class IteratorChecker
     CK_IteratorRangeChecker,
     CK_MismatchedIteratorChecker,
     CK_InvalidatedIteratorChecker,
+    CK_DebugIteratorModeling,
     CK_NumCheckKinds
   };
 
@@ -259,6 +289,7 @@ class IteratorChecker
                      CheckerContext &C) const;
   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
 };
 } // namespace
 
@@ -362,6 +393,9 @@ IteratorChecker::IteratorChecker() {
                   /*SuppressOnSink=*/true));
   InvalidatedBugType.reset(
       new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
+  DebugMsgBugType.reset(
+      new BugType(this, "Checking analyzer assumptions", "debug",
+                  /*SuppressOnSink=*/true));
 }
 
 void IteratorChecker::checkPreCall(const CallEvent &Call,
@@ -1627,6 +1661,124 @@ void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
   C.emitReport(std::move(R));
 }
 
+bool IteratorChecker::evalCall(const CallEvent &Call,
+                                     CheckerContext &C) const {
+  if (!ChecksEnabled[CK_DebugIteratorModeling])
+    return false;
+
+  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+  if (!CE)
+    return false;
+
+  const FnCheck *Handler = Callbacks.lookup(Call);
+  if (!Handler)
+    return false;
+
+  (this->**Handler)(CE, C);
+  return true;
+}
+
+template <typename Getter>
+void IteratorChecker::analyzerContainerDataField(const CallExpr *CE,
+                                                 CheckerContext &C,
+                                                 Getter get) const {
+  if (CE->getNumArgs() == 0) {
+    reportDebugMsg("Missing container argument", C);
+    return;
+  }
+
+  auto State = C.getState();
+  const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
+  if (Cont) {
+    const auto *Data = getContainerData(State, Cont);
+    if (Data) {
+      SymbolRef Field = get(Data);
+      if (Field) {
+        State = State->BindExpr(CE, C.getLocationContext(),
+                                nonloc::SymbolVal(Field));
+        C.addTransition(State);
+        return;
+      }
+    }
+  }
+
+  auto &BVF = C.getSValBuilder().getBasicValueFactory();
+  State = State->BindExpr(CE, C.getLocationContext(),
+                   nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+void IteratorChecker::analyzerContainerBegin(const CallExpr *CE,
+                                             CheckerContext &C) const {
+  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
+      return D->getBegin();
+    });
+}
+
+void IteratorChecker::analyzerContainerEnd(const CallExpr *CE,
+                                             CheckerContext &C) const {
+  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
+      return D->getEnd();
+    });
+}
+
+template <typename Getter>
+void IteratorChecker::analyzerIteratorDataField(const CallExpr *CE,
+                                                CheckerContext &C,
+                                                Getter get,
+                                                SVal Default) const {
+  if (CE->getNumArgs() == 0) {
+    reportDebugMsg("Missing iterator argument", C);
+    return;
+  }
+
+  auto State = C.getState();
+  SVal V = C.getSVal(CE->getArg(0));
+  const auto *Pos = getIteratorPosition(State, V);
+  if (Pos) {
+    State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
+  } else {
+    State = State->BindExpr(CE, C.getLocationContext(), Default);
+  }
+  C.addTransition(State);
+}
+
+void IteratorChecker::analyzerIteratorPosition(const CallExpr *CE,
+                                               CheckerContext &C) const {
+  auto &BVF = C.getSValBuilder().getBasicValueFactory();
+  analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
+      return nonloc::SymbolVal(P->getOffset());
+    }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+void IteratorChecker::analyzerIteratorContainer(const CallExpr *CE,
+                                               CheckerContext &C) const {
+  auto &BVF = C.getSValBuilder().getBasicValueFactory();
+  analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
+      return loc::MemRegionVal(P->getContainer());
+    }, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+void IteratorChecker::analyzerIteratorValidity(const CallExpr *CE,
+                                               CheckerContext &C) const {
+  auto &BVF = C.getSValBuilder().getBasicValueFactory();
+  analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
+      return
+        nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
+    }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+ExplodedNode *IteratorChecker::reportDebugMsg(llvm::StringRef Msg,
+                                              CheckerContext &C) const {
+  ExplodedNode *N = C.generateNonFatalErrorNode();
+  if (!N)
+    return nullptr;
+
+  auto &BR = C.getBugReporter();
+  BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
+                                                         Msg, N));
+  return N;
+}
+
 namespace {
 
 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
@@ -2388,3 +2540,4 @@ bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
 REGISTER_CHECKER(IteratorRangeChecker)
 REGISTER_CHECKER(MismatchedIteratorChecker)
 REGISTER_CHECKER(InvalidatedIteratorChecker)
+REGISTER_CHECKER(DebugIteratorModeling)

diff  --git a/clang/test/Analysis/debug-iterator-modeling.cpp b/clang/test/Analysis/debug-iterator-modeling.cpp
new file mode 100644
index 000000000000..00816c80c661
--- /dev/null
+++ b/clang/test/Analysis/debug-iterator-modeling.cpp
@@ -0,0 +1,61 @@
+// RUN: %clang_analyze_cc1 -std=c++11\
+// RUN: -analyzer-checker=core,cplusplus\
+// RUN: -analyzer-checker=debug.DebugIteratorModeling,debug.ExprInspection\
+// RUN: -analyzer-config aggressive-binary-operation-simplification=true\
+// RUN: -analyzer-config c++-container-inlining=false %s -verify
+
+// RUN: %clang_analyze_cc1 -std=c++11\
+// RUN: -analyzer-checker=core,cplusplus\
+// RUN: -analyzer-checker=debug.DebugIteratorModeling,debug.ExprInspection\
+// RUN: -analyzer-config aggressive-binary-operation-simplification=true\
+// RUN: -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+template <typename Container>
+long clang_analyzer_container_begin(const Container&);
+template <typename Container>
+long clang_analyzer_container_end(const Container&);
+template <typename Iterator>
+long clang_analyzer_iterator_position(const Iterator&);
+template <typename Iterator>
+void* clang_analyzer_iterator_container(const Iterator&);
+template <typename Iterator>
+bool clang_analyzer_iterator_validity(const Iterator&);
+void clang_analyzer_denote(long, const char*);
+void clang_analyzer_express(long);
+void clang_analyzer_dump(const void*);
+void clang_analyzer_eval(bool);
+
+void iterator_position(const std::vector<int> v0) {
+  auto b0 = v0.begin(), e0 = v0.end();
+
+  clang_analyzer_denote(clang_analyzer_iterator_position(b0), "$b0");
+  clang_analyzer_denote(clang_analyzer_iterator_position(e0), "$e0");
+
+  clang_analyzer_express(clang_analyzer_iterator_position(b0)); // expected-warning{{$b0}}
+  clang_analyzer_express(clang_analyzer_iterator_position(e0)); // expected-warning{{$e0}}
+
+  clang_analyzer_express(clang_analyzer_container_begin(v0)); // expected-warning{{$b0}}
+  clang_analyzer_express(clang_analyzer_container_end(v0)); // expected-warning{{$e0}}
+
+  ++b0;
+
+  clang_analyzer_express(clang_analyzer_iterator_position(b0)); // expected-warning{{$b0 + 1}}
+}
+
+void iterator_container(const std::vector<int> v0) {
+  auto b0 = v0.begin();
+
+  clang_analyzer_dump(&v0); //expected-warning{{&v0}}
+  clang_analyzer_eval(clang_analyzer_iterator_container(b0) == &v0); // expected-warning{{TRUE}}
+}
+
+void iterator_validity(std::vector<int> v0) {
+  auto b0 = v0.begin();
+  clang_analyzer_eval(clang_analyzer_iterator_validity(b0)); //expected-warning{{TRUE}}
+
+  v0.clear();
+
+  clang_analyzer_eval(clang_analyzer_iterator_validity(b0)); //expected-warning{{FALSE}}
+}


        


More information about the cfe-commits mailing list