public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-3002] analyzer: detect and analyze calls via function pointer
@ 2021-08-18 17:41 Ankur saini
  0 siblings, 0 replies; only message in thread
From: Ankur saini @ 2021-08-18 17:41 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:aef703cf982072427e74034f4c460a11c5e04b8e

commit r12-3002-gaef703cf982072427e74034f4c460a11c5e04b8e
Author: Ankur Saini <arsenic@sourceware.org>
Date:   Thu Jul 29 15:48:07 2021 +0530

    analyzer: detect and analyze calls via function pointer
    
    2021-07-29  Ankur Saini  <arsenic@sourceware.org>
    
    gcc/analyzer/ChangeLog:
            PR analyzer/100546
            * analysis-plan.cc (analysis_plan::use_summary_p): Don't use call
            summaries if there is no callgraph edge
            * checker-path.cc (call_event::call_event): Handle calls events that
            are not represented by a supergraph call edge
            (return_event::return_event): Likewise.
            (call_event::get_desc): Work with new call_event structure.
            (return_event::get_desc): Likeise.
            * checker-path.h (call_event::m_src_snode): New field.
            (call_event::m_dest_snode): New field.
            (return_event::m_src_snode): New field.
            (return_event::m_dest_snode): New field.
            * diagnostic-manager.cc
            (diagnostic_manager::prune_for_sm_diagnostic)<case EK_CALL_EDGE>:
            Refactor to work with edges without callgraph edge.
            (diagnostic_manager::prune_for_sm_diagnostic)<case EK_RETURN_EDGE>:
            Likewise.
            * engine.cc (dynamic_call_info_t::update_model): New function.
            (dynamic_call_info_t::add_events_to_path): New function.
            (exploded_graph::create_dynamic_call): New function.
            (exploded_graph::process_node): Work with dynamically discovered calls.
            * exploded-graph.h (class dynamic_call_info_t): New class.
            (exploded_graph::create_dynamic_call): New decl.
            * program-point.cc (program_point::push_to_call_stack): New function.
            (program_point::pop_from_call_stack): New function.
            * program-point.h (program_point::push_to_call_stack): New decl.
            (program_point::pop_from_call_stack): New decl.
            * program-state.cc (program_state::push_call): New function.
            (program_state::returning_call): New function.
            * program-state.h (program_state::push_call): New decl.
            (program_state::returning_call): New decl.
            * region-model.cc (region_model::update_for_gcall) New function.
            (region_model::update_for_return_gcall): New function.
            (egion_model::update_for_call_superedge): Get the underlying gcall and
            update for gcall.
            (region_model::update_for_return_superedge): Likewise.
            * region-model.h (region_model::update_for_gcall): New decl.
            (region_model::update_for_return_gcall): New decl.
            * state-purge.cc (state_purge_per_ssa_name::process_point): Update to
            work with calls without underlying cgraph edge.
            * supergraph.cc (supergraph::supergraph) Split snodes at every callsite.
            * supergraph.h (supernode::get_returning_call) New accessor.
    
    gcc/testsuite/ChangeLog:
            PR analyzer/100546
            * gcc.dg/analyzer/function-ptr-4.c: New test.
            * gcc.dg/analyzer/pr100546.c: New test.

Diff:
---
 gcc/analyzer/analysis-plan.cc                  |   4 +
 gcc/analyzer/checker-path.cc                   |  28 ++--
 gcc/analyzer/checker-path.h                    |   6 +
 gcc/analyzer/diagnostic-manager.cc             |  23 +---
 gcc/analyzer/engine.cc                         | 183 +++++++++++++++++++++++++
 gcc/analyzer/exploded-graph.h                  |  39 ++++++
 gcc/analyzer/program-point.cc                  |  18 +++
 gcc/analyzer/program-point.h                   |   3 +-
 gcc/analyzer/program-state.cc                  |  44 ++++++
 gcc/analyzer/program-state.h                   |  11 ++
 gcc/analyzer/region-model.cc                   |  44 ++++--
 gcc/analyzer/region-model.h                    |   6 +
 gcc/analyzer/state-purge.cc                    |  35 +++--
 gcc/analyzer/supergraph.cc                     |  43 +++++-
 gcc/analyzer/supergraph.h                      |   7 +-
 gcc/testsuite/gcc.dg/analyzer/function-ptr-4.c |  24 ++++
 gcc/testsuite/gcc.dg/analyzer/pr100546.c       |  17 +++
 17 files changed, 481 insertions(+), 54 deletions(-)

diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc
index 7dfc48e9c3e..57a6dcb1f6e 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/checker-path.cc b/gcc/analyzer/checker-path.cc
index e10c8e2bb7c..e132f003470 100644
--- a/gcc/analyzer/checker-path.cc
+++ b/gcc/analyzer/checker-path.cc
@@ -614,7 +614,11 @@ call_event::call_event (const exploded_edge &eedge,
 			location_t loc, tree fndecl, int depth)
 : superedge_event (EK_CALL_EDGE, eedge, loc, fndecl, depth)
 {
-  gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CALL);
+  if (eedge.m_sedge)
+    gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CALL);
+
+   m_src_snode = eedge.m_src->get_supernode ();
+   m_dest_snode = eedge.m_dest->get_supernode ();
 }
 
 /* Implementation of diagnostic_event::get_desc vfunc for
@@ -638,8 +642,8 @@ call_event::get_desc (bool can_colorize) const
       label_text custom_desc
 	= m_pending_diagnostic->describe_call_with_state
 	    (evdesc::call_with_state (can_colorize,
-				      m_sedge->m_src->m_fun->decl,
-				      m_sedge->m_dest->m_fun->decl,
+				      m_src_snode->m_fun->decl,
+				      m_dest_snode->m_fun->decl,
 				      var,
 				      m_critical_state));
       if (custom_desc.m_buffer)
@@ -648,8 +652,8 @@ call_event::get_desc (bool can_colorize) const
 
   return make_label_text (can_colorize,
 			  "calling %qE from %qE",
-			  m_sedge->m_dest->m_fun->decl,
-			  m_sedge->m_src->m_fun->decl);
+			  m_dest_snode->m_fun->decl,
+			  m_src_snode->m_fun->decl);
 }
 
 /* Override of checker_event::is_call_p for calls.  */
@@ -668,7 +672,11 @@ return_event::return_event (const exploded_edge &eedge,
 			    location_t loc, tree fndecl, int depth)
 : superedge_event (EK_RETURN_EDGE, eedge, loc, fndecl, depth)
 {
-  gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_RETURN);
+  if (eedge.m_sedge)
+    gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_RETURN);
+
+  m_src_snode = eedge.m_src->get_supernode ();
+  m_dest_snode = eedge.m_dest->get_supernode ();
 }
 
 /* Implementation of diagnostic_event::get_desc vfunc for
@@ -694,16 +702,16 @@ return_event::get_desc (bool can_colorize) const
       label_text custom_desc
 	= m_pending_diagnostic->describe_return_of_state
 	    (evdesc::return_of_state (can_colorize,
-				      m_sedge->m_dest->m_fun->decl,
-				      m_sedge->m_src->m_fun->decl,
+				      m_dest_snode->m_fun->decl,
+				      m_src_snode->m_fun->decl,
 				      m_critical_state));
       if (custom_desc.m_buffer)
 	return custom_desc;
     }
   return make_label_text (can_colorize,
 			  "returning to %qE from %qE",
-			  m_sedge->m_dest->m_fun->decl,
-			  m_sedge->m_src->m_fun->decl);
+			  m_dest_snode->m_fun->decl,
+			  m_src_snode->m_fun->decl);
 }
 
 /* Override of checker_event::is_return_p for returns.  */
diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h
index 1843c4bc7b4..27634c20864 100644
--- a/gcc/analyzer/checker-path.h
+++ b/gcc/analyzer/checker-path.h
@@ -338,6 +338,9 @@ public:
   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
 
   bool is_call_p () const FINAL OVERRIDE;
+
+  const supernode *m_src_snode;
+  const supernode *m_dest_snode;
 };
 
 /* A concrete event subclass for an interprocedural return.  */
@@ -351,6 +354,9 @@ public:
   label_text get_desc (bool can_colorize) const FINAL OVERRIDE;
 
   bool is_return_p () const FINAL OVERRIDE;
+
+  const supernode *m_src_snode;
+  const supernode *m_dest_snode;
 };
 
 /* A concrete event subclass for the start of a consolidated run of CFG
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index ef3df324365..06e751033ac 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -2093,18 +2093,13 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
 	case EK_CALL_EDGE:
 	  {
 	    call_event *event = (call_event *)base_event;
-	    const callgraph_superedge& cg_superedge
-	      = event->get_callgraph_superedge ();
 	    const region_model *callee_model
 	      = event->m_eedge.m_dest->get_state ().m_region_model;
+	    const region_model *caller_model
+	      = event->m_eedge.m_src->get_state ().m_region_model;
 	    tree callee_var = callee_model->get_representative_tree (sval);
-	    /* We could just use caller_model->get_representative_tree (sval);
-	       to get the caller_var, but for now use
-	       map_expr_from_callee_to_caller so as to only record critical
-	       state for parms and the like.  */
 	    callsite_expr expr;
