public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/arsenic/heads/analyzer_extension)] analyzer: make analyer detect calls via function pointers
@ 2021-07-22 5:54 Ankur saini
0 siblings, 0 replies; only message in thread
From: Ankur saini @ 2021-07-22 5:54 UTC (permalink / raw)
To: gcc-cvs
https://gcc.gnu.org/g:a0eee7ebbe450095800878e0945ab30a91debbf4
commit a0eee7ebbe450095800878e0945ab30a91debbf4
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 e3d35511551..f3b332774a2 100644
--- a/gcc/analyzer/program-point.cc
+++ b/gcc/analyzer/program-point.cc
@@ -323,6 +323,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 6d02c60449c..1a6f4e1abc4 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -3156,6 +3156,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). */
@@ -3180,6 +3204,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 734ec601237..05acb8240b1 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -589,6 +589,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 e82ea87e735..c4050c2cf9a 100644
--- a/gcc/analyzer/state-purge.cc
+++ b/gcc/analyzer/state-purge.cc
@@ -377,18 +377,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 8611d0f8689..ace9e7b128a 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 f4090fd5e0e..6ca8b418224 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-22 5:54 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-22 5:54 [gcc(refs/users/arsenic/heads/analyzer_extension)] 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).