[committed] analyzer: fix ICE due to missing state_change purging (PR 93374)

Message ID 20200211184315.5538-1-dmalcolm@redhat.com
State New
Headers show
Series
  • [committed] analyzer: fix ICE due to missing state_change purging (PR 93374)
Related show

Commit Message

David Malcolm Feb. 11, 2020, 6:43 p.m.
PR analyzer/93374 reports an ICE within state_change::validate due to an
m_new_sid in a recorded state-change being out of range of the svalues
of the region_model of the new state.

During get_or_create_node we attempt to merge the new state with the
state of each of the existing enodes at the program point (in the
absence of sm-state differences), simplifying the state at each
attempt, and potentially reusing a node if we get a match.

This state-merging invalidates any svalue_ids within any state_change
object.

The root cause is that, although the code was purging any such
svalue_ids for the case where no match was found during merging, it was
failing to purge them for the case where a matching enode *was* found
for the merged state, leading to an invalid state_change along the
exploded_edge to the reused enode.

This patch moves the invalidation code to cover both cases, fixing the
ICE.  It also extends state_change validation so that states are also
checked.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to master as r10-6582-ga60d98890bba58649c26c2fc0c6f28cd6073aaaf.

gcc/analyzer/ChangeLog:
	PR analyzer/93374
	* engine.cc (exploded_edge::exploded_edge): Add ext_state param
	and pass it to change.validate.
	(exploded_graph::get_or_create_node): Move purging of change
	svalues to also cover the case of reusing an existing enode.
	(exploded_graph::add_edge): Pass m_ext_state to exploded_edge's
	ctor.
	* exploded-graph.h (exploded_edge::exploded_edge): Add ext_state
	param.
	* program-state.cc (state_change::sm_change::validate): Likewise.
	Assert that m_sm_idx is sane.  Use ext_state to validate
	m_old_state and m_new_state.
	(state_change::validate): Add ext_state param and pass it to
	the sm_change validate calls.
	* program-state.h (state_change::sm_change::validate): Add
	ext_state param.
	(state_change::validate): Likewise.

gcc/testsuite/ChangeLog:
	PR analyzer/93374
	* gcc.dg/analyzer/torture/pr93374.c: New test.
---
 gcc/analyzer/engine.cc                        | 21 ++++++++++---------
 gcc/analyzer/exploded-graph.h                 |  1 +
 gcc/analyzer/program-state.cc                 | 12 ++++++++---
 gcc/analyzer/program-state.h                  |  6 ++++--
 .../gcc.dg/analyzer/torture/pr93374.c         |  2 ++
 5 files changed, 27 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/torture/pr93374.c

-- 
2.21.0

Patch

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 837f3feabfe..7860da0572a 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1398,13 +1398,14 @@  rewind_info_t::add_events_to_path (checker_path *emission_path,
 /* exploded_edge's ctor.  */
 
 exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
+			      const extrinsic_state &ext_state,
 			      const superedge *sedge,
 			      const state_change &change,
 			      custom_info_t *custom_info)
 : dedge<eg_traits> (src, dest), m_sedge (sedge), m_change (change),
   m_custom_info (custom_info)
 {
-  change.validate (dest->get_state ());
+  change.validate (dest->get_state (), ext_state);
 }
 
 /* exploded_edge's dtor.  */
