From 36afabc82abd1a109efc55b97ea9db14f11d3b70 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 8 Apr 2019 17:23:39 -0400
Subject: [PATCH 181/217] FIXME: initial WIP on impl 3

---
 gcc/analyzer/Make-lang.in     |   1 +
 gcc/analyzer/analyzer-pass.cc |  11 +-
 gcc/analyzer/engine.cc        |   2 +-
 gcc/analyzer/engine.h         |   3 +-
 gcc/analyzer/impl-3.cc        | 596 ++++++++++++++++++++++++++++++++++++++++++
 gcc/analyzer/supergraph.cc    |  10 +
 gcc/analyzer/supergraph.h     |   2 +
 gcc/common.opt                |   4 +
 8 files changed, 626 insertions(+), 3 deletions(-)
 create mode 100644 gcc/analyzer/impl-3.cc

diff --git a/gcc/analyzer/Make-lang.in b/gcc/analyzer/Make-lang.in
index b7c738d..95c14d8 100644
--- a/gcc/analyzer/Make-lang.in
+++ b/gcc/analyzer/Make-lang.in
@@ -68,6 +68,7 @@ ANALYZER_PLUGIN_SO_OBJS = \
 	analyzer/hardcoded-state-machine-pattern-test.o \
 	analyzer/hardcoded-state-machine-sensitive.o \
 	analyzer/hardcoded-state-machine-taint.o \
+	analyzer/impl-3.o \
 	analyzer/supergraph.o \
 	analyzer/tristate.o \
 
diff --git a/gcc/analyzer/analyzer-pass.cc b/gcc/analyzer/analyzer-pass.cc
index cc60e5f..8417ef1 100644
--- a/gcc/analyzer/analyzer-pass.cc
+++ b/gcc/analyzer/analyzer-pass.cc
@@ -82,7 +82,16 @@ public:
 unsigned int
 pass_analyzer::execute (function *)
 {
-  run_checkers ();
+  switch (flag_analyzer_impl)
+    {
+    default:
+    case 2:
+      run_checkers_impl_2 ();
+      break;
+    case 3:
+      run_checkers_impl_3 ();
+      break;
+    }
   return 0;
 }
 
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 2ccb48d..c4a2cb7 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -3811,7 +3811,7 @@ run_one_checker (const supergraph &sg, const state_machine &sm, logger *logger)
 /* FIXME.  */
 
 void
