From: David Malcolm Date: Thu, 6 Feb 2020 02:29:04 +0000 (-0500) Subject: analyzer: use ultimate alias target at calls (PR 93288) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=91f993b7e31ce85676148dca180bc0d827d4245e;p=gcc.git analyzer: use ultimate alias target at calls (PR 93288) PR analyzer/93288 reports an ICE in a C++ testcase when calling a constructor. The issue is that when building the supergraph, we encounter the cgraph edge to "__ct_comp ", the DECL_COMPLETE_CONSTRUCTOR_P, and this node's DECL_STRUCT_FUNCTION has a NULL CFG, which the analyzer reads through, leading to the ICE. This patch reworks function and fndecl lookup at calls throughout the analyzer so that it looks for the ultimate_alias_target of the callee. In the case above, this means using the "__ct_base " for the ctor, which has a CFG, fixing the ICE. Getting this right allows for some simple C++ cases involving ctors to work, so the patch also adds some test coverage for that. gcc/analyzer/ChangeLog: PR analyzer/93288 * analysis-plan.cc (analysis_plan::use_summary_p): Look through the ultimate_alias_target when getting the called function. * engine.cc (exploded_node::on_stmt): Rename second "ctxt" to "sm_ctxt". Use the region_model's get_fndecl_for_call rather than gimple_call_fndecl. * region-model.cc (region_model::get_fndecl_for_call): Use ultimate_alias_target on fndecl. * supergraph.cc (get_ultimate_function_for_cgraph_edge): New function. (supergraph_call_edge): Use it when rejecting edges without functions. (supergraph::supergraph): Use it to get the function for the cgraph_edge when building interprocedural superedges. (callgraph_superedge::get_callee_function): Use it. * supergraph.h (supergraph::get_num_snodes): Make param const. (supergraph::function_to_num_snodes_t): Make first type param const. gcc/testsuite/ChangeLog: PR analyzer/93288 * g++.dg/analyzer/malloc.C: Add test coverage for a double-free called in a constructor. * g++.dg/analyzer/pr93288.C: New test. --- diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index 0313e437f34..eda1052e4ff 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,24 @@ +2020-02-11 David Malcolm + + PR analyzer/93288 + * analysis-plan.cc (analysis_plan::use_summary_p): Look through + the ultimate_alias_target when getting the called function. + * engine.cc (exploded_node::on_stmt): Rename second "ctxt" to + "sm_ctxt". Use the region_model's get_fndecl_for_call rather than + gimple_call_fndecl. + * region-model.cc (region_model::get_fndecl_for_call): Use + ultimate_alias_target on fndecl. + * supergraph.cc (get_ultimate_function_for_cgraph_edge): New + function. + (supergraph_call_edge): Use it when rejecting edges without + functions. + (supergraph::supergraph): Use it to get the function for the + cgraph_edge when building interprocedural superedges. + (callgraph_superedge::get_callee_function): Use it. + * supergraph.h (supergraph::get_num_snodes): Make param const. + (supergraph::function_to_num_snodes_t): Make first type param + const. + 2020-02-11 David Malcolm PR analyzer/93374 diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc index 8ad2fa2ebb4..3c8b10b3314 100644 --- a/gcc/analyzer/analysis-plan.cc +++ b/gcc/analyzer/analysis-plan.cc @@ -120,7 +120,11 @@ analysis_plan::use_summary_p (const cgraph_edge *edge) const /* Require the callee to be sufficiently complex to be worth summarizing. */ - if ((int)m_sg.get_num_snodes (callee->get_fun ()) + const function *fun + = const_cast (callee)->ultimate_alias_target ()->get_fun (); + /* TODO(stage1): can ultimate_alias_target be made const? */ + + if ((int)m_sg.get_num_snodes (fun) < param_analyzer_min_snodes_for_call_summary) return false; diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 4d329e2b6af..7860da0572a 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -1044,19 +1044,19 @@ exploded_node::on_stmt (exploded_graph &eg, const sm_state_map *old_smap = old_state.m_checker_states[sm_idx]; sm_state_map *new_smap = state->m_checker_states[sm_idx]; - impl_sm_context ctxt (eg, sm_idx, sm, this, &old_state, state, - change, - old_smap, new_smap); + impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state, + change, + old_smap, new_smap); /* Allow the state_machine to handle the stmt. */ - if (sm.on_stmt (&ctxt, snode, stmt)) + if (sm.on_stmt (&sm_ctxt, snode, stmt)) unknown_side_effects = false; else { /* For those stmts that were not handled by the state machine. */ if (const gcall *call = dyn_cast (stmt)) { - tree callee_fndecl = gimple_call_fndecl (call); - // TODO: maybe we can be smarter about handling function pointers? + tree callee_fndecl + = state->m_region_model->get_fndecl_for_call (call, &ctxt); if (!fndecl_has_gimple_body_p (callee_fndecl)) new_smap->purge_for_unknown_fncall (eg, sm, call, callee_fndecl, diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index b9d52f64270..ae810f5eb4b 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -6676,7 +6676,10 @@ region_model::get_fndecl_for_call (const gcall *call, if (code) { tree fn_decl = code->get_tree_for_child_region (fn_rid); - return fn_decl; + const cgraph_node *ultimate_node + = cgraph_node::get (fn_decl)->ultimate_alias_target (); + if (ultimate_node) + return ultimate_node->decl; } } diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc index b20daa081d2..fb4dbdfd8b9 100644 --- a/gcc/analyzer/supergraph.cc +++ b/gcc/analyzer/supergraph.cc @@ -56,6 +56,18 @@ along with GCC; see the file COPYING3. If not see namespace ana { +/* Get the function of the ultimate alias target being called at EDGE, + if any. */ + +static function * +get_ultimate_function_for_cgraph_edge (cgraph_edge *edge) +{ + cgraph_node *ultimate_node = edge->callee->ultimate_alias_target (); + if (!ultimate_node) + return NULL; + return ultimate_node->get_fun (); +} + /* Get the cgraph_edge, but only if there's an underlying function body. */ cgraph_edge * @@ -69,7 +81,7 @@ supergraph_call_edge (function *fun, gimple *stmt) return NULL; if (!edge->callee) return NULL; /* e.g. for a function pointer. */ - if (!edge->callee->get_fun ()) + if (!get_ultimate_function_for_cgraph_edge (edge)) return NULL; return edge; } @@ -178,8 +190,10 @@ supergraph::supergraph (logger *logger) { cgraph_edge *edge = (*iter).first; supernode *caller_prev_supernode = (*iter).second; - basic_block callee_cfg_block - = ENTRY_BLOCK_PTR_FOR_FN (edge->callee->get_fun ()); + function* callee_fn = get_ultimate_function_for_cgraph_edge (edge); + if (!callee_fn || !callee_fn->cfg) + continue; + basic_block callee_cfg_block = ENTRY_BLOCK_PTR_FOR_FN (callee_fn); supernode *callee_supernode = *m_bb_to_initial_node.get (callee_cfg_block); call_superedge *sedge @@ -199,8 +213,10 @@ supergraph::supergraph (logger *logger) { cgraph_edge *edge = (*iter).first; supernode *caller_next_supernode = (*iter).second; - basic_block callee_cfg_block - = EXIT_BLOCK_PTR_FOR_FN (edge->callee->get_fun ()); + function* callee_fn = get_ultimate_function_for_cgraph_edge (edge); + if (!callee_fn || !callee_fn->cfg) + continue; + basic_block callee_cfg_block = EXIT_BLOCK_PTR_FOR_FN (callee_fn); supernode *callee_supernode = *m_bb_to_initial_node.get (callee_cfg_block); return_superedge *sedge @@ -840,7 +856,7 @@ callgraph_superedge::dump_label_to_pp (pretty_printer *pp, function * callgraph_superedge::get_callee_function () const { - return m_cedge->callee->get_fun (); + return get_ultimate_function_for_cgraph_edge (m_cedge); } /* Get the calling function at this interprocedural call/return edge. */ diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h index 0eac0b8bfc9..2c94f0544ce 100644 --- a/gcc/analyzer/supergraph.h +++ b/gcc/analyzer/supergraph.h @@ -156,7 +156,7 @@ public: return m_nodes[idx]; } - unsigned get_num_snodes (function *fun) const + unsigned get_num_snodes (const function *fun) const { function_to_num_snodes_t &map = const_cast (m_function_to_num_snodes); @@ -201,7 +201,7 @@ private: typedef ordered_hash_map stmt_to_node_t; stmt_to_node_t m_stmt_to_node_t; - typedef hash_map function_to_num_snodes_t; + typedef hash_map function_to_num_snodes_t; function_to_num_snodes_t m_function_to_num_snodes; }; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c077000d304..e7e630bdb74 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2020-02-11 David Malcolm + + PR analyzer/93288 + * g++.dg/analyzer/malloc.C: Add test coverage for a double-free + called in a constructor. + * g++.dg/analyzer/pr93288.C: New test. + 2020-02-11 David Malcolm PR analyzer/93212 diff --git a/gcc/testsuite/g++.dg/analyzer/malloc.C b/gcc/testsuite/g++.dg/analyzer/malloc.C index 0637295e1f2..76baab98222 100644 --- a/gcc/testsuite/g++.dg/analyzer/malloc.C +++ b/gcc/testsuite/g++.dg/analyzer/malloc.C @@ -7,3 +7,19 @@ void test_1 (void *ptr) free (ptr); free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ } + +/* Test of double-free in ctor. */ + +struct s2 +{ + s2 (void *v) + { + free (v); // { dg-warning "double-'free' of 'v'" } + } +}; + +void test_2 (void *ptr) +{ + free (ptr); // { dg-message "first 'free' here" } + s2 a (ptr); // { dg-message "passing freed pointer 'ptr' in call to 's2::s2' from 'test_2'" } +} diff --git a/gcc/testsuite/g++.dg/analyzer/pr93288.C b/gcc/testsuite/g++.dg/analyzer/pr93288.C new file mode 100644 index 00000000000..1798fed805f --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/pr93288.C @@ -0,0 +1,8 @@ +// { dg-do compile } + +struct a { + a(); +}; +class foo { + a b; +} c;