From 8253067d9f9a602f7cb8352f55cf201bf7e4fd5a Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 1 Nov 2019 17:25:53 -0400
Subject: [PATCH 101/119] FIXME: don't report on leaks when equal values don't
 leak

---
 gcc/analyzer/engine.cc        | 32 ++++++++++++++++++++++++++++++++
 gcc/analyzer/exploded-graph.h |  1 +
 gcc/analyzer/program-state.cc |  6 ++----
 gcc/analyzer/region-model.cc  | 30 +++++++++++++++++++++++++-----
 gcc/analyzer/region-model.h   |  3 +++
 5 files changed, 63 insertions(+), 9 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index e38c672..c043001 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -488,6 +488,7 @@ void
 impl_region_model_context::on_state_leak (const state_machine &sm,
 					  int sm_idx,
 					  svalue_id sid,
+					  svalue_id first_unused_sid,
 					  const svalue_id_map &map,
 					  state_machine::state_t state)
 {
@@ -498,6 +499,37 @@ impl_region_model_context::on_state_leak (const state_machine &sm,
      up the old state of the sid.  */
   gcc_assert (m_old_state);
 
+  /* Don't report on sid leaking if it's equal to one of the used sids.
+     For example, given:
+       some_non_trivial_expression = malloc (sizeof (struct foo));
+     we have:
+       _1 = malloc;                         (void *)
+       some_non_trivial_expression = _1;    (struct foo *)
+     and at leak-detection time we may have:
+       sv5: {type: ‘struct foo *’, &r3}  (used)
+       sv6: {type: ‘void *’, &r3}         (unused)
+     where both point to the same region.  We don't want to report a
+     leak of sv6, so we reject the report due to its equality with sv5.  */
+  gcc_assert (m_new_state);
+  gcc_assert (!first_unused_sid.null_p ());
+  for (int i = 0; i < first_unused_sid.as_int (); i++)
+    {
+      svalue_id used_sid = svalue_id::from_int (i);
+
+      /* Use the "_without_cm" form of eval_condition, since
+	 we're half-way through purging - we don't want to introduce new
+	 equivalence classes into the constraint_manager for "sid" and
+	 for each of the used_sids.  */
+      const region_model &rm = *m_new_state->m_region_model;
+      tristate eq = rm.eval_condition_without_cm (sid, EQ_EXPR, used_sid);
+      if (eq.is_true ())
+	{
+	  log ("rejecting leak of sv%i due to equality with sv%i",
+	       sid.as_int (), used_sid.as_int ());
+	  return;
+	}
+    }
+
   /* SID has leaked within the new state: no regions use it.
      We need to convert it back to a tree, but since no regions use it, we
      have to use MAP to convert it back to an svalue_id within the old state.
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index c829506..a7e058a 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -84,6 +84,7 @@ class impl_region_model_context : public region_model_context, public log_user
   void on_state_leak (const state_machine &sm,
 		      int sm_idx,
 		      svalue_id sid,
+		      svalue_id first_unused_sid,
 		      const svalue_id_map &map,
 		      state_machine::state_t state);
 
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index bf0c297..3e2ed3d 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -369,10 +369,8 @@ sm_state_map::on_svalue_purge (const state_machine &sm,
 	  //gcc_unreachable ();
 
 	  if (!sm.can_purge_p (e.m_state))
-	    {
-	      //gcc_unreachable ();
-	      ctxt->on_state_leak (sm, sm_idx, sid, map, e.m_state);
-	    }
+	    ctxt->on_state_leak (sm, sm_idx, sid, first_unused_sid, map,
+				 e.m_state);
 
 #if 0
 	  // FIXME: we need a tree, an snode, a stmt, and an sm_ctxt
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 6299ab8..affc482 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -5196,6 +5196,27 @@ region_model::eval_condition (svalue_id lhs_sid,
 			      enum tree_code op,
 			      svalue_id rhs_sid) const
 {
+  tristate ts = eval_condition_without_cm (lhs_sid, op, rhs_sid);
+
+  if (ts.is_known ())
+    return ts;
+
+  /* Otherwise, try constraints.  */
+  return m_constraints->eval_condition (lhs_sid, op, rhs_sid);
+}
+
+/* Determine what is known about the condition "LHS_SID OP RHS_SID" within
+   this model, without resorting to the constraint_manager.
+
+   This is exposed so that impl_region_model_context::on_state_leak can
+   check for equality part-way through region_model::purge_unused_svalues
+   without risking creating new ECs.  */
+
+tristate
+region_model::eval_condition_without_cm (svalue_id lhs_sid,
+					 enum tree_code op,
+					 svalue_id rhs_sid) const
+{
   svalue *lhs = get_svalue (lhs_sid);
   svalue *rhs = get_svalue (rhs_sid);
   gcc_assert (lhs);
@@ -5232,7 +5253,7 @@ region_model::eval_condition (svalue_id lhs_sid,
 	    tristate res = region_svalue::eval_condition (lhs_ptr, op, rhs_ptr);
 	    if (res.is_known ())
 	      return res;
-	    /* Otherwise, try constraints.  */
+	    /* Otherwise, only known through constraints.  */
 	  }
 
       /* If we have a pair of constants, compare them.  */
@@ -5269,8 +5290,7 @@ region_model::eval_condition (svalue_id lhs_sid,
 	    }
     }
 
-  /* Otherwise, try constraints.  */
-  return m_constraints->eval_condition (lhs_sid, op, rhs_sid);
+  return tristate::TS_UNKNOWN;
 }
 
 /* Attempt to add the constraint "LHS OP RHS" to this region_model.
@@ -6130,8 +6150,8 @@ region_model::purge_unused_svalues (purge_stats *stats,
       logger->end_log_line ();
     }
 
-  /* Notify any client about the remapping and pending deletion.  */
-  // FIXME: potentially this could trigger leak warnings
+  /* Notify any client about the remapping and pending deletion.
+     Potentially this could trigger leak warnings.  */
   if (ctxt)
     {
       ctxt->remap_svalue_ids (map);
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 72edc3a..138046e 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -1736,6 +1736,9 @@ class region_model
   tristate eval_condition (svalue_id lhs,
 			   enum tree_code op,
 			   svalue_id rhs) const;
+  tristate eval_condition_without_cm (svalue_id lhs,
+				      enum tree_code op,
+				      svalue_id rhs) const;
   tristate eval_condition (tree lhs,
 			   enum tree_code op,
 			   tree rhs,
-- 
1.8.5.3

