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).