[3/7,analyzer] Generalize rewind_info_t to exploded_edge::custom_info_t

Message ID 20191204162530.9285-4-dmalcolm@redhat.com
State New
Headers show
Series
  • Add checking for unsafe calls within signal handlers
Related show

Commit Message

David Malcolm Dec. 4, 2019, 4:25 p.m.
This patch generalizes the optional rewind_info_t associated with an
exploded_edge (for handling longjmp) into a subclass of a new
exploded_edge::custom_info_t abstract base class, so that other kinds
of custom edges can be supported (e.g. to support signal handlers being
called, or possibly to support C++ exceptions, etc)

gcc/ChangeLog:
	* analyzer/analyzer.h (class rewind_info_t): New forward decl.
	* analyzer/checker-path.cc (rewind_event::rewind_event): Update
	assertion.
	(rewind_to_setjmp_event::prepare_for_emission): Update call to
	get_enode_origin.
	* analyzer/checker-path.h
	(rewind_to_setjmp_event::rewind_to_setjmp_event): Add rewind_info
	param and use it to initializer m_rewind_event.
	(rewind_to_setjmp_event::clone): Update for new field.
	(rewind_to_setjmp_event::m_rewind_info): New field.
	* analyzer/diagnostic-manager.cc
	(diagnostic_manager::add_events_for_eedge): Drop src_stack_depth
	local.  Move injection of rewind_from/to_setjmp_event from here
	to rewind_info_t::add_events_to_path, calling it via a vfunc.
	* analyzer/engine.cc: Include "analyzer/checker-path.h".
	(rewind_info_t::update_model): New vfunc.
	(rewind_info_t::add_events_to_path): New vfunc.
	(exploded_edge::exploded_edge): Generalize final param from
	rewind_info_t * to custom_info_t *.
	(exploded_edge::~exploded_edge): Update for renaming of
	m_rewind_info to m_custom_info.
	(exploded_edge::dump_dot): Likewise, replacing hardcode print
	of "rewind" with a call to the custom_info_t::print vfunc.
	(exploded_graph::add_edge): Update final param from
	rewind_info_t * to exploded_edge::custom_info_t *.
	(exploded_path::feasible_p): When logging a rejection due to the
	region model, dump the model.  Move update due to rewind info
	to rewind_info_t::update_model and call it via a vfunc.
	* analyzer/exploded-graph.h (class exploded_edge::custom_info_t):
	New class.
	(exploded_edge::m_rewind_info): Rename to...
	(exploded_edge::m_custom_info): ...this, converting from a
	rewind_info_t * to a custom_info_t *.
	(class rewind_info_t): Make a subclass of
	exploded_edge::custom_info_t.
	(rewind_info_t::print): New vfunc.
	(rewind_info_t::update_model): New vfunc.
	(rewind_info_t::add_events_to_path): New vfunc.
	(exploded_graph::add_edge): Convert final param from
	rewind_info_t * to exploded_edge::custom_info_t *.
---
 gcc/analyzer/analyzer.h            |   1 +
 gcc/analyzer/checker-path.cc       |   4 +-
 gcc/analyzer/checker-path.h        |  10 ++-
 gcc/analyzer/diagnostic-manager.cc |  19 ++----
 gcc/analyzer/engine.cc             | 104 +++++++++++++++++++++--------
 gcc/analyzer/exploded-graph.h      |  85 +++++++++++++++--------
 6 files changed, 148 insertions(+), 75 deletions(-)

-- 
2.21.0

Patch

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 90da44b1a00a..19bc2c6d8252 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -65,6 +65,7 @@  class analysis_plan;
 class state_purge_map;
 class state_purge_per_ssa_name;
 class state_change;
+class rewind_info_t;
 
 ////////////////////////////////////////////////////////////////////////////
 
diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc
index 0a1840682cd5..d277c2806308 100644
--- a/gcc/analyzer/checker-path.cc
+++ b/gcc/analyzer/checker-path.cc
@@ -705,7 +705,7 @@  rewind_event::rewind_event (const exploded_edge *eedge,
 : checker_event (kind, loc, fndecl, depth),
   m_eedge (eedge)
 {
-  gcc_assert (m_eedge->m_rewind_info);
+  gcc_assert (m_eedge->m_custom_info); // a rewind_info_t
 }
 
 ////////////////////////////////////////////////////////////////////////////
@@ -789,7 +789,7 @@  rewind_to_setjmp_event::prepare_for_emission (checker_path *path,
 					      diagnostic_event_id_t emission_id)
 {
   checker_event::prepare_for_emission (path, pd, emission_id);
-  path->get_setjmp_event (get_eedge ()->m_rewind_info->get_enode_origin (),
+  path->get_setjmp_event (m_rewind_info->get_enode_origin (),
 			  &m_original_setjmp_event_id);
 }
 
diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h
index 916e5a736e3e..ccff8f2ea0bc 100644
--- a/gcc/analyzer/checker-path.h
+++ b/gcc/analyzer/checker-path.h
@@ -428,8 +428,10 @@  class rewind_to_setjmp_event : public rewind_event
 {
 public:
   rewind_to_setjmp_event (const exploded_edge *eedge,
-			  location_t loc, tree fndecl, int depth)
-  : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth)
+			  location_t loc, tree fndecl, int depth,
+			  const rewind_info_t *rewind_info)
+  : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth),
+    m_rewind_info (rewind_info)
   {
   }
 
@@ -438,7 +440,8 @@  public:
   rewind_to_setjmp_event *clone () const FINAL OVERRIDE
   {
     return new rewind_to_setjmp_event (get_eedge (),
-				       m_loc, m_fndecl, m_depth);
+				       m_loc, m_fndecl, m_depth,
+				       m_rewind_info);
   }
 
   void prepare_for_emission (checker_path *path,
@@ -447,6 +450,7 @@  public:
 
 private:
   diagnostic_event_id_t m_original_setjmp_event_id;
+  const rewind_info_t *m_rewind_info;
 };
 
 /* Concrete subclass of checker_event for use at the end of a path:
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 8cd4507bc6e5..461cc3318b7f 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -655,7 +655,6 @@  diagnostic_manager::add_events_for_eedge (const exploded_edge &eedge,
 {
   const exploded_node *src_node = eedge.m_src;
   const program_point &src_point = src_node->get_point ();
-  const int src_stack_depth = src_point.get_stack_depth ();
   const exploded_node *dst_node = eedge.m_dest;
   const program_point &dst_point = dst_node->get_point ();
   const int dst_stack_depth = dst_point.get_stack_depth ();
@@ -693,20 +692,10 @@  diagnostic_manager::add_events_for_eedge (const exploded_edge &eedge,
   for_each_state_change (src_state, dst_state, ext_state,
 			 &visitor);
 
-  /* Add events for rewinding from a longjmp to a setjmp.  */
-  if (eedge.m_rewind_info)
-    {
-      emission_path->add_event
-	(new rewind_from_longjmp_event
-	 (&eedge, src_point.get_supernode ()->get_end_location (),
-	  src_point.get_fndecl (),
-	  src_stack_depth));
-      emission_path->add_event
-	(new rewind_to_setjmp_event
-	 (&eedge, eedge.m_rewind_info->get_setjmp_call ()->location,
-	  dst_point.get_fndecl (),
-	  dst_stack_depth));
-    }
+  /* Allow non-standard edges to add events, e.g. when rewinding from
+     longjmp to a setjmp.  */
+  if (eedge.m_custom_info)
+    eedge.m_custom_info->add_events_to_path (emission_path, eedge);
 
   /* Add events for superedges, function entries, and for statements.  */
   switch (dst_point.get_kind ())
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index eed2be091c93..d327340c8c28 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -26,6 +26,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gcc-rich-location.h"
 #include "analyzer/exploded-graph.h"
 #include "analyzer/analysis-plan.h"
+#include "analyzer/checker-path.h"
 #include "analyzer/state-purge.h"
 
 /* For an overview, see gcc/doc/analyzer.texi.  */
@@ -1247,6 +1248,66 @@  exploded_node::dump_succs_and_preds (FILE *outf) const
 
 ////////////////////////////////////////////////////////////////////////////
 
