From 111dd2b8253acc8471d74e672dadcc12cb779041 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 22 Apr 2020 09:39:52 -0400
Subject: [PATCH 069/237] FIXME: WIP on malloc and alloca

---
 gcc/analyzer/region-model2.cc | 202 ++++++++++++++++++++++++++++++++--
 gcc/analyzer/region-model2.h  |  90 ++++++++++++---
 2 files changed, 268 insertions(+), 24 deletions(-)

diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 84a168756a8..f9125d68dfd 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -2428,6 +2428,16 @@ heap_region2::compare_fields (const heap_region2 &) const
 }
 #endif
 
+void
+heap_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+    }
+  else
+    pp_string (pp, "heap_region2()");
+}
+
 /* class root_region2 : public region2.  */
 
 /* root_region2's default ctor.  */
@@ -2895,12 +2905,37 @@ element_region2::add_to_hash (inchash::hash &hstate) const
   hstate.add_ptr (m_index);
 }
 
+/* class heap_allocated_region2 : public region2.  */
+
+void
+heap_allocated_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  // TODO:
+  if (simple)
+    pp_string (pp, "HEAP_ALLOCATED_REGION");
+  else
+    pp_string (pp, "heap_allocated_region2()");
+}
+
+/* class alloca_region2 : public region2.  */
+
+void
+alloca_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  // TODO:
+  if (simple)
+    pp_string (pp, "ALLOCA_REGION");
+  else
+    pp_string (pp, "alloca_region2()");
+}
+
 /* class region_model2_manager.  */
 
 region_model2_manager::region_model2_manager ()
 : m_next_region_id (0),
   m_root_region (alloc_region_id ()),
   m_stack_region (alloc_region_id (), &m_root_region),
+  m_heap_region (alloc_region_id (), &m_root_region),
   m_code_region (alloc_region_id (), &m_root_region),
   m_fndecls_map (),
   m_globals_region (alloc_region_id (), &m_root_region),
@@ -3097,6 +3132,32 @@ region_model2_manager::get_symbolic_region (svalue2 *sval)
   return const_cast <region2 *> (m_symbolic_regions.consolidate (reg));
 }
 
+/* FIXME.  */
+
+region2 *
+region_model2_manager::create_region_for_heap_alloc ()
+{
+  // FIXME: should this be a symbolic region??
+  region2 *reg
+    = new heap_allocated_region2 (alloc_region_id (), &m_heap_region);
+  m_managed_dynamic_regions.safe_push (reg);
+  // TODO: selftest; verify no leak under valgrind
+  return reg;
+  /* TODO: maybe have an ID to model the different allocators?
+     (are they different parent regions? or some kind of discriminant var?)  */
+}
+
+/* FIXME.  */
+
+region2 *
+region_model2_manager::create_region_for_alloca (frame_region2 *frame)
+{
+  region2 *reg = new alloca_region2 (alloc_region_id (), frame);
+  m_managed_dynamic_regions.safe_push (reg);
+  // TODO: selftest; verify no leak under valgrind
+  return reg;
+}
+
 /* class region_model2.  */
 
 /* FIXME: region_model2's default ctor.  */
