public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/arsenic/heads/polymorphic_call)] analyzer: make analyer detect calls via function pointers
@ 2021-07-27  9:03 Ankur saini
  0 siblings, 0 replies; only message in thread
From: Ankur saini @ 2021-07-27  9:03 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:5ba0adb523c2dec0a058abbe57c0461bf3f6d16c

commit 5ba0adb523c2dec0a058abbe57c0461bf3f6d16c
Author: Ankur Saini <arsenic@sourceware.org>
Date:   Fri Jul 16 20:46:55 2021 +0530

    analyzer: make analyer detect calls via function pointers

Diff:
---
 gcc/analyzer/analysis-plan.cc                  |  4 ++
 gcc/analyzer/engine.cc                         | 99 ++++++++++++++++++++++++--
 gcc/analyzer/program-point.cc                  | 17 +++++
 gcc/analyzer/program-point.h                   |  3 +-
 gcc/analyzer/program-state.cc                  | 44 ++++++++++++
 gcc/analyzer/program-state.h                   |  9 +++
 gcc/analyzer/region-model.cc                   | 48 +++++++++++++
 gcc/analyzer/region-model.h                    |  5 ++
 gcc/analyzer/state-purge.cc                    | 36 +++++++---
 gcc/analyzer/supergraph.cc                     | 33 +++++++--
 gcc/analyzer/supergraph.h                      |  5 ++
 gcc/testsuite/gcc.dg/analyzer/function-ptr-4.c | 53 ++++++++++++++
 gcc/testsuite/gcc.dg/analyzer/pr100546.c       | 17 +++++
 13 files changed, 349 insertions(+), 24 deletions(-)

diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc
index 7dfc48e9c3e..1c7e4d2cc84 100644
--- a/gcc/analyzer/analysis-plan.cc
+++ b/gcc/analyzer/analysis-plan.cc
@@ -109,6 +109,10 @@ analysis_plan::use_summary_p (const cgraph_edge *edge) const
   if (!flag_analyzer_call_summaries)
     return false;
 
+  /* Don't use call summaries if there is no callgraph edge */
+  if(!edge || !edge->callee)
+    return false;
+
   /* TODO: don't count callsites each time.  */
   int num_call_sites = 0;
   const cgraph_node *callee = edge->callee;
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index ee625fbdcdf..c0d55bbcaba 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -3174,10 +3174,13 @@ exploded_graph::process_node (exploded_node *node)
       break;
     case PK_AFTER_SUPERNODE:
       {
+        bool found_a_superedge = false;
+        bool is_an_exit_block = false;
 	/* If this is an EXIT BB, detect leaks, and potentially
 	   create a function summary.  */
 	if (point.get_supernode ()->return_p ())
 	  {
+	    is_an_exit_block = true;
 	    node->detect_leaks (*this);
 	    if (flag_analyzer_call_summaries
 		&& point.get_call_string ().empty_p ())
@@ -3205,6 +3208,7 @@ exploded_graph::process_node (exploded_node *node)
 	superedge *succ;
 	FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ)
 	  {
+	    found_a_superedge = true;
 	    if (logger)
 	      logger->log ("considering SN: %i -> SN: %i",
 			   succ->m_src->m_index, succ->m_dest->m_index);
@@ -3214,19 +3218,100 @@ exploded_graph::process_node (exploded_node *node)
 						 point.get_call_string ());
 	    program_state next_state (state);
 	    uncertainty_t uncertainty;
+
+	    /* Check if now the analyzer know about the call via 
+               function pointer or not. */
+            if (succ->m_kind == SUPEREDGE_INTRAPROCEDURAL_CALL && 
+                !(succ->get_any_callgraph_edge()))
+              {    
+                const program_point *this_point = &node->get_point();
+                const program_state *this_state = &node->get_state ();
+                const gcall *call 
+                  = this_point->get_supernode ()->get_final_call ();
+
+                impl_region_model_context ctxt (*this,
+                  node, 
+                  this_state, 
+                  &next_state, 
+                  &uncertainty,
+                  this_point->get_stmt());
+
+                region_model *model = this_state->m_region_model;
+                tree fn_decl = model->get_fndecl_for_call(call,&ctxt);
+                function *fun = DECL_STRUCT_FUNCTION(fn_decl);
+                if(fun)
+                {
+                  const supergraph *sg = &(this->get_supergraph());
+                  supernode * sn_entry = sg->get_node_for_function_entry (fun);
+                  supernode * sn_exit = sg->get_node_for_function_exit (fun);
+
+                  program_point new_point 
+                    = program_point::before_supernode (sn_entry,
+            				               NULL,
+            				               point.get_call_string ());
+
+                  new_point.push_to_call_stack (sn_exit,
+                  				next_point.get_supernode());
+
+                  next_state.push_call(*this, node, call, &uncertainty);
+
+                  if (next_state.m_valid)
+                  {
+                    exploded_node *enode = get_or_create_node (new_point,
+            					               next_state,
+            					               node);
+                    if (enode)
+                      add_edge (node,enode, NULL);
+                  }
+                }
+              }
+
 	    if (!node->on_edge (*this, succ, &next_point, &next_state,
-				&uncertainty))
+			        &uncertainty))
 	      {
-		if (logger)
-		  logger->log ("skipping impossible edge to SN: %i",
-			       succ->m_dest->m_index);
-		continue;
+	        if (logger)
+	          logger->log ("skipping impossible edge to SN: %i",
+		               succ->m_dest->m_index);
+	        continue;
 	      }
