From 9f085d789f31759b460aae1cd42977daf6fc18d1 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 7 Jul 2020 11:50:12 -0400
Subject: [PATCH 284/377] FIXME: WIP on inherited state (taint)

---
 gcc/analyzer/diagnostic-manager.cc |   2 +-
 gcc/analyzer/engine.cc             |  16 ++-
 gcc/analyzer/program-state.cc      | 172 ++++++++++++++++++-----------
 gcc/analyzer/program-state.h       |  31 +++---
 gcc/analyzer/region-model2.cc      |  19 ++++
 gcc/analyzer/region-model2.h       |   1 +
 6 files changed, 154 insertions(+), 87 deletions(-)

diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index c5e57d281ed..43cdfad3fd9 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -979,7 +979,7 @@ for_each_state_change2 (const program_state &src_state,
 	  const svalue2 *sval = (*iter).first;
 	  tree dst_rep = dst_state.get_representative_tree2 (sval);
 	  state_machine::state_t dst_sm_val = (*iter).second.m_state;
-	  state_machine::state_t src_sm_val = src_smap.get_state (sval);
+	  state_machine::state_t src_sm_val = src_smap.get_state (sval, ext_state);
 	  if (dst_sm_val != src_sm_val)
 	    {
 	      const svalue2 *origin_sval = (*iter).second.m_origin;
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 26925fc5245..ca97f2d9efd 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -202,10 +202,7 @@ impl_region_model2_context::on_svalue2_leak (const svalue2 *sval)
   int sm_idx;
   sm_state_map2 *smap;
   FOR_EACH_VEC_ELT (m_new_state->m_checker_states2, sm_idx, smap)
-    {
-      const state_machine &sm = m_ext_state.get_sm (sm_idx);
-      smap->on_svalue2_leak (sm, sm_idx, sval, this);
-    }
+    smap->on_svalue2_leak (sval, this);
 
 #if 0
   // FIXME: what is/was this used for?
@@ -223,7 +220,7 @@ impl_region_model2_context::on_unknown_change (const svalue2 *sval,
   FOR_EACH_VEC_ELT (m_new_state->m_checker_states2, sm_idx, smap)
     {
       const state_machine &sm = m_ext_state.get_sm (sm_idx);
-      smap->on_unknown_change (sm, sval, is_mutable);
+      smap->on_unknown_change (sm, sval, is_mutable, m_ext_state);
     }
 }
 
@@ -545,7 +542,8 @@ public:
     const svalue2 *origin_new_sval
       = m_new_state->m_region_model2->get_rvalue (origin, &new_ctxt);
 
-    state_machine::state_t current = m_old_smap->get_state (var_old_sval);
+    state_machine::state_t current
+      = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ());
     if (current == from)
       {
 	if (logger)
@@ -555,7 +553,7 @@ public:
 		       m_sm.get_state_name (from),
 		       m_sm.get_state_name (to));
 	m_new_smap->set_state (m_new_state->m_region_model2, var_new_sval,
-			       to, origin_new_sval);
+			       to, origin_new_sval, m_eg.get_ext_state ());
 #if 0
 	if (m_change)
 	  m_change->add_sm_change (m_sm_idx, var_new_sval, from, to);
@@ -577,7 +575,7 @@ public:
       {
 	const svalue2 *var_old_sval
 	  = m_old_state->m_region_model2->get_rvalue (var, &old_ctxt);
-	current = m_old_smap->get_state (var_old_sval);
+	current = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ());
       }
     else
       current = m_old_smap->get_global_state ();
@@ -1796,7 +1794,7 @@ exploded_node::on_stmt2 (exploded_graph &eg,
 		= state->m_region_model2->get_fndecl_for_call (call, &ctxt);
 
 	      if (!fndecl_has_gimple_body_p (callee_fndecl))
-		new_smap->purge_for_unknown_fncall (eg, sm, call, callee_fndecl,
+		new_smap->purge_for_unknown_fncall (eg, call, callee_fndecl,
 						    state->m_region_model2,
 						    &ctxt);
 	    }
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index d9a4f24d308..fc3c938e44b 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -101,6 +101,17 @@ extrinsic_state::dump () const
   dump_to_file (stderr);
 }
 