@@ -1898,8 +1899,14 @@  exploded_graph::get_or_create_node (const program_point &point,
 		logger->log ("merging new state with that of EN: %i",
 			     existing_enode->m_index);
 
-	      /* Try again for a cache hit.  */
+	      /* Try again for a cache hit.
+		 Whether we get one or not, merged_state's value_ids have no
+		 relationship to those of the input state, and thus to those
+		 of CHANGE, so we must purge any svalue_ids from *CHANGE.  */
 	      ps.set_state (merged_state);
+	      if (change)
+		change->on_svalue_purge (svalue_id::from_int (0));
+
 	      if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
 		{
 		  /* An exploded_node for PS already exists.  */
@@ -1910,13 +1917,6 @@  exploded_graph::get_or_create_node (const program_point &point,
 		  per_cs_stats->m_node_reuse_after_merge_count++;
 		  return *slot;
 		}
-
-	      /* Otherwise, continue, using the merged state in "ps".
-		 Given that merged_state's svalue_ids have no relationship
-		 to those of the input state, and thus to those of CHANGE,
-		 purge any svalue_ids from *CHANGE.  */
-	      if (change)
-		change->on_svalue_purge (svalue_id::from_int (0));
 	    }
 	  else
 	    if (logger)
@@ -1986,7 +1986,8 @@  exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
 			  const state_change &change,
 			  exploded_edge::custom_info_t *custom_info)
 {
-  exploded_edge *e = new exploded_edge (src, dest, sedge, change, custom_info);
+  exploded_edge *e = new exploded_edge (src, dest, m_ext_state,
+					sedge, change, custom_info);
   digraph<eg_traits>::add_edge (e);
   return e;
 }
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index e47816a5b6e..5d69bffdddd 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -306,6 +306,7 @@  class exploded_edge : public dedge<eg_traits>
   };
 
   exploded_edge (exploded_node *src, exploded_node *dest,
+		 const extrinsic_state &ext_state,
 		 const superedge *sedge,
 		 const state_change &change,
 		 custom_info_t *custom_info);
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 4c0b9a8bfa0..82b921eb969 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1083,8 +1083,13 @@  state_change::sm_change::on_svalue_purge (svalue_id first_unused_sid)
 /* Assert that this object is sane.  */
 
 void
-state_change::sm_change::validate (const program_state &new_state) const
+state_change::sm_change::validate (const program_state &new_state,
+				   const extrinsic_state &ext_state) const
 {
+  gcc_assert ((unsigned)m_sm_idx < ext_state.get_num_checkers ());
+  const state_machine &sm = ext_state.get_sm (m_sm_idx);
+  sm.validate (m_old_state);
+  sm.validate (m_new_state);
   m_new_sid.validate (*new_state.m_region_model);
 }
 
@@ -1191,7 +1196,8 @@  state_change::on_svalue_purge (svalue_id first_unused_sid)
 /* Assert that this object is sane.  */
 
 void
-state_change::validate (const program_state &new_state) const
+state_change::validate (const program_state &new_state,
+			const extrinsic_state &ext_state) const
 {
   /* Skip this in a release build.  */
 #if !CHECKING_P
@@ -1200,7 +1206,7 @@  state_change::validate (const program_state &new_state) const
   unsigned i;
   sm_change *change;
   FOR_EACH_VEC_ELT (m_sm_changes, i, change)
-    change->validate (new_state);
+    change->validate (new_state, ext_state);
 }
 
 #if CHECKING_P
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index d2badb1a2ed..a4608c7498d 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -343,7 +343,8 @@  class state_change
     void remap_svalue_ids (const svalue_id_map &map);
     int on_svalue_purge (svalue_id first_unused_sid);
 
-    void validate (const program_state &new_state) const;
+    void validate (const program_state &new_state,
+		   const extrinsic_state &ext_state) const;
 
     int m_sm_idx;
     svalue_id m_new_sid;
@@ -367,7 +368,8 @@  class state_change
   void remap_svalue_ids (const svalue_id_map &map);
   int on_svalue_purge (svalue_id first_unused_sid);
 
-  void validate (const program_state &new_state) const;
+  void validate (const program_state &new_state,
+		 const extrinsic_state &ext_state) const;
 
  private:
   auto_vec<sm_change> m_sm_changes;
diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/pr93374.c b/gcc/testsuite/gcc.dg/analyzer/torture/pr93374.c
new file mode 100644
index 00000000000..a7adecdc872
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/torture/pr93374.c
@@ -0,0 +1,2 @@ 
+#include <stdlib.h>
+#include "../../../gcc.c-torture/execute/pr27073.c"