From 59db63bcd107589de67476271fcbd98f0949f0fc Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 12 May 2020 18:38:58 -0400
Subject: [PATCH 116/237] FIXME: move call-handling to impl_* subroutines

---
 gcc/analyzer/analyzer.h       |   1 +
 gcc/analyzer/region-model2.cc | 247 ++++++++++++++++++++++++----------
 gcc/analyzer/region-model2.h  |   8 ++
 3 files changed, 184 insertions(+), 72 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index c15cd687d55..68bbf0c4d57 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -76,6 +76,7 @@ class store2_manager;
 class region_model2;
 class region_model2_context;
   class impl_region_model2_context;
+class call_details;
 class constraint_manager2;
 class equiv_class2;
 
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index dcd0a4ab38d..c7bba5625ab 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -4118,6 +4118,56 @@ region_model2::on_gcall (const supernode *snode,
 }
 #endif
 
+/* Helper class for handling calls.  */
+
+class call_details
+{
+public:
+  call_details (const gcall *call, region_model2 *model,
+		region_model2_context *ctxt)
+    : m_call (call), m_model (model), m_ctxt (ctxt),
+      m_lhs_type (NULL_TREE), m_lhs_region (NULL)
+  {
+    m_lhs_type = NULL_TREE;
+    if (tree lhs = gimple_call_lhs (call))
+      {
+	m_lhs_region = model->get_lvalue (lhs, ctxt);
+	m_lhs_type = TREE_TYPE (lhs);
+      }
+  }
+
+  region_model2_context *get_ctxt () const { return m_ctxt; }
+
+  tree get_lhs_type () const { return m_lhs_type; }
+  const region2 *get_lhs_region () const { return m_lhs_region; }
+
+  bool maybe_set_lhs (const svalue2 *result) const
+  {
+    gcc_assert (result);
+    if (m_lhs_region)
+      {
+	m_model->set_value (m_lhs_region, result, m_ctxt);
+	return true;
+      }
+    else
+      return false;
+  }
+
+  tree get_arg_tree (unsigned idx) const { return gimple_call_arg (m_call, idx); }
+  const svalue2 *get_arg_svalue2 (unsigned idx) const
+  {
+    tree arg = get_arg_tree (idx);
+    return m_model->get_rvalue (arg, m_ctxt);
+  }
+
+private:
+  const gcall *m_call;
+  region_model2 *m_model;
+  region_model2_context *m_ctxt;
+  tree m_lhs_type;
+  const region2 *m_lhs_region;
+};
+
 /* Update this model for the CALL stmt, using CTXT to report any
    diagnostics - the first half.
 
@@ -4132,14 +4182,6 @@ region_model2::on_gcall (const supernode *snode,
 bool
 region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
 {
-  const region2 *lhs_reg;
-  tree lhs_type = NULL_TREE;
-  if (tree lhs = gimple_call_lhs (call))
-    {
-      lhs_reg = get_lvalue (lhs, ctxt);
-      lhs_type = TREE_TYPE (lhs);
-    }
-
   /* Check for uses of poisoned values.
      For now, special-case "free", to avoid warning about "use-after-free"
      when "double free" would be more precise.  */
