From 48103387f763d7ed92e326aebcf22a77a4154c1c Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 2 Apr 2019 16:01:11 -0400
Subject: [PATCH 175/217] FIXME: fix false memory leak report

---
 gcc/analyzer/constraints.cc              | 68 ++++++++++++++++++++++++++++++++
 gcc/analyzer/constraints.h               |  3 ++
 gcc/analyzer/engine.cc                   | 11 ++++--
 gcc/testsuite/gcc.dg/analyzer/malloc-1.c | 19 +++++++--
 4 files changed, 94 insertions(+), 7 deletions(-)

diff --git a/gcc/analyzer/constraints.cc b/gcc/analyzer/constraints.cc
index e12882a..4f1ce65 100644
--- a/gcc/analyzer/constraints.cc
+++ b/gcc/analyzer/constraints.cc
@@ -973,6 +973,74 @@ path_state::eval_condition (equiv_class_id lhs_ec,
   return tristate (tristate::TS_UNKNOWN);
 }
 
+/* Mark EC_ID as having escaped; walk other ECs that haven't yet been marked
+   as escaped, and determine if they've now escaped, if so, mark them,
+   recursively.
+   e.g. if we know "ptr" escapes, then "ptr->foo" escapes.  */
+
+void
+path_state::recursively_mark_as_escaping (equiv_class_id ec_id)
+{
+  equiv_class &ec = ec_id.get_obj (*this);
+  if (ec.m_escaped)
+    {
+      /* Nothing to do.  */
+      return;
+    }
+
+  ec.m_escaped = true;
+
+  /* Look for other ECs that haven't yet been marked as escaped.  */
+  int other_ec_idx;
+  equiv_class *other_ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, other_ec_idx, other_ec)
+    {
+      if (other_ec->m_escaped)
+	continue;
+
+      int var_idx;
+      path_var *v;
+      FOR_EACH_VEC_ELT (other_ec->m_vars, var_idx, v)
+	{
+	  gcc_assert (!CONSTANT_CLASS_P (v->m_tree));
+	  if (calc_escaped_p (*v))
+	    {
+	      recursively_mark_as_escaping (other_ec_idx);
+	      break;
+	    }
+	}
+    }
+
+  // TODO: selftests
+  // TODO: selftest involving a cycle
+}
+
+/* Determine if PV (or something that points to it) has been marked as
+   having escaped.  */
+
+bool
+path_state::calc_escaped_p (path_var pv)
+{
+  switch (TREE_CODE (pv.m_tree))
+    {
+    case COMPONENT_REF:
+    case MEM_REF:
+      return calc_escaped_p (path_var (TREE_OPERAND (pv.m_tree, 0),
+				       pv.m_stack_depth));
+    case SSA_NAME:
+      {
+	equiv_class_id ec_id (-1);
+	if (get_equiv_class (pv, &ec_id))
+	  return ec_id.get_obj (*this).m_escaped;
+      }
+      break;
+    default:
+      return false;
+    }
+
+  return false;
+}
+
 // FIXME:
 void
 debug (const path_state &ps)
diff --git a/gcc/analyzer/constraints.h b/gcc/analyzer/constraints.h
index 69a731b..0c2e032 100644
--- a/gcc/analyzer/constraints.h
+++ b/gcc/analyzer/constraints.h
@@ -220,6 +220,9 @@ public:
   /* For implementing "__analyzer_eval".  */
   tristate dump_condition (tree lhs, enum tree_code op, tree rhs);
 
+  void recursively_mark_as_escaping (equiv_class_id ec_id);
+  bool calc_escaped_p (path_var pv);
+
   auto_vec<equiv_class *> m_equiv_classes;
   auto_vec<constraint> m_constraints;
   int m_stack_depth;
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index cfb5efa..bb1b2ec 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -2568,10 +2568,12 @@ engine::detect_leaks (const supernode *node, const sm_instance &sm_inst,
     tree result = DECL_RESULT (node->m_fun->decl);
     if (result)
       {
+	log ("new_ps before escapes: %s", new_ps.to_str ().c_str ());
 	// FIXME: stack depth:
-	equiv_class_id ec_id = new_ps.get_or_add_equiv_class (path_var (result, 0));
-	equiv_class &ec = ec_id.get_obj (new_ps);
-	ec.m_escaped = true;
+	equiv_class_id ec_id
+	  = new_ps.get_or_add_equiv_class (path_var (result, 0));
+	new_ps.recursively_mark_as_escaping (ec_id);
+	log ("new_ps after escapes: %s", new_ps.to_str ().c_str ());
       }
   }
 
@@ -2588,7 +2590,8 @@ engine::detect_leaks (const supernode *node, const sm_instance &sm_inst,
       /* Did this var's equivalence class escape?  If so, don't complain
 	 about it leaking.  */
       // FIXME: stack depth:
-      equiv_class_id ec_id = new_ps.get_or_add_equiv_class (path_var ((*iter).m_var, 0));
+      equiv_class_id ec_id
+	= new_ps.get_or_add_equiv_class (path_var ((*iter).m_var, 0));
       equiv_class &ec = ec_id.get_obj (new_ps);
       if (get_logger ())
 	log ("within %s", ec.to_str ().c_str ());
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
index 3de1c39..bbee1e8 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
@@ -306,8 +306,6 @@ struct holder
   int *m_ptr;
 };
 
-// TODO: this one is showing false positive
-#if 0
 struct holder *test_29 (void)
 {
   struct holder *res = (struct holder *)malloc (sizeof (struct holder));
@@ -316,7 +314,6 @@ struct holder *test_29 (void)
   res->m_ptr = (int *)malloc (sizeof (int));
   return res;
 }
-#endif
 
 /* TODO: this one is showing two leaks:
      warning: leak of '<unknown>'
@@ -329,3 +326,19 @@ void test_30 (void)
   tmp.m_ptr = (int *)malloc (sizeof (int));
 }
 #endif
+
+#if 0
+void test_31 (void *p)
+{
+  void *q = realloc (p, 1024);
+  free (p); /* FIXME: this is a double-free.  */
+  free (q);
+}
+
+void test_32 (void)
+{
+  void *p = malloc (64);
+  p = realloc (p, 1024); /* FIXME: this leaks if it fails.  */
+  free (p);
+}
+#endif
-- 
1.8.5.3