-	    exploded_node *next = get_or_create_node (next_point, next_state,
-						      node);
+	    exploded_node *next = get_or_create_node (next_point, 
+	    					      next_state,
+					              node);
 	    if (next)
 	      add_edge (node, next, succ);
 	  }
+
+    /* Return from the calls which doesn't have a return superedge.
+    	Such case occurs when GCC's middle end didn't knew which function to
+    	call but analyzer did */
+    if((is_an_exit_block && !found_a_superedge) && 
+       (!point.get_call_string().empty_p()))
+    {
+      const call_string cs = point.get_call_string();
+      program_point next_point 
+        = program_point::before_supernode (cs.get_caller_node (),
+        				   NULL,
+        				   cs);
+
+      program_state next_state (state);
+      uncertainty_t uncertainty;
+
+      const gcall *call = next_point.get_supernode ()->get_returning_call (); 
+      if(call)
+      {
+        next_state.returning_call(*this, node, call, &uncertainty);
+      }
+
+      if (next_state.m_valid)
+      {
+        next_point.pop_from_call_stack();
+        exploded_node *enode = get_or_create_node (next_point, 
+        					   next_state, 
+        					   node);
+        if (enode)
+          add_edge (node,enode, NULL);
+      }
+    }
       }
       break;
     }
diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc
index 0c39a7731a9..96a498d4ed7 100644
--- a/gcc/analyzer/program-point.cc
+++ b/gcc/analyzer/program-point.cc
@@ -330,6 +330,23 @@ program_point::to_json () const
   return point_obj;
 }
 
+/* update the callstack to represent a call from caller to callee 
+   genrally used to push a custom call to a perticular program point 
+   where we don't have a superedge representing the call.  */
+void 
+program_point::push_to_call_stack(const supernode *caller,
+				  const supernode *callee)
+{
+  m_call_string.push_call(callee,caller);
+}
+
+/* pop the topmost call from the current callstack.  */
+void 
+program_point::pop_from_call_stack()
+{
+  m_call_string.pop();
+}
+
 /* Generate a hash value for this program_point.  */
 
 hashval_t
diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h
index 5f86745cd1e..b43e18720f3 100644
--- a/gcc/analyzer/program-point.h
+++ b/gcc/analyzer/program-point.h
@@ -293,7 +293,8 @@ public:
   }
 
   bool on_edge (exploded_graph &eg, const superedge *succ);