-run_checkers ()
+run_checkers_impl_2 ()
 {
   //FILE *dump = fopen ("test.out", "w"); // FIXME
   auto_client_timevar tv ("run_checkers");
diff --git a/gcc/analyzer/engine.h b/gcc/analyzer/engine.h
index 6e7d60b..8f0b1f4 100644
--- a/gcc/analyzer/engine.h
+++ b/gcc/analyzer/engine.h
@@ -1 +1,2 @@
-extern void run_checkers ();
+extern void run_checkers_impl_2 ();
+extern void run_checkers_impl_3 ();
diff --git a/gcc/analyzer/impl-3.cc b/gcc/analyzer/impl-3.cc
new file mode 100644
index 0000000..0c26e6f
--- /dev/null
+++ b/gcc/analyzer/impl-3.cc
@@ -0,0 +1,596 @@
+/* FIXME.  */
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "toplev.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "context.h"
+#include "diagnostic.h"
+#include "bitmap.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-pretty-print.h"
+#include "tree-pretty-print.h"
+#include "analyzer.h"
+#include "graphviz.h"
+#include "cgraph.h"
+#include "diagnostic-path.h"
+#include "gcc-rich-location.h"
+#include "tree-dfa.h"
+#include <string>
+#include "supergraph.h"
+#include "hardcoded-state-machine.h"
+#include "analyzer-logging.h"
+#include "stringpool.h"
+#include "selftest.h"
+#include "fold-const.h"
+#include "tristate.h"
+#include "selftest.h"
+#include "exploded-graph.h"
+#include "constraints.h"
+
+namespace impl_3 {
+
+/* FIXME.  */
+
+class checker3_event : public simple_diagnostic_event //diagnostic_event
+{
+public:
+  checker3_event (location_t loc, tree fndecl, int depth, const char *desc)
+  : simple_diagnostic_event (loc, fndecl, depth, desc)
+  {
+  }
+  // FIXME: for now
+
+  checker3_event *clone () const
+  {
+    label_text desc = get_desc ();
+    checker3_event *result
+      = new checker3_event (get_location (), get_fndecl (),
+			    get_stack_depth (), desc.m_buffer);
+    // TODO: make virtual (if we add subclasses)
+    desc.maybe_free ();
+    return result;
+  }
+
+  std::string to_str () const
+  {
+    std::string result ("\"");
+    label_text event_desc (get_desc ());
+    result += event_desc.m_buffer;
+    event_desc.maybe_free ();
+    result += "\" (depth ";
+    result += std::to_string (get_stack_depth ());
+    result += ", m_loc=";
+    result += std::to_string (get_location ());
+    result += ")";
+    return result;
+  }
+};
+
+/* FIXME.  */
+
+class checker3_path : public diagnostic_path
+{
+public:
+  struct link
+  {
+    link (const superedge *edge, const supernode *node)
+    : m_edge (edge), m_node (node)
+    {}
+
+    const superedge *m_edge;
+    const supernode *m_node;
+  };
+
+  checker3_path (const supergraph &sg) : diagnostic_path (), m_sg (sg), m_links (), m_active_calls (), m_ps () {}
+  checker3_path (const checker3_path &other);
+  checker3_path& operator= (const checker3_path &other);
+
+  std::string to_str () const
+  {
+    std::string result ("[");
+
+    checker3_event *e;
+    int i;
+    FOR_EACH_VEC_ELT (m_events, i, e)
+      {
+	if (i > 0)
+	  result += ", ";
+	label_text event_desc (e->get_desc ());
+	result += std::string ("\"") + event_desc.m_buffer + std::string ("\"");
+	event_desc.maybe_free ();
+      }
+    result += "]";
+    {
+      result += ", m_links=[";
+      link *link;
+      FOR_EACH_VEC_ELT (m_links, i, link)
+	{
+	  if (i > 0)
+	    result += ", ";
+	  result += std::to_string (link->m_node->m_index);
+	}
+      result += "]";
+    }
+    {
+      result += ", m_active_calls=[";
+      cgraph_edge *edge;
+      FOR_EACH_VEC_ELT (m_active_calls, i, edge)
+	{
+	  if (i > 0)
+	    result += ", ";
+	  result += m_sg.get_edge_for_call (edge)->to_str ();
+	}
+      result += "]";
+    }
+    result += ", m_ps=" + m_ps.to_str ();
+    return result;
+  }
+
+  void maybe_log (logger *logger, const char *desc) const
+  {
+    if (!logger)
+      return;
+    logger->log ("%s: %s", desc, to_str ().c_str ());
+    for (unsigned i = 0; i < m_events.length (); i++)
+      logger->log ("%s[%i]: %s",
+		   desc, i, m_events[i]->to_str ().c_str ());
+  }
+
+  unsigned num_events () const FINAL OVERRIDE
+  {
+    return m_events.length ();
+  }
+
+  const diagnostic_event & get_event (int idx) const FINAL OVERRIDE
+  {
+    return *m_events[idx];
+  }
+
+  const supernode *get_end_node () const
+  {
+    gcc_assert (m_links.length () > 0);
+    return m_links[m_links.length () - 1].m_node;
+  }
+
+  /* Get the SUPEREDGE_RETURN corresponding to the most recent
+     unreturned SUPEREDGE_CALL.  Return NULL if there isn't one.  */
+  const superedge *get_next_return_edge (const supergraph &sg) const
+  {
+    if (m_active_calls.length () == 0)
+      return NULL;
+    cgraph_edge *cedge = m_active_calls[m_active_calls.length () - 1];
+    return sg.get_edge_for_return (cedge);
+  }
+
+  /* Record that we've reached NODE, via EDGE.  EDGE may be NULL,
+     to cover entrypoints to the analysis.
+     Update m_active_calls to reflect call and return edges.  */
+  void add_link (const superedge *edge, const supernode *node)
+  {
+    m_links.safe_push (link (edge, node));
+    if (edge)
+      {
+	switch (edge->m_kind)
+	  {
+	  default:
+	    break;
+	  case SUPEREDGE_CALL:
+	    {
+	      cgraph_edge *cedge = edge->get_callgraph_edge ();
+	      gcc_assert (cedge);
+	      m_active_calls.safe_push (cedge);
+	    }
+	    break;
+	  case SUPEREDGE_RETURN:
+	    {
+	      cgraph_edge *top_cedge = m_active_calls.pop ();
+	      cgraph_edge *edge_cedge = edge->get_callgraph_edge ();
+	      gcc_assert (edge_cedge == top_cedge);
+	    }
+	    break;
+	  }
+      }
+  }
+
+  ////////////////////////////////////////////////////////////////////////////
+
+  const supergraph &m_sg;
+
+  /* The events that have occurred along this path.  */
+  auto_delete_vec<checker3_event> m_events;
+
+  /* A trace of nodes and edges that the path has followed.  */
+  auto_vec <link> m_links;
+
+  /* The outstanding calls, so that we can find where we should
+     return to.  */
+  auto_vec <cgraph_edge *> m_active_calls;
+
+  /* The current state of memory.  */
+  path_state m_ps;
+
+  // TODO: add map from EC to state (or subclass path_state?)
+
+  /* TODO: add additional things, like edge counts, etc
+     (as an optimization; these could be derived by walking
+     the events).  */
+};
+
+checker3_path::checker3_path (const checker3_path &other)
+: m_sg (other.m_sg),
+  m_links (),
+  m_ps (other.m_ps)
+{
+  unsigned i;
+  {
+    link *l;
+    FOR_EACH_VEC_ELT (other.m_links, i, l)
+      m_links.safe_push (*l);
+  }
+  {
+    cgraph_edge *edge;
+    FOR_EACH_VEC_ELT (other.m_active_calls, i, edge)
+      m_active_calls.safe_push (edge);
+  }
+  {
+    checker3_event *e;
+    FOR_EACH_VEC_ELT (other.m_events, i, e)
+      m_events.safe_push (e->clone ());
+  }
+  // FIXME: quick push for these?
+}
+
+checker3_path&
+checker3_path::operator= (const checker3_path &other)
+{
+  gcc_assert (&m_sg == &other.m_sg);
+  m_ps = other.m_ps;
+
+  unsigned i;
+  {
+    link *l;
+    FOR_EACH_VEC_ELT (other.m_links, i, l)
+      m_links.safe_push (*l);
+  }
+  {
+    cgraph_edge *edge;
+    FOR_EACH_VEC_ELT (other.m_active_calls, i, edge)
+      m_active_calls.safe_push (edge);
+  }
+  {
+    checker3_event *e;
+    FOR_EACH_VEC_ELT (other.m_events, i, e)
+      m_events.safe_push (e->clone ());
+  }
+  return *this;
+}
+
+/* FIXME.  */
+
+class engine : public log_user
+{
+public:
+  engine (const supergraph &sg, const state_machine &sm,
+	  logger *logger, int verbosity)
+  : log_user (logger), m_sg (sg), m_sm (sm),
+    m_verbosity (verbosity),
+    m_num_traverse_calls (0),
+    m_on_stmt_calls (0)
+  {
+  }
+
+  void traverse (checker3_path path);
+  void log_stats () const;
+
+  const supergraph &m_sg;
+
+  // FIXME: this implies one traversal per state machine; should we instead
+  // do multiple state machines in one traversal?
+  const state_machine &m_sm;
+
+  int m_verbosity;
+
+  int m_num_traverse_calls;
+  int m_on_stmt_calls;
+};
+
+/* FIXME.  */
+
+void
+engine::traverse (checker3_path path)
+{
+  const supernode *node = path.get_end_node ();
+  JIT_LOG_FUNC_1 (get_logger (), "SN: %i", node->m_index);
+  m_num_traverse_calls++;
+
+  if (get_logger ())
+    log ("path: %s", path.to_str ().c_str ());
+
+  // TODO
+
+  /* Phi nodes.  */
+  // FIXME: do these have to somehow happen simultaneously?
+  for (gphi_iterator gpi = const_cast<supernode *>(node)->start_phis ();
+       !gsi_end_p (gpi); gsi_next (&gpi))
+    {
+      gphi *phi = gpi.phi ();
+      // TODO
+    }
+
+  /* Stmts.  */
+  int i;
+  gimple *stmt;
+  FOR_EACH_VEC_ELT (node->m_stmts, i, stmt)
+    {
+      input_location = stmt->location;
+      // TODO
+      m_on_stmt_calls++;
+    }
+
+  /* Traverse into successors of NODE.  */
+  superedge *succ;
+  FOR_EACH_VEC_ELT (node->m_succs, i, succ)
+    {
+#if 1
+      log ("considering SN: %i -> SN: %i",
+	   node->m_index, succ->m_dest->m_index);
+#endif
+
+     switch (succ->m_kind)
+	{
+	default:
+	  gcc_unreachable ();
+	case SUPEREDGE_CFG_EDGE:
+	case SUPEREDGE_CALL:
+	  break;
+	case SUPEREDGE_RETURN:
+	  /* Only follow the correct return edge.  */
+	  if (succ != path.get_next_return_edge (m_sg))
+	    {
+	      log ("rejecting return edge");
+	      continue;
+	    }
+	  break;
+	case SUPEREDGE_INTRAPROCEDURAL_CALL:
+	  {
+	    /* Don't traverse intraprocedural edges here.  */
+	    log ("rejecting intraprocedural call edge");
+	    continue;
+	  }
+	}
+
+      checker3_path new_path (path);
+
+      /* For conditionals and switch statements, add the
+	 relevant conditions (for the specific edge) to new_ps;
+	 skip edges for which the resulting constraints
+	 are impossible.  */
+      const gimple *last_stmt = node->get_last_stmt ();
+      if (!new_path.m_ps.apply_constraints_for_edge (*succ, last_stmt))
+	{
+	  log ("skipping impossible edge to SN: %i",
+	       succ->m_dest->m_index);
+	  continue;
+	}
+      // TODO: but if it's a loop, consider skipping early
+
+      // TODO: reject edges that we've already followed
+
+      /* Allow for pattern-matching based on edge constraints.  */
+      //log ("considering edge-based patterns");
+
+      /* We need to clone the backtrace here so if the transition
+	 writes into the path each edge has its own copy.  */
+
+#if 0
+      checker_event *event
+	= new edge_event (succ,
+			  (last_stmt
+			   ? last_stmt->location
+			   : UNKNOWN_LOCATION),
+			  node->m_fun->decl,
+			  new_b.m_path.m_stack_depth);
+      new_b.m_path.add_event (event);
+
+      sm_instance per_edge_new_sm_inst = sm_inst;
+      engine_sm_context ctxt (this, new_b,
+			      &per_edge_new_sm_inst,
+			      &sm_inst,
+			      &new_b.m_path, &new_ps);
+      m_sm.on_edge (&ctxt, last_stmt, succ);
+#endif
+
+#if 0
+      /* This could have updated the state.  Make sure this effect
+	 is recorded in the cache.  */
+      add_blk_edges_to_cache (node, per_edge_new_sm_inst,
+			      per_block_old_sm_inst);
+#endif
+
+      new_path.add_link (succ, succ->m_dest);
+      traverse (new_path);
+    }
+}
+
+/* FIXME.  */
+
+void
+engine::log_stats () const
+{
+  log ("#supernodes: %i", m_sg.num_nodes ());
+  log ("#superedges: %i", m_sg.num_edges ());
+  log ("m_num_traverse_calls: %i", m_num_traverse_calls);
+  log ("m_on_stmt_calls: %i", m_on_stmt_calls);
+}
+
+
+/* FIXME.  */
+
+static int
+function_comparator (const void *pn1, const void *pn2)
+{
+  const cgraph_node *node1 = *(const cgraph_node * const *)pn1;
+  const cgraph_node *node2 = *(const cgraph_node * const *)pn2;
+
+  location_t loc1 = node1->get_fun ()->function_start_locus;
+  location_t loc2 = node2->get_fun ()->function_start_locus;
+
+  return linemap_compare_locations (line_table, loc2, loc1);
+}
+
+/* FIXME.  */
+/* TODO: should we run them all at once, or serially?
+   For now, this does them serially.  */
+
+void
+run_one_checker (const supergraph &sg, const state_machine &sm, logger *logger)
+{
+  JIT_LOG_FUNC_1 (logger, "sm: %s", sm.get_name ());
+
+  if (flag_analyzer_checker)
+    {
+      if (0 != strcmp (flag_analyzer_checker, sm.get_name ()))
+	{
+	  if (logger)
+	    logger->log ("skipping checker %s (does not match %s)",
+			 sm.get_name (), flag_analyzer_checker);
+	  return;
+	}
+    }
+
+  engine e (sg, sm, logger, analyzer_verbosity);
+
+  cgraph_node *node;
+  auto_vec<cgraph_node *> root_functions;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+    root_functions.safe_push (node);
+
+  /* Sort root_functions so that diagnostics are emitted in a
+     sane order.
+     TODO: perhaps we should buffer the diagnostics and sort them later,
+     instead.  */
+  root_functions.qsort (function_comparator);
+
+  int i;
+  FOR_EACH_VEC_ELT (root_functions, i, node)
+    {
+      function *fun = node->get_fun ();
+
+      if (!TREE_PUBLIC (fun->decl))
+	{
+	  if (logger)
+	    logger->log ("not traversing %qE (static)", fun->decl);
+	  continue;
+	}
+
+      log_scope s (logger, "top-level traversal", "%s", function_name (fun));
+
+      checker3_path path (sg);
+      path.add_link (NULL, sg.get_node_for_function_entry (fun));
+      e.traverse (path);
+    }
+
+  e.log_stats ();
+}
+
+} // namespace impl_3
+
+/* FIXME.  */
+
+void
+run_checkers_impl_3 ()
+{
+  //FILE *dump = fopen ("test.out", "w"); // FIXME
+  auto_client_timevar tv ("run_checkers");
+  {
+    log_user the_logger (NULL);
+    if (flag_dump_analyzer)
+      {
+	FILE *dump_file = /* 0 ? dump : */stderr; // FIXME
+	the_logger.set_logger (new logger (dump_file, 0, 0,
+					   *global_dc->printer));
+      }
+    JIT_LOG_SCOPE (the_logger.get_logger ());
+
+    /* Create the supergraph.  */
+    supergraph sg (the_logger.get_logger ());
+
+    if (1)
+      {
+	char *filename = concat (dump_base_name, ".supergraph.dot", NULL);
+	sg.dump_dot (filename, (enum supergraph_dot_flags)0);
+	free (filename);
+      }
+
+#if 0
+    // TODO: selftest for auto_delete_vec
+    auto_delete_vec <state_machine> checkers;
+    make_checkers (checkers, the_logger.get_logger ());
+
+    int i;
+    state_machine *checker;
+    FOR_EACH_VEC_ELT (checkers, i, checker)
+      // FIXME: this requires better timevar ID management
+#endif
+
+    // TODO
+    // For now, just count paths through the supergraph
+
+#if 1
+    {
+      auto_client_timevar tv ("malloc_state_machine");
+      malloc_state_machine sm (the_logger.get_logger ());
+      impl_3::run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
+    {
+      auto_client_timevar tv ("fileptr_state_machine");
+      fileptr_state_machine sm (the_logger.get_logger ());
+      impl_3::run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
+    {
+      auto_client_timevar tv ("taint_state_machine");
+      taint_state_machine sm (the_logger.get_logger ());
+      impl_3::run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
+    {
+      auto_client_timevar tv ("sensitive_state_machine");
+      sensitive_state_machine sm (the_logger.get_logger ());
+      impl_3::run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
+    /* We only attempt to run the pattern tests if it might have been manually
+       enabled (for DejaGnu purposes).  */
+    if (flag_analyzer_checker)
+      {
+	auto_client_timevar tv ("pattern_test_state_machine");
+	pattern_test_state_machine sm (the_logger.get_logger ());
+	impl_3::run_one_checker (sg, sm, the_logger.get_logger ());
+      }
+#endif
+
+    /* end of lifetime of the_logger (so that dump file is closed after the
+       various dtors run).  */
+  }
+
+  //fclose (dump);
+}
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index 860d704..01f1a9d 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -682,6 +682,16 @@ superedge::get_description () const
 
 /* FIXME.  */
 
+std::string
+superedge::to_str () const
+{
+  return (std::to_string (m_src->m_index)
+	  + " -> "
+	  + std::to_string (m_dest->m_index));
+}
+
+/* FIXME.  */
+
 void
 cfg_superedge::dump_label_to_pp (pretty_printer *pp) const
 {
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index c68411f..85b762e 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -242,6 +242,8 @@ class superedge
 
   char *get_description () const;
 
+  std::string to_str () const;
+
  protected:
   superedge (supernode *src, supernode *dest, enum edge_kind kind)
     : m_src (src), m_dest (dest), m_kind (kind)
diff --git a/gcc/common.opt b/gcc/common.opt
index d5ccbe1..c59063d 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -996,6 +996,10 @@ fanalyzer-checker=
 Common Joined RejectNegative Var(flag_analyzer_checker)
 FIXME
 
+fanalyzer-impl=
+Common Joined RejectNegative UInteger Var(flag_analyzer_impl)
+FIXME
+
 fargument-alias
 Common Ignore
 Does nothing. Preserved for backward compatibility.
-- 
1.8.5.3