+/* FIXME.  */
+
+region_model2_manager *
+extrinsic_state::get_model_manager () const
+{
+  if (m_engine2)
+    return m_engine2->get_model_manager ();
+  else
+    return NULL; /* for selftests.  */
+}
+
 /* class sm_state_map.  */
 
 /* sm_state_map's ctor.  */
@@ -596,8 +607,9 @@ sm_state_map::validate (const state_machine &sm,
 
 /* sm_state_map2's ctor.  */
 
-sm_state_map2::sm_state_map2 ()
-: m_map (), m_global_state (0)
+sm_state_map2::sm_state_map2 (const state_machine &sm,
+			      int sm_idx)
+  : m_sm (sm), m_sm_idx (sm_idx), m_map (), m_global_state (0)
 {
 }
 
@@ -609,12 +621,12 @@ sm_state_map2::clone () const
   return new sm_state_map2 (*this);
 }
 
-/* Print this sm_state_map2 (for SM) to PP.
+/* Print this sm_state_map2 to PP.
    If MODEL is non-NULL, print representative tree values where
    available.  */
 
 void
-sm_state_map2::print (const state_machine &sm, const region_model2 *model,
+sm_state_map2::print (const region_model2 *model,
 		      bool simple, bool multiline,
 		      pretty_printer *pp) const
 {
@@ -625,7 +637,7 @@ sm_state_map2::print (const state_machine &sm, const region_model2 *model,
     {
       if (multiline)
 	pp_string (pp, "  ");
-      pp_printf (pp, "global: %s", sm.get_state_name (m_global_state));
+      pp_printf (pp, "global: %s", m_sm.get_state_name (m_global_state));
       if (multiline)
 	pp_newline (pp);
       first = false;
@@ -645,7 +657,7 @@ sm_state_map2::print (const state_machine &sm, const region_model2 *model,
       sval->dump_to_pp (pp, simple);
 
       entry_t e = (*iter).second;
-      pp_printf (pp, ": %s", sm.get_state_name (e.m_state));
+      pp_printf (pp, ": %s", m_sm.get_state_name (e.m_state));
 #if 0
       if (model)
 	if (tree rep = model->get_representative_tree (sid))
@@ -682,12 +694,12 @@ sm_state_map2::print (const state_machine &sm, const region_model2 *model,
 /* Dump this object (for SM) to stderr.  */
 
 DEBUG_FUNCTION void
-sm_state_map2::dump (const state_machine &sm, bool simple) const
+sm_state_map2::dump (bool simple) const
 {
   pretty_printer pp;
   pp_show_color (&pp) = pp_show_color (global_dc->printer);
   pp.buffer->stream = stderr;
-  print (sm, NULL, simple, true, &pp);
+  print (NULL, simple, true, &pp);
   pp_newline (&pp);
   pp_flush (&pp);
 }
@@ -760,15 +772,39 @@ sm_state_map2::operator== (const sm_state_map2 &other) const
    States default to the start state.  */
 
 state_machine::state_t
-sm_state_map2::get_state (const svalue2 *sval) const
+sm_state_map2::get_state (const svalue2 *sval,
+			  const extrinsic_state &ext_state) const
 {
   gcc_assert (sval);
 
   if (entry_t *slot
       = const_cast <map_t &> (m_map).get (sval))
     return slot->m_state;
-  else
-    return 0;
+
+  /* SVAL has no explicit sm-state.
+     If this sm allows for state inheritance, then SVAL might have implicit
+     sm-state inherited via a parent.
+     For example INIT_VAL(foo.field) might inherit taintedness state from
+     INIT_VAL(foo).  */
+  if (m_sm.inherited_state_p ())
+    if (region_model2_manager *mgr = ext_state.get_model_manager ())
+      if (const initial_svalue2 *init_sval = sval->dyn_cast_initial_svalue2 ())
+	{
+	  const region2 *reg = init_sval->get_region ();
+	  /* Try recursing upwards (up to the base region for the cluster).  */
+	  if (!reg->base_region_p ())
+	    if (const region2 *parent_reg = reg->get_parent_region ())
+	      {
+		const svalue2 *parent_init_sval
+		  = mgr->get_or_create_initial_value (parent_reg);
+		state_machine::state_t parent_state
+		  = get_state (parent_init_sval, ext_state);
+		if (parent_state)
+		  return parent_state;
+	      }
+	}
+
+  return 0;
 }
 
 /* Get the "origin" svalue2 for any state of SVAL.  */
@@ -793,7 +829,8 @@ void
 sm_state_map2::set_state (region_model2 *model,
 			  const svalue2 *sval,
 			  state_machine::state_t state,
-			  const svalue2 *origin)
+			  const svalue2 *origin,
+			  const extrinsic_state &ext_state)
 {
   if (model == NULL)
     return;
@@ -803,7 +840,7 @@ sm_state_map2::set_state (region_model2 *model,
     return;
 
   equiv_class2 &ec = model->get_constraints2 ()->get_equiv_class2 (sval);
-  if (!set_state (ec, state, origin))
+  if (!set_state (ec, state, origin, ext_state))
     return;
 
   // TODO:
@@ -830,13 +867,14 @@ sm_state_map2::set_state (region_model2 *model,
 bool
 sm_state_map2::set_state (const equiv_class2 &ec,
 			  state_machine::state_t state,
-			  const svalue2 *origin)
+			  const svalue2 *origin,
+			  const extrinsic_state &ext_state)
 {
   int i;
   const svalue2 *sval;
   bool any_changed = false;
   FOR_EACH_VEC_ELT (ec.m_vars, i, sval)
-    any_changed |= impl_set_state (sval, state, origin);
+    any_changed |= impl_set_state (sval, state, origin, ext_state);
   return any_changed;
 }
 
@@ -846,9 +884,10 @@ sm_state_map2::set_state (const equiv_class2 &ec,
 bool
 sm_state_map2::impl_set_state (const svalue2 *sval,
 			       state_machine::state_t state,
-			       const svalue2 *origin)
+			       const svalue2 *origin,
+			       const extrinsic_state &ext_state)
 {
-  if (get_state (sval) == state)
+  if (get_state (sval, ext_state) == state)
     return false;
 
   /* Special-case state 0 as the default value.  */
@@ -887,11 +926,10 @@ sm_state_map2::get_global_state () const
 
 void
 sm_state_map2::purge_for_unknown_fncall (const exploded_graph &eg,
-					const state_machine &sm,
-					const gcall *call,
-					tree fndecl,
-					region_model2 *new_model,
-					region_model2_context *ctxt)
+					 const gcall *call,
+					 tree fndecl,
+					 region_model2 *new_model,
+					 region_model2_context *ctxt)
 {
   // TODO
 #if 0
@@ -954,15 +992,13 @@ sm_state_map2::purge_for_unknown_fncall (const exploded_graph &eg,
    using FIXME.  */
 
 void
-sm_state_map2::on_svalue2_leak (const state_machine &sm,
-				int sm_idx,
-				const svalue2 *sval,
+sm_state_map2::on_svalue2_leak (const svalue2 *sval,
 				impl_region_model2_context *ctxt)
 {
-  if (state_machine::state_t state = get_state (sval))
+  if (state_machine::state_t state = get_state (sval, ctxt->m_ext_state))
     {
-      if (!sm.can_purge_p (state))
-	ctxt->on_state_leak (sm, sm_idx, sval, state);
+      if (!m_sm.can_purge_p (state))
+	ctxt->on_state_leak (m_sm, m_sm_idx, sval, state);
       m_map.remove (sval);
     }
 
@@ -1031,7 +1067,8 @@ sm_state_map2::on_cast (svalue_id src_sid,
 void
 sm_state_map2::on_unknown_change (const state_machine &sm,
 				  const svalue2 *sval,
-				  bool is_mutable)
+				  bool is_mutable,
+				  const extrinsic_state &ext_state)
 {
   hash_set <const svalue2 *> svals_to_unset;
 
@@ -1066,7 +1103,7 @@ sm_state_map2::on_unknown_change (const state_machine &sm,
   for (hash_set <const svalue2 *>::iterator iter = svals_to_unset.begin ();
        iter != svals_to_unset.end (); ++iter)
     {
-      impl_set_state (*iter, (state_machine::state_t)0, NULL);
+      impl_set_state (*iter, (state_machine::state_t)0, NULL, ext_state);
     }
 }
 
@@ -1113,12 +1150,15 @@ program_state::program_state (const extrinsic_state &ext_state)
     }
   else
     m_region_model = new region_model ();
-  int num_states = ext_state.get_num_checkers ();
+  const int num_states = ext_state.get_num_checkers ();
   for (int i = 0; i < num_states; i++)
     m_checker_states.quick_push (new sm_state_map ());
   if (ext_state.get_engine2 ())
     for (int i = 0; i < num_states; i++)
-      m_checker_states2.safe_push (new sm_state_map2 ());
+      {
+	sm_state_map2 *sm = new sm_state_map2 (ext_state.get_sm (i), i);
+	m_checker_states2.safe_push (sm);
+      }
 }
 
 /* program_state's copy ctor.  */
@@ -1299,7 +1339,7 @@ program_state::print (const extrinsic_state &ext_state,
       if (!smap2->is_empty_p ())
 	{
 	  pp_printf (pp, "%s: ", ext_state.get_name (i));
-	  smap2->print (ext_state.get_sm (i), m_region_model2, true, false, pp);
+	  smap2->print (m_region_model2, true, false, pp);
 	  pp_newline (pp);
 	}
     }
@@ -1370,8 +1410,7 @@ program_state::dump_to_pp (const extrinsic_state &ext_state,
 	  pp_printf (pp, "%s: ", ext_state.get_name (i));
 	  if (multiline)
 	    pp_newline (pp);
-	  smap2->print (ext_state.get_sm (i), m_region_model2,
-			true, multiline, pp);
+	  smap2->print (m_region_model2, true, multiline, pp);
 	  if (!multiline)
 	    pp_string (pp, "}");
 	}
@@ -2661,6 +2700,11 @@ test_sm_state_map2 ()
   tree y = build_global_decl ("y", integer_type_node);
   tree z = build_global_decl ("z", integer_type_node);
 
+  state_machine *sm = make_malloc_state_machine (NULL);
+  auto_delete_vec <state_machine> checkers;
+  checkers.safe_push (sm);
+  extrinsic_state ext_state (checkers);
+
   /* Test setting states on svalue_id instances directly.  */
   {
     region_model2_manager mgr;
@@ -2669,21 +2713,21 @@ test_sm_state_map2 ()
     const svalue2 *y_sval = model.get_rvalue (y, NULL);
     const svalue2 *z_sval = model.get_rvalue (z, NULL);
 
-    sm_state_map2 map;
+    sm_state_map2 map (*sm, 0);
     ASSERT_TRUE (map.is_empty_p ());
-    ASSERT_EQ (map.get_state (x_sval), 0);
+    ASSERT_EQ (map.get_state (x_sval, ext_state), 0);
 
-    map.impl_set_state (x_sval, 42, z_sval);
-    ASSERT_EQ (map.get_state (x_sval), 42);
+    map.impl_set_state (x_sval, 42, z_sval, ext_state);
+    ASSERT_EQ (map.get_state (x_sval, ext_state), 42);
     ASSERT_EQ (map.get_origin (x_sval), z_sval);
-    ASSERT_EQ (map.get_state (y_sval), 0);
+    ASSERT_EQ (map.get_state (y_sval, ext_state), 0);
     ASSERT_FALSE (map.is_empty_p ());
 
-    map.impl_set_state (y_sval, 0, z_sval);
-    ASSERT_EQ (map.get_state (y_sval), 0);
+    map.impl_set_state (y_sval, 0, z_sval, ext_state);
+    ASSERT_EQ (map.get_state (y_sval, ext_state), 0);
 
-    map.impl_set_state (x_sval, 0, z_sval);
-    ASSERT_EQ (map.get_state (x_sval), 0);
+    map.impl_set_state (x_sval, 0, z_sval, ext_state);
+    ASSERT_EQ (map.get_state (x_sval, ext_state), 0);
     ASSERT_TRUE (map.is_empty_p ());
   }
 
@@ -2695,18 +2739,18 @@ test_sm_state_map2 ()
     const svalue2 *y_sval = model.get_rvalue (y, NULL);
     const svalue2 *z_sval = model.get_rvalue (z, NULL);
 
-    sm_state_map2 map;
+    sm_state_map2 map (*sm, 0);
     ASSERT_TRUE (map.is_empty_p ());
-    ASSERT_EQ (map.get_state (x_sval), 0);
-    ASSERT_EQ (map.get_state (y_sval), 0);
+    ASSERT_EQ (map.get_state (x_sval, ext_state), 0);
+    ASSERT_EQ (map.get_state (y_sval, ext_state), 0);
 
     model.add_constraint (x, EQ_EXPR, y, NULL);
 
     /* Setting x to a state should also update y, as they
        are in the same equivalence class.  */
-    map.set_state (&model, x_sval, 5, z_sval);
-    ASSERT_EQ (map.get_state (x_sval), 5);
-    ASSERT_EQ (map.get_state (y_sval), 5);
+    map.set_state (&model, x_sval, 5, z_sval, ext_state);
+    ASSERT_EQ (map.get_state (x_sval, ext_state), 5);
+    ASSERT_EQ (map.get_state (y_sval, ext_state), 5);
     ASSERT_EQ (map.get_origin (x_sval), z_sval);
     ASSERT_EQ (map.get_origin (y_sval), z_sval);
   }
@@ -2718,28 +2762,28 @@ test_sm_state_map2 ()
     const svalue2 *y_sval = model.get_rvalue (y, NULL);
     const svalue2 *z_sval = model.get_rvalue (z, NULL);
 
-    sm_state_map2 map0;
-    sm_state_map2 map1;
-    sm_state_map2 map2;
+    sm_state_map2 map0 (*sm, 0);
+    sm_state_map2 map1 (*sm, 0);
+    sm_state_map2 map2 (*sm, 0);
 
     ASSERT_EQ (map0.hash (), map1.hash ());
     ASSERT_EQ (map0, map1);
 
-    map1.impl_set_state (y_sval, 5, z_sval);
+    map1.impl_set_state (y_sval, 5, z_sval, ext_state);
     ASSERT_NE (map0.hash (), map1.hash ());
     ASSERT_NE (map0, map1);
 
     /* Make the same change to map2.  */
-    map2.impl_set_state (y_sval, 5, z_sval);
+    map2.impl_set_state (y_sval, 5, z_sval, ext_state);
     ASSERT_EQ (map1.hash (), map2.hash ());
     ASSERT_EQ (map1, map2);
   }
 
   /* Equality and hashing shouldn't depend on ordering.  */
   {
-    sm_state_map2 map0;
-    sm_state_map2 map1;
-    sm_state_map2 map2;
+    sm_state_map2 map0 (*sm, 0);
+    sm_state_map2 map1 (*sm, 0);
+    sm_state_map2 map2 (*sm, 0);
 
     ASSERT_EQ (map0.hash (), map1.hash ());
     ASSERT_EQ (map0, map1);
@@ -2750,13 +2794,13 @@ test_sm_state_map2 ()
     const svalue2 *y_sval = model.get_rvalue (y, NULL);
     const svalue2 *z_sval = model.get_rvalue (z, NULL);
 
-    map1.impl_set_state (x_sval, 2, NULL);
-    map1.impl_set_state (y_sval, 3, NULL);
-    map1.impl_set_state (z_sval, 2, NULL);
+    map1.impl_set_state (x_sval, 2, NULL, ext_state);
+    map1.impl_set_state (y_sval, 3, NULL, ext_state);
+    map1.impl_set_state (z_sval, 2, NULL, ext_state);
 
-    map2.impl_set_state (z_sval, 2, NULL);
-    map2.impl_set_state (y_sval, 3, NULL);
-    map2.impl_set_state (x_sval, 2, NULL);
+    map2.impl_set_state (z_sval, 2, NULL, ext_state);
+    map2.impl_set_state (y_sval, 3, NULL, ext_state);
+    map2.impl_set_state (x_sval, 2, NULL, ext_state);
 
     ASSERT_EQ (map1.hash (), map2.hash ());
     ASSERT_EQ (map1, map2);
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 8ccb00677e8..376174fb382 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -54,6 +54,7 @@ public:
   void dump () const;
 
   engine2 *get_engine2 () const { return m_engine2; }
+  region_model2_manager *get_model_manager () const;
 
 private:
   /* The state machines.  */
@@ -254,14 +255,14 @@ public:
   typedef hash_map <const svalue2 *, entry_t> map_t;
   typedef map_t::iterator iterator_t;
 
-  sm_state_map2 ();
+  sm_state_map2 (const state_machine &sm, int m_sm_idx);
 
   sm_state_map2 *clone () const;
 
-  void print (const state_machine &sm, const region_model2 *model,
+  void print (const region_model2 *model,
 	      bool simple, bool multiline,
 	      pretty_printer *pp) const;
-  void dump (const state_machine &sm, bool simple) const;
+  void dump (bool simple) const;
 
   bool is_empty_p () const;
 
@@ -273,32 +274,33 @@ public:
     return !(*this == other);
   }
 
-  state_machine::state_t get_state (const svalue2 *sval) const;
+  state_machine::state_t get_state (const svalue2 *sval,
+				    const extrinsic_state &ext_state) const;
   const svalue2 *get_origin (const svalue2 *sval) const;
 
   void set_state (region_model2 *model,
 		  const svalue2 *sval,
 		  state_machine::state_t state,
-		  const svalue2 *origin);
+		  const svalue2 *origin,
+		  const extrinsic_state &ext_state);
   bool set_state (const equiv_class2 &ec,
 		  state_machine::state_t state,
-		  const svalue2 *origin);
+		  const svalue2 *origin,
+		  const extrinsic_state &ext_state);
   bool impl_set_state (const svalue2 *sval,
 		       state_machine::state_t state,
-		       const svalue2 *origin);
+		       const svalue2 *origin,
+		       const extrinsic_state &ext_state);
 
   void set_global_state (state_machine::state_t state);
   state_machine::state_t get_global_state () const;
 
   void purge_for_unknown_fncall (const exploded_graph &eg,
-				 const state_machine &sm,
 				 const gcall *call, tree fndecl,
 				 region_model2 *new_model,
 				 region_model2_context *ctxt);
 
-  void on_svalue2_leak (const state_machine &sm,
-			int sm_idx,
-			const svalue2 *sval,
+  void on_svalue2_leak (const svalue2 *sval,
 			impl_region_model2_context *ctxt);
 #if 0
   void on_inherited_svalue (svalue_id parent_sid,
@@ -310,7 +312,8 @@ public:
 
   void on_unknown_change (const state_machine &sm,
 			  const svalue2 *sval,
-			  bool is_mutable);
+			  bool is_mutable,
+			  const extrinsic_state &ext_state);
 
 #if 0
   void validate (const state_machine &sm, int num_svalues) const;
@@ -319,6 +322,8 @@ public:
   iterator_t end () const { return m_map.end (); }
 
 private:
+  const state_machine &m_sm;
+  int m_sm_idx;
   map_t m_map;
   state_machine::state_t m_global_state;
 };
@@ -414,7 +419,7 @@ public:
     FOR_EACH_VEC_ELT (m_checker_states2, i, smap)
       {
 	const state_machine &sm = ext_state.get_sm (i);
-	if (!sm.can_purge_p (smap->get_state (sval)))
+	if (!sm.can_purge_p (smap->get_state (sval, ext_state)))
 	  return false;
       }
     return true;
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 4579ebd7c5a..8fc7c04a635 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -1216,6 +1216,25 @@ region2::get_base_region () const
   return iter;
 }
 
+/* Return true if get_base_region() == this for this region.  */
+
+bool
+region2::base_region_p () const
+{
+  switch (get_kind ())
+    {
+    /* Region kinds representing a descendent of a base region.  */
+    case RK_FIELD:
+    case RK_ELEMENT:
+    case RK_OFFSET:
+    case RK_CAST:
+      return false;
+
+    default:
+      return true;
+    }
+}
+
 /* Return true if this region is ELDER or one of its descendents.  */
 // FIXME: unit test
 
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index c0520d5c985..566fc512879 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -1250,6 +1250,7 @@ public:
 
   const region2 *get_parent_region () const { return m_parent; }
   const region2 *get_base_region () const;
+  bool base_region_p () const;
   bool descendent_of_p (const region2 *elder) const;
   const frame_region2 *maybe_get_frame_region () const;
 
-- 
2.26.2