@@ -4153,77 +4195,25 @@ region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
 
   if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
     {
-      /* Get any left-hand side for the destination of the result.  */
-      const region2 *lhs_reg = NULL;
-      tree lhs_type = NULL_TREE;
-      if (tree lhs = gimple_call_lhs (call))
-	{
-	  lhs_reg = get_lvalue (lhs, ctxt);
-	  lhs_type = TREE_TYPE (lhs);
-	}
+      call_details cd (call, this, ctxt);
 
+      /* FIXME: should we be returning bools from these,
+	 or dropping through?
+	 Existing behavior was to return "true", but that looks wrong.  */
       if (is_named_call_p (callee_fndecl, "malloc", call, 1))
-	{
-	  tree size_arg = gimple_call_arg (call, 0);
-	  const svalue2 *size_sval = get_rvalue (size_arg, ctxt);
-	  const region2 *new_reg = create_region_for_heap_alloc (size_sval);
-	  if (lhs_reg)
-	    {
-	      const 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;
-	}
+	return impl_call_malloc (cd);
+      else if (is_named_call_p (callee_fndecl, "calloc", call, 2))
+	return impl_call_calloc (cd);
       else if (is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
-	{
-	  tree size_arg = gimple_call_arg (call, 0);
-	  const svalue2 *size_sval = get_rvalue (size_arg, ctxt);
-	  const region2 *new_reg = create_region_for_alloca (size_sval);
-	  if (lhs_reg)
-	    {
-	      const svalue2 *ptr_sval = m_mgr->get_ptr_svalue2 (lhs_type, new_reg);
-	      set_value (lhs_reg, ptr_sval, ctxt);
-	    }
-	  return true;
-	}
+	return impl_call_alloca (cd);
       else if (gimple_call_builtin_p (call, BUILT_IN_EXPECT)
 	       || gimple_call_builtin_p (call, BUILT_IN_EXPECT_WITH_PROBABILITY)
 	       || gimple_call_internal_p (call, IFN_BUILTIN_EXPECT))
-	{
-	  /* __builtin_expect's return value is its initial argument.  */
-	  if (lhs_reg)
-	    {
-	      tree initial_arg = gimple_call_arg (call, 0);
-	      const svalue2 *sval = get_rvalue (initial_arg, ctxt);
-	      set_value (lhs_reg, sval, ctxt);
-	    }
-	  return false;
-	}
+	return impl_call_builtin_expect (cd);
+      else if (is_named_call_p (callee_fndecl, "memset", call, 3))
+	impl_call_memset (cd);
       else if (is_named_call_p (callee_fndecl, "strlen", call, 1))
-	{
-	  tree arg = gimple_call_arg (call, 0);
-	  const svalue2 *arg_sval = get_rvalue (arg, ctxt);
-	  const region2 *buf_reg = deref_rvalue (arg_sval, ctxt);
-	  if (const string_region2 *str_reg
-		= buf_reg->dyn_cast_string_region2 ())
-	    {
-	      tree str_cst = str_reg->get_string_cst ();
-	      /* TREE_STRING_LENGTH is sizeof, not strlen.  */
-	      int sizeof_cst = TREE_STRING_LENGTH (str_cst);
-	      int strlen_cst = sizeof_cst - 1;
-	      tree t_cst = build_int_cst (lhs_type, strlen_cst);
-	      const svalue2 *result_sval
-		= m_mgr->get_or_create_constant_svalue2 (t_cst);
-	      set_value (lhs_reg, result_sval, ctxt);
-	      return true;
-	    }
-	  /* Otherwise an unknown value.  */
-	  return true;
-	}
+	impl_call_strlen (cd);
       else if (is_named_call_p (callee_fndecl,
 				"__analyzer_dump_num_heap_region2s", call, 0))
 	{
@@ -4323,6 +4313,119 @@ region_model2::on_call_post (const gcall *call,
 }
 #endif
 
+/* Handling for the known behavior of various specific functions.  */
+
+/* Handle the on_call_pre part of "alloca".  */
+
+bool
+region_model2::impl_call_alloca (const call_details &cd)
+{
+  const svalue2 *size_sval = cd.get_arg_svalue2 (0);
+  const region2 *new_reg = create_region_for_alloca (size_sval);
+  const svalue2 *ptr_sval
+    = m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
+  cd.maybe_set_lhs (ptr_sval);
+  return true;
+}
+
+/* Handle the on_call_pre part of "__builtin_expect" etc.  */
+
+bool
+region_model2::impl_call_builtin_expect (const call_details &cd)
+{
+  /* __builtin_expect's return value is its initial argument.  */
+  const svalue2 *sval = cd.get_arg_svalue2 (0);
+  cd.maybe_set_lhs (sval);
+  return false;
+}
+
+/* Handle the on_call_pre part of "calloc".  */
+
+bool
+region_model2::impl_call_calloc (const call_details &cd)
+{
+  const svalue2 *nmemb_sval = cd.get_arg_svalue2 (0);
+  const svalue2 *size_sval = cd.get_arg_svalue2 (1);
+  /* FIXME: check for overflow here?  */
+  const svalue2 *prod_sval
+    = m_mgr->get_or_create_binop (size_type_node, MULT_EXPR,
+				  nmemb_sval, size_sval);
+  const region2 *new_reg = create_region_for_heap_alloc (prod_sval);
+  zero_fill_region (new_reg);
+  if (cd.get_lhs_type ())
+    {
+      const svalue2 *ptr_sval
+	= m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
+      cd.maybe_set_lhs (ptr_sval);
+    }
+  else
+    {
+      // FIXME: report a leak here?
+    }
+  return true;
+}
+
+/* Handle the on_call_pre part of "malloc".  */
+
+bool
+region_model2::impl_call_malloc (const call_details &cd)
+{
+  const svalue2 *size_sval = cd.get_arg_svalue2 (0);
+  const region2 *new_reg = create_region_for_heap_alloc (size_sval);
+  if (cd.get_lhs_type ())
+    {
+      const svalue2 *ptr_sval
+	= m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
+      cd.maybe_set_lhs (ptr_sval);
+    }
+  else
+    {
+      // FIXME: report a leak here?
+    }
+  return true;
+}
+
+/* Handle the on_call_pre part of "memset".  */
+
+bool
+region_model2::impl_call_memset (const call_details &cd)
+{
+  const svalue2 *dest = cd.get_arg_svalue2 (0);
+  const svalue2 *fill_value = cd.get_arg_svalue2 (1);
+  const svalue2 *num_bytes = cd.get_arg_svalue2 (2);
+  // TODO
+  //gcc_unreachable ();
+  return false;
+}
+
+/* Handle the on_call_pre part of "strlen".  */
+
+bool
+region_model2::impl_call_strlen (const call_details &cd)
+{
+  region_model2_context *ctxt = cd.get_ctxt ();
+  const svalue2 *arg_sval = cd.get_arg_svalue2 (0);
+  const region2 *buf_reg = deref_rvalue (arg_sval, ctxt);
+  if (const string_region2 *str_reg
+      = buf_reg->dyn_cast_string_region2 ())
+    {
+      tree str_cst = str_reg->get_string_cst ();
+      /* TREE_STRING_LENGTH is sizeof, not strlen.  */
+      int sizeof_cst = TREE_STRING_LENGTH (str_cst);
+      int strlen_cst = sizeof_cst - 1;
+      if (cd.get_lhs_type ())
+	{
+	  tree t_cst = build_int_cst (cd.get_lhs_type (), strlen_cst);
+	  const svalue2 *result_sval
+	    = m_mgr->get_or_create_constant_svalue2 (t_cst);
+	  cd.maybe_set_lhs (result_sval);
+	  return true;
+	}
+    }
+  /* Otherwise an unknown value.  */
+  return true;
+}
+
 /* Helper class for region_model2::handle_unrecognized_call, for keeping
    track of all region2s that are reachable, and, of those, which are
    mutable.
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 518faf98937..4bd2b8e8629 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -1891,6 +1891,14 @@ class region_model2
   void on_call_post (const gcall *stmt,
 		     bool unknown_side_effects,
 		     region_model2_context *ctxt);
+
+  /* Specific handling for on_call_pre.  */
+  bool impl_call_alloca (const call_details &cd);
+  bool impl_call_builtin_expect (const call_details &cd);
+  bool impl_call_calloc (const call_details &cd);
+  bool impl_call_malloc (const call_details &cd);
+  bool impl_call_memset (const call_details &cd);
+  bool impl_call_strlen (const call_details &cd);
 #endif
   void handle_unrecognized_call (const gcall *call,
 				 region_model2_context *ctxt);
-- 
2.26.2