+/* class rewind_info_t : public exploded_edge::custom_info_t.  */
+
+/* Implementation of exploded_edge::custom_info_t::update_model vfunc
+   for rewind_info_t.
+
+   Update state for the special-case of a rewind of a longjmp
+   to a setjmp (which doesn't have a superedge, but does affect
+   state).  */
+
+void
+rewind_info_t::update_model (region_model *model,
+			     const exploded_edge &eedge)
+{
+  const exploded_node &src_enode = *eedge.m_src;
+  const program_point &src_point = src_enode.get_point ();
+
+  const gimple *last_stmt
+    = src_point.get_supernode ()->get_last_stmt ();
+  gcc_assert (last_stmt);
+  const gcall *longjmp_call = as_a <const gcall *> (last_stmt);
+
+  const program_point &longjmp_point = eedge.m_src->get_point ();
+  const program_point &setjmp_point = eedge.m_dest->get_point ();
+
+  gcc_assert (longjmp_point.get_stack_depth ()
+	      >= setjmp_point.get_stack_depth ());
+
+  model->on_longjmp (longjmp_call,
+		     get_setjmp_call (),
+		     setjmp_point.get_stack_depth (), NULL);
+}
+
+/* Implementation of exploded_edge::custom_info_t::add_events_to_path vfunc
+   for rewind_info_t.  */
+
+void
+rewind_info_t::add_events_to_path (checker_path *emission_path,
+				   const exploded_edge &eedge)
+{
+  const exploded_node *src_node = eedge.m_src;
+  const program_point &src_point = src_node->get_point ();
+  const int src_stack_depth = src_point.get_stack_depth ();
+  const exploded_node *dst_node = eedge.m_dest;
+  const program_point &dst_point = dst_node->get_point ();
+  const int dst_stack_depth = dst_point.get_stack_depth ();
+
+  emission_path->add_event
+    (new rewind_from_longjmp_event
+     (&eedge, src_point.get_supernode ()->get_end_location (),
+      src_point.get_fndecl (),
+      src_stack_depth));
+  emission_path->add_event
+    (new rewind_to_setjmp_event
+     (&eedge, get_setjmp_call ()->location,
+      dst_point.get_fndecl (),
+      dst_stack_depth, this));
+}
+
+////////////////////////////////////////////////////////////////////////////
+
 /* class exploded_edge : public dedge.  */
 
 /* exploded_edge's ctor.  */
@@ -1254,9 +1315,9 @@  exploded_node::dump_succs_and_preds (FILE *outf) const
 exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
 			      const superedge *sedge,
 			      const state_change &change,
-			      rewind_info_t *rewind_info)
+			      custom_info_t *custom_info)
 : dedge (src, dest), m_sedge (sedge), m_change (change),
-  m_rewind_info (rewind_info)
+  m_custom_info (custom_info)
 {
   change.validate (dest->get_state ());
 }