-	    tree caller_var
-	      = cg_superedge.map_expr_from_callee_to_caller (callee_var, &expr);
+	    tree caller_var = caller_model->get_representative_tree (sval);
 	    if (caller_var)
 	      {
 		if (get_logger ())
@@ -2126,15 +2121,11 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
 	    if (sval)
 	      {
 		return_event *event = (return_event *)base_event;
-		const callgraph_superedge& cg_superedge
-		  = event->get_callgraph_superedge ();
-		const region_model *caller_model
-		  = event->m_eedge.m_dest->get_state ().m_region_model;
-		tree caller_var = caller_model->get_representative_tree (sval);
 		callsite_expr expr;
-		tree callee_var
-		  = cg_superedge.map_expr_from_caller_to_callee (caller_var,
-								 &expr);
+
+		const region_model *callee_model
+	      	  = event->m_eedge.m_src->get_state ().m_region_model;
+		tree callee_var = callee_model->get_representative_tree (sval);
 		if (callee_var)
 		  {
 		    if (get_logger ())
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index ecd4265733b..461de9cf77c 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1627,6 +1627,50 @@ exploded_node::dump_succs_and_preds (FILE *outf) const
   }
 }
 
+/* class dynamic_call_info_t : public exploded_edge::custom_info_t.  */
+
+/* Implementation of exploded_edge::custom_info_t::update_model vfunc
+   for dynamic_call_info_t.
+
+   Update state for the dynamically discorverd calls */
+
+void
+dynamic_call_info_t::update_model (region_model *model,
+				   const exploded_edge &eedge)
+{
+  const program_state &dest_state = eedge.m_dest->get_state ();
+  *model = *dest_state.m_region_model;
+}
+
+/* Implementation of exploded_edge::custom_info_t::add_events_to_path vfunc
+   for dynamic_call_info_t.  */
+
+void
+dynamic_call_info_t::add_events_to_path (checker_path *emission_path,
+				   const exploded_edge &eedge)
+{
+  const exploded_node *src_node = eedge.m_src;
+  const program_point &src_point = src_node->get_point ();
+  const int src_stack_depth = src_point.get_stack_depth ();
+  const exploded_node *dest_node = eedge.m_dest;
+  const program_point &dest_point = dest_node->get_point ();
+  const int dest_stack_depth = dest_point.get_stack_depth ();
+
+  if (m_is_returning_call)
+    emission_path->add_event (new return_event (eedge, (m_dynamic_call
+	                   			        ? m_dynamic_call->location
+	           	   		                : UNKNOWN_LOCATION),
+	          	      dest_point.get_fndecl (),
+	          	      dest_stack_depth));
+  else
+    emission_path->add_event (new call_event (eedge, (m_dynamic_call
+	                   			      ? m_dynamic_call->location
+	           	   		              : UNKNOWN_LOCATION),
+	          	      src_point.get_fndecl (),
+	          	      src_stack_depth));
+
+}
+
 /* class rewind_info_t : public exploded_edge::custom_info_t.  */
 
 /* Implementation of exploded_edge::custom_info_t::update_model vfunc
@@ -2980,6 +3024,61 @@ state_change_requires_new_enode_p (const program_state &old_state,
   return false;
 }
 
+/* Create enodes and eedges for the function calls that doesn't have an 
+   underlying call superedge.
+
+   Such case occurs when GCC's middle end didn't know which function to
+   call but the analyzer does (with the help of current state).
+
+   Some example such calls are dynamically dispatched calls to virtual
+   functions or calls that happen via function pointer.  */
+
+void
+exploded_graph::create_dynamic_call (const gcall *call,
+                                     tree fn_decl,
+                                     exploded_node *node,
+                                     program_state next_state,
+                                     program_point &next_point,
+                                     uncertainty_t *uncertainty,
+                                     logger *logger)
+{
+  LOG_FUNC (logger);
+
+  const program_point *this_point = &node->get_point ();
+  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,
+                                           this_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)
+        {
+          if (logger)
+            logger->log ("Discovered call to %s [SN: %i -> SN: %i]",
+                          function_name(fun),
+                          this_point->get_supernode ()->m_index,
+                          sn_entry->m_index);
+
+          exploded_node *enode = get_or_create_node (new_point,
+                                                     next_state,
+                                                     node);
+          if (enode)
+            add_edge (node,enode, NULL,
+                      new dynamic_call_info_t (call));
+        }
+     }
+}
+
 /* The core of exploded_graph::process_worklist (the main analysis loop),
    handling one node in the worklist.
 
@@ -3174,10 +3273,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 +3307,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,6 +3317,54 @@ exploded_graph::process_node (exploded_node *node)
 						 point.get_call_string ());
 	    program_state next_state (state);
 	    uncertainty_t uncertainty;
+
+            /* Make use the current state and try to discover and analyse
+               indirect function calls (a call that doesn't have an underlying
+               cgraph edge representing call).
+
+               Some examples of such calls are virtual function calls
+               and calls that happen via a function pointer.  */
+            if (succ->m_kind == SUPEREDGE_INTRAPROCEDURAL_CALL
+            	&& !(succ->get_any_callgraph_edge ()))
+              {
+                const gcall *call
+                  = point.get_supernode ()->get_final_call ();
+
+                impl_region_model_context ctxt (*this,
+                                                node,
+                                                &state,
+                                                &next_state,
+                                                &uncertainty,
+                                                point.get_stmt());
+
+                region_model *model = state.m_region_model;
+
+                if (tree fn_decl = model->get_fndecl_for_call(call,&ctxt))
+                  create_dynamic_call (call,
+                                       fn_decl,
+                                       node,
+                                       next_state,
+                                       next_point,
+                                       &uncertainty,
+                                       logger);
+                else
+                  {
+                     /* An unknown function was called at this point, in such
+                        case, don't terminate the analysis of the current
+                        function.
+
+                        The analyzer handles calls to unknown functions while
+                        analysing the stmt itself, so the the function call
+                        must have been handled by the anlyzer till now.  */
+                     exploded_node *next
+                       = get_or_create_node (next_point,
+                                             next_state,
+                                             node);
+                     if (next)
+                       add_edge (node, next, succ);
+                  }
+              }
+
 	    if (!node->on_edge (*this, succ, &next_point, &next_state,
 				&uncertainty))
 	      {
@@ -3227,6 +3378,38 @@ exploded_graph::process_node (exploded_node *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,
+                            new dynamic_call_info_t (call, true));
+              }
+          }
       }
       break;
     }
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index 8f48d8a286c..192a4b3c8f8 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -362,6 +362,37 @@ private:
   DISABLE_COPY_AND_ASSIGN (exploded_edge);
 };
 