-
+  void push_to_call_stack(const supernode *caller, const supernode *callee);
+  void pop_from_call_stack();
   void validate () const;
 
   /* For before_stmt, go to next stmt.  */
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 5bb86767873..f7ccf96b2b6 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1034,6 +1034,50 @@ program_state::on_edge (exploded_graph &eg,
   return true;
 }
 
+/* Update this program_state to reflect a call to function
+   represented by CALL_STMT.
+   currently used only when the call doesn't have a superedge representing 
+   the call ( like call via a function pointer )  */
+void
+program_state::push_call(exploded_graph &eg,
+      			 exploded_node *enode,
+      			 const gcall *call_stmt,
+      			 uncertainty_t *uncertainty)
+{
+  /* Update state.  */
+  const program_point &point = enode->get_point ();
+  const gimple *last_stmt = point.get_supernode ()->get_last_stmt ();
+
+  impl_region_model_context ctxt (eg, enode,
+          			  &enode->get_state (),
+          			  this,
+          			  uncertainty,
+          			  last_stmt);
+  m_region_model->update_for_gcall(call_stmt, &ctxt);
+}
+
+/* Update this program_state to reflect a return from function
+   call to which is represented by CALL_STMT.
+   currently used only when the call doesn't have a superedge representing 
+   the return */
+void
+program_state::returning_call(exploded_graph &eg,
+      			      exploded_node *enode,
+      			      const gcall *call_stmt,
+      			      uncertainty_t *uncertainty)
+{
+  /* Update state.  */
+  const program_point &point = enode->get_point ();
+  const gimple *last_stmt = point.get_supernode ()->get_last_stmt ();
+
+  impl_region_model_context ctxt (eg, enode,
+          			  &enode->get_state (),
+          			  this,
+          			  uncertainty,
+          			  last_stmt);
+  m_region_model->update_for_return_gcall(call_stmt, &ctxt);
+}
+
 /* Generate a simpler version of THIS, discarding state that's no longer
    relevant at POINT.
    The idea is that we're more likely to be able to consolidate
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 8dee930665c..e91381a6d7d 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -217,6 +217,15 @@ public:
 
   void push_frame (const extrinsic_state &ext_state, function *fun);
   function * get_current_function () const;
+  void push_call(exploded_graph &eg,
+      		 exploded_node *enode,
+      		 const gcall *call_stmt,
+      		 uncertainty_t *uncertainty);
+  void returning_call(exploded_graph &eg,
+      		      exploded_node *enode,
+      		      const gcall *call_stmt,
+      		      uncertainty_t *uncertainty);
+
 
   bool on_edge (exploded_graph &eg,
 		exploded_node *enode,
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index c029759cb9b..ca6b04fb24c 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -3164,6 +3164,30 @@ region_model::update_for_call_superedge (const call_superedge &call_edge,
   push_frame (call_edge.get_callee_function (), &arg_svals, ctxt);
 }
 
+/* Push a new frame_region on to the stack region.
+   works in similar manner of that of region_model::update_for_call_superedge()
+   but it get the call info from CALL_STMT instead from a suerpedge and 
+   is availabe publicically   */
+void
+region_model::update_for_gcall (const gcall *call_stmt,
+				region_model_context *ctxt)
+{
+  /* Build a vec of argument svalues, using the current top
+     frame for resolving tree expressions.  */
+  auto_vec<const svalue *> arg_svals (gimple_call_num_args (call_stmt));
+
+  for (unsigned i = 0; i < gimple_call_num_args (call_stmt); i++)
+    {
+      tree arg = gimple_call_arg (call_stmt, i);
+      arg_svals.quick_push (get_rvalue (arg, ctxt));
+    }
+
+  /* get the function * from the call */
+  tree fn_decl = get_fndecl_for_call(call_stmt,ctxt);
+  function *fun = DECL_STRUCT_FUNCTION(fn_decl);
+  push_frame (fun, &arg_svals, ctxt);
+}
+
 /* Pop the top-most frame_region from the stack, and copy the return
    region's values (if any) into the region for the lvalue of the LHS of
    the call (if any).  */
@@ -3188,6 +3212,30 @@ region_model::update_for_return_superedge (const return_superedge &return_edge,
   pop_frame (result_dst_reg, NULL, ctxt);
 }
 