@@ -1265,7 +1326,7 @@  exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
 
 exploded_edge::~exploded_edge ()
 {
-  delete m_rewind_info;
+  delete m_custom_info;
 }
 
 /* Implementation of dedge::dump_dot vfunc for exploded_edge.
@@ -1300,7 +1361,7 @@  exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &args) const
 	style = "\"dotted\"";
 	break;
       }
-  if (m_rewind_info)
+  if (m_custom_info)
     {
       color = "red";
       style = "\"dotted\"";
@@ -1316,8 +1377,8 @@  exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &args) const
 
   if (m_sedge)
     m_sedge->dump_label_to_pp (pp, false);
-  else if (m_rewind_info)
-    pp_string (pp, "rewind");
+  else if (m_custom_info)
+    m_custom_info->print (pp);
 
   m_change.dump (pp, args.m_eg.get_ext_state ());
   //pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
@@ -1859,9 +1920,9 @@  exploded_edge *
 exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
 			  const superedge *sedge,
 			  const state_change &change,
-			  rewind_info_t *rewind_info)
+			  exploded_edge::custom_info_t *custom_info)
 {
-  exploded_edge *e = new exploded_edge (src, dest, sedge, change, rewind_info);
+  exploded_edge *e = new exploded_edge (src, dest, sedge, change, custom_info);
   digraph::add_edge (e);
   return e;
 }
@@ -2569,7 +2630,10 @@  exploded_path::feasible_p (logger *logger) const
 	  if (!model.maybe_update_for_edge (*sedge, last_stmt, NULL))
 	    {
 	      if (logger)
-		logger->log ("rejecting due to region model");
+		{
+		  logger->log ("rejecting due to region model");
+		  model.dump_to_pp (logger->get_printer (), false);
+		}
 	      return false;
 	    }
 	}
@@ -2589,26 +2653,8 @@  exploded_path::feasible_p (logger *logger) const
 	      if (logger)
 		logger->log ("  pushing frame for %qD", fun->decl);
 	    }
-	  else if (eedge->m_rewind_info)
-	    {
-	      /* Update state for the special-case of a rewind of a longjmp
-		 to a setjmp (which doesn't have a superedge, but does affect
-		 state).  */
-	      const gimple *last_stmt
-		= src_point.get_supernode ()->get_last_stmt ();
-	      gcc_assert (last_stmt);
-	      const gcall *longjmp_call = as_a <const gcall *> (last_stmt);
-
-	      const program_point &longjmp_point = eedge->m_src->get_point ();
-	      const program_point &setjmp_point = eedge->m_dest->get_point ();
-
-	      gcc_assert (longjmp_point.get_stack_depth ()
-			  >= setjmp_point.get_stack_depth ());
-
-	      model.on_longjmp (longjmp_call,
-				eedge->m_rewind_info->get_setjmp_call (),
-				setjmp_point.get_stack_depth (), NULL);
-	    }
+	  else if (eedge->m_custom_info)
+	    eedge->m_custom_info->update_model (&model, *eedge);
 	}
 
       /* Handle phi nodes on an edge leaving a PK_BEFORE_SUPERNODE (to
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index c8e36f25a575..26ae109628f4 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -252,16 +252,73 @@  public:
   const int m_index;
 };
 
+/* An edge within the exploded graph.
+   Some exploded_edges have an underlying superedge; others don't.  */
+
+class exploded_edge : public dedge<eg_traits>
+{
+ public:
+  /* Abstract base class for associating custom data with an
+     exploded_edge, for handling non-standard edges such as
+     rewinding from a longjmp, signal handlers, etc.  */
+  class custom_info_t
+  {
+  public:
+    virtual ~custom_info_t () {}
+
+    /* Hook for making .dot label more readable .  */
+    virtual void print (pretty_printer *pp) = 0;
+
+    /* Hook for updating MODEL within exploded_path::feasible_p.  */
+    virtual void update_model (region_model *model,
+			       const exploded_edge &eedge) = 0;
+
+    virtual void add_events_to_path (checker_path *emission_path,
+				     const exploded_edge &eedge) = 0;
+  };
+
+  exploded_edge (exploded_node *src, exploded_node *dest,
+		 const superedge *sedge,
+		 const state_change &change,
+		 custom_info_t *custom_info);
+  ~exploded_edge ();
+  void dump_dot (graphviz_out *gv, const dump_args_t &args)
+    const FINAL OVERRIDE;
+
+  //private:
+  const superedge *const m_sedge;
+
+  const state_change m_change;
+
+  /* NULL for most edges; will be non-NULL for special cases
+     such as an unwind from a longjmp to a setjmp, or when
+     a signal is delivered to a signal-handler.
+
+     Owned by this class.  */
+  custom_info_t *m_custom_info;
+};
+
 /* Extra data for an exploded_edge that represents a rewind from a
    longjmp to a setjmp.  */
 
-class rewind_info_t
+class rewind_info_t : public exploded_edge::custom_info_t
 {
 public:
   rewind_info_t (const exploded_node *enode_origin)
   : m_enode_origin (enode_origin)
   {}
 
+  void print (pretty_printer *pp) FINAL OVERRIDE
+  {
+    pp_string (pp, "rewind");
+  }
+
+  void update_model (region_model *model,
+		     const exploded_edge &eedge) FINAL OVERRIDE;
+
+  void add_events_to_path (checker_path *emission_path,
+			   const exploded_edge &eedge) FINAL OVERRIDE;
+
   const program_point &get_setjmp_point () const
   {
     const program_point &origin_point = m_enode_origin->get_point ();
@@ -285,30 +342,6 @@  private:
   const exploded_node *m_enode_origin;
 };
 
-/* An edge within the exploded graph.
-   Some exploded_edges have an underlying superedge; others don't.  */
-
-class exploded_edge : public dedge<eg_traits>
-{
- public:
-  exploded_edge (exploded_node *src, exploded_node *dest,
-		 const superedge *sedge,
-		 const state_change &change,
-		 rewind_info_t *rewind_info);
-  ~exploded_edge ();
-  void dump_dot (graphviz_out *gv, const dump_args_t &args)
-    const FINAL OVERRIDE;
-
-  //private:
-  const superedge *const m_sedge;
-
-  const state_change m_change;
-
-  /* NULL for most edges; will be non-NULL for an unwind from a longjmp
-     to a setjmp (owned by this class).  */
-  rewind_info_t *m_rewind_info;
-};
-
 /* Statistics about aspects of an exploded_graph.  */
 
 struct stats
@@ -665,7 +698,7 @@  public:
   exploded_edge *add_edge (exploded_node *src, exploded_node *dest,
 			   const superedge *sedge,
 			   const state_change &change,
-			   rewind_info_t *rewind_info = NULL);
+			   exploded_edge::custom_info_t *custom = NULL);
 
   per_program_point_data *
   get_or_create_per_program_point_data (const program_point &);