+/* Extra data for an exploded_edge that represents dynamic call info ( calls
+   that doesn't have an underlying superedge representing the call ).  */
+
+class dynamic_call_info_t : public exploded_edge::custom_info_t
+{
+public:
+  dynamic_call_info_t (const gcall *dynamic_call,
+  		       const bool is_returning_call = false)
+  : m_dynamic_call (dynamic_call), 
+    m_is_returning_call (is_returning_call)
+  {}
+
+  void print (pretty_printer *pp) FINAL OVERRIDE
+  {
+    if (m_is_returning_call)
+      pp_string (pp, "dynamic_return");
+    else
+      pp_string (pp, "dynamic_call");
+  }
+
+  void update_model (region_model *model,
+		     const exploded_edge &eedge) FINAL OVERRIDE;
+
+  void add_events_to_path (checker_path *emission_path,
+			   const exploded_edge &eedge) FINAL OVERRIDE;
+private:
+  const gcall *m_dynamic_call;
+  const bool m_is_returning_call;
+};
+
+
 /* Extra data for an exploded_edge that represents a rewind from a
    longjmp to a setjmp (or from a siglongjmp to a sigsetjmp).  */
 
@@ -785,6 +816,14 @@ public:
   bool maybe_process_run_of_before_supernode_enodes (exploded_node *node);
   void process_node (exploded_node *node);
 
+  void create_dynamic_call (const gcall *call,
+                            tree fn_decl,
+                            exploded_node *node,
+                            program_state next_state,
+                            program_point &next_point,
+                            uncertainty_t *uncertainty,
+                            logger *logger);
+
   exploded_node *get_or_create_node (const program_point &point,
 				     const program_state &state,
 				     exploded_node *enode_for_diag);
diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc
index d9f50daeb3c..25d56af3191 100644
--- a/gcc/analyzer/program-point.cc
+++ b/gcc/analyzer/program-point.cc
@@ -330,6 +330,24 @@ program_point::to_json () const
   return point_obj;
 }
 
