analyzer: Fix PR analyzer/101980

2021-08-19  Ankur Saini  <arsenic@sourceware.org>

gcc/analyzer/ChangeLog:
	PR analyzer/101980
	* diagnostic-manager.cc
	(diagnostic_manager::prune_for_sm_diagnostic)<case EK_CALL_EDGE>: Use
	caller_model only when the supergraph_edge doesn't exixt.
	(diagnostic_manager::prune_for_sm_diagnostic)<case EK_RETURN_EDGE>:
	Likewise.
	* engine.cc (exploded_graph::create_dynamic_call): Rename to...
	(exploded_graph::maybe_create_dynamic_call): ...this, return call
	creation status.
	(exploded_graph::process_node): Handle calls which were not dynamically
	discovered.
	* exploded-graph.h (exploded_graph::create_dynamic_call): Rename to...
	(exploded_graph::maybe_create_dynamic_call): ...this.
	* region-model.cc (region_model::update_for_gcall): New param, use it
	to push call to frame.
	(region_model::update_for_call_superedge): Pass callee function to
	update_for_gcall.
	* region-model.h (region_model::update_for_gcall): New param.

gcc/testsuite/ChangeLog:
	PR analyzer/101980
	* gcc.dg/analyzer/function-ptr-2.c : Add issue for double 'free'.
	* gcc.dg/analyzer/malloc-callbacks.c : Fix xfail testcase.
This commit is contained in:
Ankur Saini 2021-08-19 19:54:56 +05:30
parent 7c9e164583
commit e92d0ff6b5
7 changed files with 85 additions and 45 deletions

View File