@@ -3882,6 +3943,7 @@ region_model2::on_gcall (const supernode *snode,
 
   if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
     {
+      /* Get any left-hand side for the destination of the result.  */
       region2 *lhs_reg = NULL;
       tree lhs_type = NULL_TREE;
       if (tree lhs = gimple_call_lhs (call))
@@ -3892,17 +3954,30 @@ region_model2::on_gcall (const supernode *snode,
 
       if (is_named_call_p (callee_fndecl, "malloc", call, 1))
 	{
-	  // TODO: capture size as a svalue2?
-	  gcc_unreachable ();
-#if 0
-	  region2 *new_reg = add_new_malloc_region2 ();
+	  tree size_arg = gimple_call_arg (call, 0);
+	  svalue2 *size_sval = get_rvalue (size_arg, NULL);
+	  region2 *new_reg = create_region_for_heap_alloc (size_sval);
 	  if (lhs_reg)
 	    {
-	      svalue2 *ptr_sval
-		= get_or_create_ptr_svalue2 (lhs_type, new_reg);
+	      svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
+	      set_value (lhs_reg, ptr_sval, ctxt);
+	    }
+	  else
+	    {
+	      // FIXME: report a leak here?
+	    }
+	  return true;
+	}
+      else if (is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
+	{
+	  tree size_arg = gimple_call_arg (call, 0);
+	  svalue2 *size_sval = get_rvalue (size_arg, NULL);
+	  region2 *new_reg = create_region_for_alloca (size_sval);
+	  if (lhs_reg)
+	    {
+	      svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
 	      set_value (lhs_reg, ptr_sval, ctxt);
 	    }
-#endif
 	  return true;
 	}
     }
@@ -4706,6 +4781,20 @@ region_model2::get_rvalue_1 (path_var pv,
 	  return sval_binop;
 	}
 
+    /* Binary ops.  */
+    case MULT_EXPR:
+	{
+	  tree expr = pv.m_tree;
+	  tree arg0 = TREE_OPERAND (expr, 0);
+	  tree arg1 = TREE_OPERAND (expr, 1);
+	  svalue2 *arg0_sval = get_rvalue (arg0, ctxt);
+	  svalue2 *arg1_sval = get_rvalue (arg1, ctxt);
+	  svalue2 *sval_binop
+	    = m_mgr->get_or_create_binop (TREE_TYPE (expr), TREE_CODE (expr),
+					  arg0_sval, arg1_sval);
+	  return sval_binop;
+	}
+
     case COMPONENT_REF:
     case MEM_REF:
       {
@@ -6610,6 +6699,44 @@ region_model2::get_fndecl_for_call (const gcall *call,
   return NULL_TREE;
 }
 
+/* FIXME.  */
+
+region2 *
+region_model2::create_region_for_heap_alloc (svalue2 *size_in_bytes)
+{
+  region2 *reg = m_mgr->create_region_for_heap_alloc ();
+  record_dynamic_extents (reg, size_in_bytes);
+  return reg;
+}
+
+/* FIXME.  */
+
+region2 *
+region_model2::create_region_for_alloca (svalue2 *size_in_bytes)
+{
+  region2 *reg = m_mgr->create_region_for_alloca (m_current_frame);
+  record_dynamic_extents (reg, size_in_bytes);
+  return reg;
+}
+
+/* FIXME.  */
+
+void
+region_model2::record_dynamic_extents (region2 *reg, svalue2 *size_in_bytes)
+{
+#if 0
+  svalue2 *extents_sval = m_mgr->get_or_create_extents_svalue2 (reg);
+  add_constraint (extents_sval, EQ_EXPR, size_in_bytes);
+  /* TODO: This needs the ability to add constraints in the model based on an
+     svalue2.  */
+  /* TODO: should be capture where we got the extents from?  (so that we can
+     display that to the user)  Perhaps we can just walk backwards along
+     the exploded_path to find where the constraint was added.  */
+  /* FIXME: or should the dynamic extents instead be recorded in a special
+     data map? */
+#endif
+}
+
 /* struct model_merger.  */
 
 /* Dump a multiline representation of this merger to PP.  */
@@ -8543,6 +8670,65 @@ test_POINTER_PLUS_EXPR_then_MEM_REF ()
   m.set_value (mem_ref, int_42, NULL);
 }
 
+/* FIXME.  */
+
+static void
+test_malloc ()
+{
+  tree int_star = build_pointer_type (integer_type_node);
+  tree p = build_global_decl ("p", int_star);
+  tree n = build_global_decl ("n", integer_type_node);
+  tree n_times_4 = build2 (MULT_EXPR, size_type_node,
+			   n, build_int_cst (size_type_node, 4));
+
+  region_model2_manager mgr;
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+
+  /* "p = malloc (n * 4);".  */
+  svalue2 *size_sval = model.get_rvalue (n_times_4, &ctxt);
+  region2 *reg = model.create_region_for_heap_alloc (size_sval);
+  svalue2 *ptr = mgr.get_ptr_svalue2 (int_star, reg);
+  model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt);
+  // TODO: verify dynamic extents
+
+}
+
+/* FIXME.  */
+
+static void
+test_alloca ()
+{
+  auto_vec <tree> param_types;
+  tree fndecl = make_fndecl (integer_type_node,
+			     "test_fn",
+			     param_types);
+  allocate_struct_function (fndecl, true);
+
+
+  tree int_star = build_pointer_type (integer_type_node);
+  tree p = build_global_decl ("p", int_star);
+  tree n = build_global_decl ("n", integer_type_node);
+  tree n_times_4 = build2 (MULT_EXPR, size_type_node,
+			   n, build_int_cst (size_type_node, 4));
+
+  region_model2_manager mgr;
+  test_region_model2_context ctxt;
+  region_model2 model (&mgr);
+
+  /* Push stack frame.  */
+  region2 *frame_reg
+    = model.push_frame (DECL_STRUCT_FUNCTION (fndecl),
+			NULL, &ctxt);
+  /* "p = alloca (n * 4);".  */
+  svalue2 *size_sval = model.get_rvalue (n_times_4, &ctxt);
+  region2 *reg = model.create_region_for_alloca (size_sval);
+  svalue2 *ptr = mgr.get_ptr_svalue2 (int_star, reg);
+  model.set_value (model.get_lvalue (p, &ctxt), ptr, &ctxt);
+  // TODO: verify dynamic extents
+  // TODO: try popping the frame; verify that the alloca region is poisoned
+}
+
 /* Run all of the selftests within this file.  */
 
 void
@@ -8583,6 +8769,8 @@ analyzer_region_model2_cc_tests ()
   test_array ();
   test_mem_ref ();
   test_POINTER_PLUS_EXPR_then_MEM_REF ();
+  test_malloc ();
+  test_alloca ();
 }
 
 } // namespace selftest
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 1b6442855e9..f01541de334 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -753,7 +753,9 @@ namespace ana {
      label_region (RK_LABEL)
      symbolic_region (RK_SYMBOLIC)
      field_region2 (RK_FIELD)
-     element_region2 (RK_ELEMENT).  */
+     element_region2 (RK_ELEMENT)
+     heap_allocated_region2 (RK_HEAP_ALLOCATED)
+     alloca_region2 (RK_ALLOCA).  */
 
 /* Abstract base class representing a chunk of memory.
 
@@ -785,7 +787,9 @@ public:
    RK_SYMBOLIC,
    RK_DECL,
    RK_FIELD,
-   RK_ELEMENT
+   RK_ELEMENT,
+   RK_HEAP_ALLOCATED,
+   RK_ALLOCA
   };
   static const char *kind_to_str (enum kind);
 
@@ -1230,8 +1234,6 @@ is_a_helper <stack_region2 *>::test (region2 *reg)
   return reg->get_kind () == region2::RK_STACK;
 }
 
-#if 0
-
 namespace ana {
 
 /* Concrete region2 subclass: a region2 within which region2s can be
@@ -1240,25 +1242,22 @@ namespace ana {
 class heap_region2 : public region2
 {
 public:
-  heap_region2 (region2 *parent)
-  : region2 (parent, NULL_TREE)
+  heap_region2 (unsigned id, region2 *parent)
+    : region2 (id, parent, NULL_TREE)
   {}
-  heap_region2 (const heap_region2 &other);
-
-  bool compare_fields (const heap_region2 &other) const;
 
 #if 0
-  region2 *clone () const FINAL OVERRIDE;
+  bool compare_fields (const heap_region2 &other) const;
 #endif
 
   enum kind get_kind () const FINAL OVERRIDE { return RK_HEAP; }
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 #if 0
   static bool can_merge_p (const heap_region2 *heap_a, region2_id heap_a_rid,
 			   const heap_region2 *heap_b, region2_id heap_b_rid,
 			   heap_region2 *merged_heap, region2_id merged_heap_rid,
 			   model_merger *merger);
 #endif
-
 };
 
 } // namespace ana
@@ -1271,8 +1270,6 @@ is_a_helper <heap_region2 *>::test (region2 *reg)
   return reg->get_kind () == region2::RK_HEAP;
 }
 
-#endif
-
 namespace ana {
 
 /* Concrete region2 subclass.  The root region2, containing all region2s
@@ -1286,10 +1283,6 @@ public:
 
   bool compare_fields (const root_region2 &other) const;
 
-#if 0
-  region2 *clone () const FINAL OVERRIDE;
-#endif
-
   enum kind get_kind () const FINAL OVERRIDE { return RK_ROOT; }
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 
@@ -1470,6 +1463,52 @@ private:
   svalue2 *m_index;
 };
 
+/* An untyped region dynamically allocated on the heap via "malloc"
+   or similar.  */
+// FIXME: ...but could be NULL
+
+class heap_allocated_region2 : public region2
+{
+public:
+  heap_allocated_region2 (unsigned id, region2 *parent)
+  : region2 (id, parent, NULL_TREE)
+  {}
+
+#if 0
+  bool compare_fields (const element_region2 &other) const;
+#endif
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_HEAP_ALLOCATED; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+protected:
+#if 0
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+#endif
+};
+
+/* An untyped region dynamically allocated on the stack via "alloca".  */
+
+class alloca_region2 : public region2
+{
+public:
+  alloca_region2 (unsigned id, frame_region2 *parent)
+  : region2 (id, parent, NULL_TREE)
+  {}
+
+#if 0
+  bool compare_fields (const element_region2 &other) const;
+#endif
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ALLOCA; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+protected:
+#if 0
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+#endif
+};
+
 /* A class responsible for owning and consolidating region2 and svalue2
    instances.    */
 
@@ -1509,10 +1548,17 @@ public:
 
   store2_manager *get_store2_manager () { return &m_store2_mgr; }
 
+  /* Dynamically-allocated region2 instances.
+     FIXME: for now, the number of these within the analysis can grow
+     arbitrarily.  They are still owned by the manager.  */
+  region2 *create_region_for_heap_alloc ();
+  region2 *create_region_for_alloca (frame_region2 *frame);
+
 private:
   unsigned m_next_region_id;
   root_region2 m_root_region;
   stack_region2 m_stack_region;
+  heap_region2 m_heap_region;
 
   /* svalue2 consolidation.  */
   typedef hash_map<tree, constant_svalue2 *> constants_map_t;
@@ -1554,6 +1600,11 @@ private:
   uniq_manager<region2> m_symbolic_regions;
 
   store2_manager m_store2_mgr;
+
+  /* "Dynamically-allocated" region2 instances.
+     FIXME: for now, the number of these within the analysis can grow
+     arbitrarily.  They are still owned by the manager.  */
+  auto_delete_vec<region2> m_managed_dynamic_regions;
 };
 
 /* A region_model2 encapsulates a representation of the state of memory, with
@@ -1703,7 +1754,11 @@ class region_model2
   tree maybe_get_constant (svalue2_id sid) const;
 
   region2_id add_new_malloc_region2 ();
+#endif
+  region2 *create_region_for_heap_alloc (svalue2 *size_in_bytes);
+  region2 *create_region_for_alloca (svalue2 *size_in_bytes);
 
+#if 0
   tree get_representative_tree (svalue2_id sid) const;
   path_var get_representative_path_var (region2_id rid) const;
   void get_path_vars_for_svalue2 (svalue2_id sid, vec<path_var> *out) const;
@@ -1809,6 +1864,7 @@ class region_model2
 				      bool *is_first);
 #endif
 
+  void record_dynamic_extents (region2 *reg, svalue2 *size_in_bytes);
 
   /* Storing this here to avoid passing it around everywhere.  */
   region_model2_manager *const m_mgr;
-- 
2.26.2