+/* Update the callstack to represent a call from caller to callee.
+
+   Generally 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..6bae29b23e8 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..ea53c61f497 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..eb49006d777 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -218,6 +218,17 @@ 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,
 		const superedge *succ,
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 58da7e3c0e4..2316fbe5041 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -3172,12 +3172,11 @@ region_model::maybe_update_for_edge (const superedge &edge,
    caller's frame.  */
 
 void
-region_model::update_for_call_superedge (const call_superedge &call_edge,
-					 region_model_context *ctxt)
+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.  */
-  const gcall *call_stmt = call_edge.get_call_stmt ();
   auto_vec<const svalue *> arg_svals (gimple_call_num_args (call_stmt));
 
   for (unsigned i = 0; i < gimple_call_num_args (call_stmt); i++)
@@ -3186,33 +3185,58 @@ region_model::update_for_call_superedge (const call_superedge &call_edge,
       arg_svals.quick_push (get_rvalue (arg, ctxt));
     }
 
-  push_frame (call_edge.get_callee_function (), &arg_svals, 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).  */
+
 void
-region_model::update_for_return_superedge (const return_superedge &return_edge,
-					   region_model_context *ctxt)
+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;
-  const gcall *call_stmt = return_edge.get_call_stmt ();
   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.  */
+         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);
+           			   ctxt);
     }
 
   pop_frame (result_dst_reg, NULL, ctxt);
 }
 
+/* Extract calling information from the superedge and update the model for the 
+   call  */
+
+void
+region_model::update_for_call_superedge (const call_superedge &call_edge,
+					 region_model_context *ctxt)
+{
+  const gcall *call_stmt = call_edge.get_call_stmt ();
+  update_for_gcall (call_stmt,ctxt);
+}
+
+/* Extract calling information from the return superedge and update the model 
+   for the returning call */
+
+void
+region_model::update_for_return_superedge (const return_superedge &return_edge,
+					   region_model_context *ctxt)
+{
+  const gcall *call_stmt = return_edge.get_call_stmt ();
+  update_for_return_gcall (call_stmt, 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 30f02a06b62..e40264e0eb0 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -608,6 +608,12 @@ 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..880057004e2 100644
--- a/gcc/analyzer/state-purge.cc
+++ b/gcc/analyzer/state-purge.cc
@@ -383,18 +383,31 @@ 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)
+	        {
+		  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);
+	        }
+	      else
+	        {
+	          supernode *callernode 
+	            = map.get_sg ().get_supernode_for_stmt (returning_call);
+
+	          gcc_assert (callernode);
+	          add_to_worklist 
+	            (function_point::after_supernode (callernode),
+		     worklist, logger);
+	         }
+	    }
 	  }
       }
       break;
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index 1eb25436f94..66ef765f472 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -183,11 +183,33 @@ 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
+	          if (gcall *call = dyn_cast<gcall *> (stmt))
+	          {
+	            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);
@@ -1139,6 +1161,17 @@ callgraph_superedge::get_callee_decl () const
   return get_callee_function ()->decl;
 }
 
+/* Get the gcall * of this interprocedural call/return edge.  */
+
+gcall *
+callgraph_superedge::get_call_stmt () const
+{
+  if (m_cedge)
+    return m_cedge->call_stmt;
+  
+  return m_src->get_final_call ();
+}
+
 /* Get the calling fndecl at this interprocedural call/return edge.  */
 
 tree
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index 877958f75fa..335f5133407 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)
@@ -400,7 +405,7 @@ class callgraph_superedge : public superedge
   function *get_caller_function () const;
   tree get_callee_decl () const;
   tree get_caller_decl () const;
-  gcall *get_call_stmt () const { return m_cedge->call_stmt; }
+  gcall *get_call_stmt () const;
   tree get_arg_for_parm (tree parm, callsite_expr *out) const;
   tree get_parm_for_arg (tree arg, callsite_expr *out) const;
   tree map_expr_from_caller_to_callee (tree caller_expr,
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..016351a83f6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-4.c
@@ -0,0 +1,24 @@
+/* Test to see if the analyzer detect and analyze calls via 
+   function pointers or not.  */
+
+#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); /* { dg-message "calling 'fun' from 'double_call'" } */
+	(*fun_ptr)(int_ptr);
+}
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-08-18 17:41 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-18 17:41 [gcc r12-3002] analyzer: detect and analyze calls via function pointer 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).