@ -2099,7 +2099,22 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
= event->m_eedge.m_src->get_state ().m_region_model; = event->m_eedge.m_src->get_state ().m_region_model;
tree callee_var = callee_model->get_representative_tree (sval); tree callee_var = callee_model->get_representative_tree (sval);
callsite_expr expr; callsite_expr expr;
tree caller_var = caller_model->get_representative_tree (sval);
tree caller_var;
if(event->m_sedge)
{
const callgraph_superedge& cg_superedge
= event->get_callgraph_superedge ();
if (cg_superedge.m_cedge)
caller_var
= cg_superedge.map_expr_from_callee_to_caller (callee_var,
&expr);
else
callee_var = callee_model->get_representative_tree (sval);
}
else
caller_var = caller_model->get_representative_tree (sval);
if (caller_var) if (caller_var)
{ {
if (get_logger ()) if (get_logger ())
@ -2121,11 +2136,28 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
if (sval) if (sval)
{ {
return_event *event = (return_event *)base_event; return_event *event = (return_event *)base_event;
const region_model *caller_model
= event->m_eedge.m_dest->get_state ().m_region_model;
tree caller_var = caller_model->get_representative_tree (sval);
const region_model *callee_model
= event->m_eedge.m_src->get_state ().m_region_model;
callsite_expr expr; callsite_expr expr;
const region_model *callee_model tree callee_var;
= event->m_eedge.m_src->get_state ().m_region_model; if (event->m_sedge)
tree callee_var = callee_model->get_representative_tree (sval); {
const callgraph_superedge& cg_superedge
= event->get_callgraph_superedge ();
if (cg_superedge.m_cedge)
callee_var
= cg_superedge.map_expr_from_caller_to_callee (caller_var,
&expr);
else
callee_var = callee_model->get_representative_tree (sval);
}
else
callee_var = callee_model->get_representative_tree (sval);
if (callee_var) if (callee_var)
{ {
if (get_logger ()) if (get_logger ())

View File

@ -3033,14 +3033,14 @@ state_change_requires_new_enode_p (const program_state &old_state,
Some example such calls are dynamically dispatched calls to virtual Some example such calls are dynamically dispatched calls to virtual
functions or calls that happen via function pointer. */ functions or calls that happen via function pointer. */
void bool
exploded_graph::create_dynamic_call (const gcall *call, exploded_graph::maybe_create_dynamic_call (const gcall *call,
tree fn_decl, tree fn_decl,
exploded_node *node, exploded_node *node,
program_state next_state, program_state next_state,
program_point &next_point, program_point &next_point,
uncertainty_t *uncertainty, uncertainty_t *uncertainty,
logger *logger) logger *logger)
{ {
LOG_FUNC (logger); LOG_FUNC (logger);
@ -3049,8 +3049,8 @@ exploded_graph::create_dynamic_call (const gcall *call,
if (fun) if (fun)
{ {
const supergraph &sg = this->get_supergraph (); const supergraph &sg = this->get_supergraph ();
supernode * sn_entry = sg.get_node_for_function_entry (fun); supernode *sn_entry = sg.get_node_for_function_entry (fun);
supernode * sn_exit = sg.get_node_for_function_exit (fun); supernode *sn_exit = sg.get_node_for_function_exit (fun);
program_point new_point program_point new_point
= program_point::before_supernode (sn_entry, = program_point::before_supernode (sn_entry,
@ -3075,8 +3075,10 @@ exploded_graph::create_dynamic_call (const gcall *call,
if (enode) if (enode)
add_edge (node,enode, NULL, add_edge (node,enode, NULL,
new dynamic_call_info_t (call)); new dynamic_call_info_t (call));
return true;
} }
} }
return false;
} }
/* The core of exploded_graph::process_worklist (the main analysis loop), /* The core of exploded_graph::process_worklist (the main analysis loop),
@ -3338,22 +3340,23 @@ exploded_graph::process_node (exploded_node *node)
point.get_stmt()); point.get_stmt());
region_model *model = state.m_region_model; region_model *model = state.m_region_model;
bool call_discovered = false;
if (tree fn_decl = model->get_fndecl_for_call(call,&ctxt)) if (tree fn_decl = model->get_fndecl_for_call(call,&ctxt))
create_dynamic_call (call, call_discovered = maybe_create_dynamic_call (call,
fn_decl, fn_decl,
node, node,
next_state, next_state,
next_point, next_point,
&uncertainty, &uncertainty,
logger); logger);
else if (!call_discovered)
{ {
/* An unknown function was called at this point, in such /* An unknown function or a special function was called
case, don't terminate the analysis of the current at this point, in such case, don't terminate the
function. analysis of the current function.
The analyzer handles calls to unknown functions while The analyzer handles calls to such functions while
analysing the stmt itself, so the the function call analysing the stmt itself, so the the function call
must have been handled by the anlyzer till now. */ must have been handled by the anlyzer till now. */
exploded_node *next exploded_node *next

View File

@ -816,13 +816,13 @@ public:
bool maybe_process_run_of_before_supernode_enodes (exploded_node *node); bool maybe_process_run_of_before_supernode_enodes (exploded_node *node);
void process_node (exploded_node *node); void process_node (exploded_node *node);
void create_dynamic_call (const gcall *call, bool maybe_create_dynamic_call (const gcall *call,
tree fn_decl, tree fn_decl,
exploded_node *node, exploded_node *node,
program_state next_state, program_state next_state,
program_point &next_point, program_point &next_point,
uncertainty_t *uncertainty, uncertainty_t *uncertainty,
logger *logger); logger *logger);
exploded_node *get_or_create_node (const program_point &point, exploded_node *get_or_create_node (const program_point &point,
const program_state &state, const program_state &state,

View File

@ -3178,7 +3178,8 @@ region_model::maybe_update_for_edge (const superedge &edge,
void void
region_model::update_for_gcall (const gcall *call_stmt, region_model::update_for_gcall (const gcall *call_stmt,
region_model_context *ctxt) region_model_context *ctxt,
function *callee)
{ {
/* Build a vec of argument svalues, using the current top /* Build a vec of argument svalues, using the current top
frame for resolving tree expressions. */ frame for resolving tree expressions. */
@ -3190,10 +3191,14 @@ region_model::update_for_gcall (const gcall *call_stmt,
arg_svals.quick_push (get_rvalue (arg, ctxt)); arg_svals.quick_push (get_rvalue (arg, ctxt));
} }
/* Get the function * from the call. */ if(!callee)
tree fn_decl = get_fndecl_for_call (call_stmt,ctxt); {
function *fun = DECL_STRUCT_FUNCTION (fn_decl); /* Get the function * from the gcall. */
push_frame (fun, &arg_svals, ctxt); tree fn_decl = get_fndecl_for_call (call_stmt,ctxt);
callee = DECL_STRUCT_FUNCTION (fn_decl);
}
push_frame (callee, &arg_svals, ctxt);
} }
/* Pop the top-most frame_region from the stack, and copy the return /* Pop the top-most frame_region from the stack, and copy the return
@ -3228,7 +3233,7 @@ region_model::update_for_call_superedge (const call_superedge &call_edge,
region_model_context *ctxt) region_model_context *ctxt)
{ {
const gcall *call_stmt = call_edge.get_call_stmt (); const gcall *call_stmt = call_edge.get_call_stmt ();
update_for_gcall (call_stmt,ctxt); update_for_gcall (call_stmt, ctxt, call_edge.get_callee_function ());
} }
/* Extract calling information from the return superedge and update the model /* Extract calling information from the return superedge and update the model

View File

@ -609,7 +609,8 @@ class region_model
rejected_constraint **out); rejected_constraint **out);
void update_for_gcall (const gcall *call_stmt, void update_for_gcall (const gcall *call_stmt,
region_model_context *ctxt); region_model_context *ctxt,
function *callee = NULL);
void update_for_return_gcall (const gcall *call_stmt, void update_for_return_gcall (const gcall *call_stmt,
region_model_context *ctxt); region_model_context *ctxt);

View File

@ -6,9 +6,10 @@ typedef void (*fn_ptr_t) (void *);
void void
calls_free (void *victim) calls_free (void *victim)
{ {
free (victim); free (victim); /* { dg-warning "double-'free' of 'victim'" } */
} }
void void
no_op (void *ptr) no_op (void *ptr)
{ {
@ -25,7 +26,6 @@ void test_1 (void *ptr)
fn_ptr (ptr); fn_ptr (ptr);
fn_ptr (ptr); fn_ptr (ptr);
} }
// TODO: issue a double-'free' warning at 2nd call to fn_ptr.
/* As above, but with an extra indirection to try to thwart /* As above, but with an extra indirection to try to thwart
the optimizer. */ the optimizer. */
@ -41,4 +41,3 @@ void test_2 (void *ptr, fn_ptr_t *fn_ptr)
(*fn_ptr) (ptr); (*fn_ptr) (ptr);
(*fn_ptr) (ptr); (*fn_ptr) (ptr);
} }
// TODO: issue a double-'free' warning at 2nd call to fn_ptr.

View File

@ -69,7 +69,7 @@ int *test_5 (void)
static void __attribute__((noinline)) static void __attribute__((noinline))
called_by_test_6a (void *ptr) called_by_test_6a (void *ptr)
{ {
free (ptr); /* { dg-warning "double-'free'" "" { xfail *-*-* } } */ free (ptr); /* { dg-warning "double-'free'"} */
} }
static deallocator_t __attribute__((noinline)) static deallocator_t __attribute__((noinline))