analyzer: fix ICE due to missing state_change purging (PR 93374)
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 11 Feb 2020 15:52:40 +0000 (10:52 -0500)
committerDavid Malcolm <dmalcolm@redhat.com>
Tue, 11 Feb 2020 18:37:09 +0000 (13:37 -0500)
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.

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/ChangeLog
gcc/analyzer/engine.cc
gcc/analyzer/exploded-graph.h
gcc/analyzer/program-state.cc
gcc/analyzer/program-state.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/analyzer/torture/pr93374.c [new file with mode: 0644]

index 0734716a8a8d874a8dd8eedcbbe61c899407579b..0313e437f34c161e188506d15cf5e1082e0e3c47 100644 (file)
@@ -1,3 +1,23 @@
+2020-02-11  David Malcolm  <dmalcolm@redhat.com>
+
+       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.
+
 2020-02-11  David Malcolm  <dmalcolm@redhat.com>
 
        PR analyzer/93669
index 8d5f9c69724c91a3bbd1a77d024a25e8123dda46..4d329e2b6af59be05d04d69152a5aaf24a60bd51 100644 (file)
@@ -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;
 }
index e47816a5b6ef83e12e0dd43f87b3ab1c68a5ffa6..5d69bffddddde3083cbae64d560eeb402b2a36bd 100644 (file)
@@ -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);
index 4c0b9a8bfa05754f720d838dd5a0ffb6e2baaa80..82b921eb969d6d6c3cad9ceff474dea9ca685573 100644 (file)
@@ -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
index d2badb1a2ed07cd9e7a577166691676eb5b48a18..a4608c7498d09cc7230bb53d1a2b4f78c8f3ea07 100644 (file)
@@ -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;
index f75a6781d6bc0967824a75a3b21786d5db1a80e9..4f591539a9529a2047421a7e46b6281011e38e9f 100644 (file)
@@ -1,3 +1,8 @@
+2020-02-11  David Malcolm  <dmalcolm@redhat.com>
+
+       PR analyzer/93374
+       * gcc.dg/analyzer/torture/pr93374.c: New test.
+
 2020-02-11  David Malcolm  <dmalcolm@redhat.com>
 
        PR analyzer/93669
diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/pr93374.c b/gcc/testsuite/gcc.dg/analyzer/torture/pr93374.c
new file mode 100644 (file)
index 0000000..a7adecd
--- /dev/null
@@ -0,0 +1,2 @@
+#include <stdlib.h>
+#include "../../../gcc.c-torture/execute/pr27073.c"