+/* do exatly what region_model::update_for_return_superedge() do
+   but get the call info from CALL_STMT instead from a suerpedge and 
+   is availabe publicically   */
+void
+region_model::update_for_return_gcall (const gcall *call_stmt,
+             			       region_model_context *ctxt)
+{
+  /* Get the region for the result of the call, within the caller frame.  */
+  const region *result_dst_reg = NULL;
+  tree lhs = gimple_call_lhs (call_stmt);
+  if (lhs)
+    {
+      /* Normally we access the top-level frame, which is:
+         path_var (expr, get_stack_depth () - 1)
+         whereas here we need the caller frame, hence "- 2" here.  */
+      gcc_assert (get_stack_depth () >= 2);
+      result_dst_reg = get_lvalue (path_var (lhs, get_stack_depth () - 2),
+           			   ctxt);
+    }
+
+  pop_frame (result_dst_reg, NULL, ctxt);
+}
+
+
 /* Update this region_model with a summary of the effect of calling
    and returning from CG_SEDGE.
 
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 1c7a3865346..5c74b4fa85b 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -595,6 +595,11 @@ class region_model
 			      region_model_context *ctxt,
 			      rejected_constraint **out);
 
+  void update_for_gcall (const gcall *call_stmt,
+			 region_model_context *ctxt);
+  void update_for_return_gcall (const gcall *call_stmt,
+				region_model_context *ctxt);
+
   const region *push_frame (function *fun, const vec<const svalue *> *arg_sids,
 			    region_model_context *ctxt);
   const frame_region *get_current_frame () const { return m_current_frame; }
diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc
index bfa48a9ef3f..06fc49b76dd 100644
--- a/gcc/analyzer/state-purge.cc
+++ b/gcc/analyzer/state-purge.cc
@@ -383,18 +383,32 @@ state_purge_per_ssa_name::process_point (const function_point &point,
 	  {
 	    /* Add any intraprocedually edge for a call.  */
 	    if (snode->m_returning_call)
-	      {
-		cgraph_edge *cedge
+	    {
+	      gcall *returning_call = snode->m_returning_call;
+	      cgraph_edge *cedge
 		  = supergraph_call_edge (snode->m_fun,
-					  snode->m_returning_call);
-		gcc_assert (cedge);
-		superedge *sedge
-		  = map.get_sg ().get_intraprocedural_edge_for_call (cedge);
-		gcc_assert (sedge);
-		add_to_worklist
-		  (function_point::after_supernode (sedge->m_src),
-		   worklist, logger);
-	      }
+					  returning_call);
+	      if(!cedge)
+	        {
+	          supernode *callernode 
+	            = map.get_sg ().get_supernode_for_stmt (returning_call);
+
+	          gcc_assert (callernode);
+	          add_to_worklist 
+	            (function_point::after_supernode (callernode),
+		     worklist, logger);
+	        }
+	      else
+	        {
+		  gcc_assert (cedge);
+		  superedge *sedge
+		    = map.get_sg ().get_intraprocedural_edge_for_call (cedge);
+		  gcc_assert (sedge);
+		  add_to_worklist 
+		    (function_point::after_supernode (sedge->m_src),
+		     worklist, logger);
+	         }
+	    }
 	  }
       }
       break;
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index 1eb25436f94..b1f6176e44b 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -183,11 +183,34 @@ supergraph::supergraph (logger *logger)
 	      m_stmt_to_node_t.put (stmt, node_for_stmts);
 	      m_stmt_uids.make_uid_unique (stmt);
 	      if (cgraph_edge *edge = supergraph_call_edge (fun, stmt))
-		{
-		  m_cgraph_edge_to_caller_prev_node.put(edge, node_for_stmts);
-		  node_for_stmts = add_node (fun, bb, as_a <gcall *> (stmt), NULL);
-		  m_cgraph_edge_to_caller_next_node.put (edge, node_for_stmts);
-		}
+    		{
+    		  m_cgraph_edge_to_caller_prev_node.put(edge, node_for_stmts);
+    		  node_for_stmts = add_node (fun, bb, as_a <gcall *> (stmt),
+    		   			     NULL);
+    		  m_cgraph_edge_to_caller_next_node.put (edge, node_for_stmts);
+    		}
+	        else
+	        {
+	          // maybe call is via a function pointer
+	          gcall *call = dyn_cast<gcall *> (stmt);
+	          if (call)
+	          {
+	            cgraph_edge *edge 
+		      = cgraph_node::get (fun->decl)->get_edge (stmt);
+	            if (!edge || !edge->callee)
+	            {
+	              supernode *old_node_for_stmts = node_for_stmts;
+	              node_for_stmts = add_node (fun, bb, call, NULL);
+
+	              superedge *sedge 
+	                = new callgraph_superedge (old_node_for_stmts,
+	                  			   node_for_stmts,
+	                  			   SUPEREDGE_INTRAPROCEDURAL_CALL,
+	                  			   NULL);
+	              add_edge (sedge);
+	            }
+	          }
+	        }
 	    }
 
 	  m_bb_to_final_node.put (bb, node_for_stmts);
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index 877958f75fa..abfcd8e8cfd 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -268,6 +268,11 @@ class supernode : public dnode<supergraph_traits>
     return i;
   }
 
+  gcall *get_returning_call () const
+  {
+    return m_returning_call;
+  }
+
   gimple *get_last_stmt () const
   {
     if (m_stmts.length () == 0)
diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-4.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-4.c
new file mode 100644
index 00000000000..1054ab4f240
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-4.c
@@ -0,0 +1,53 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+void fun(int *int_ptr)
+{
+	free(int_ptr);  /* { dg-warning "double-'free' of 'int_ptr'" } */
+}
+
+void single_call()
+{
+	int *int_ptr = (int*)malloc(sizeof(int));
+	void (*fun_ptr)(int *) = &fun;
+	(*fun_ptr)(int_ptr);
+}
+
+void double_call()
+{
+	int *int_ptr = (int*)malloc(sizeof(int));
+	void (*fun_ptr)(int *) = &fun;
+	(*fun_ptr)(int_ptr);
+	(*fun_ptr)(int_ptr);
+}
+
+/*{ dg-begin-multiline-output "" }
+    6 |         free(int_ptr);
+      |         ^~~~~~~~~~~~~
+  'double_call': events 1-2
+    |
+    |   16 | void double_call()
+    |      |      ^~~~~~~~~~~
+    |      |      |
+    |      |      (1) entry to 'double_call'
+    |   17 | {
+    |   18 |         int *int_ptr = (int*)malloc(sizeof(int));
+    |      |                              ~~~~~~~~~~~~~~~~~~~
+    |      |                              |
+    |      |                              (2) allocated here
+    |
+    +--> 'fun': events 3-6
+           |
+           |    4 | void fun(int *int_ptr)
+           |      |      ^~~
+           |      |      |
+           |      |      (3) entry to ‘fun’
+           |      |      (5) entry to ‘fun’
+           |    5 | {
+           |    6 |         free(int_ptr);
+           |      |         ~~~~~~~~~~~~~
+           |      |         |
+           |      |         (4) first 'free' here
+           |      |         (6) second 'free' here; first 'free' was at (4)
+           |
+*/
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr100546.c b/gcc/testsuite/gcc.dg/analyzer/pr100546.c
new file mode 100644
index 00000000000..3349d4067af
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr100546.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+static void noReturn(const char *str) __attribute__((noreturn));
+static void noReturn(const char *str) {
+    printf("%s\n", str);
+    exit(1);
+}
+
+void (*noReturnPtr)(const char *str) = &noReturn;
+
+int main(int argc, char **argv) {
+    char *str = 0;
+    if (!str)
+        noReturnPtr(__FILE__);
+    return printf("%c\n", *str);
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-07-27  9:03 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-27  9:03 [gcc(refs/users/arsenic/heads/polymorphic_call)] analyzer: make analyer detect calls via function pointers Ankur saini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).