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;
tree callee_var = callee_model->get_representative_tree (sval);
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 (get_logger ())
@ -2121,11 +2136,28 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
if (sval)
{
return_event *event = (return_event *)base_event;
callsite_expr expr;
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;
tree callee_var = callee_model->get_representative_tree (sval);
callsite_expr expr;
tree callee_var;
if (event->m_sedge)
{
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 (get_logger ())

View File

@ -3033,8 +3033,8 @@ state_change_requires_new_enode_p (const program_state &old_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,
bool
exploded_graph::maybe_create_dynamic_call (const gcall *call,
tree fn_decl,
exploded_node *node,
program_state next_state,
@ -3049,8 +3049,8 @@ exploded_graph::create_dynamic_call (const gcall *call,
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);
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,
@ -3075,8 +3075,10 @@ exploded_graph::create_dynamic_call (const gcall *call,
if (enode)
add_edge (node,enode, NULL,
new dynamic_call_info_t (call));
return true;
}
}
return false;
}
/* 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());
region_model *model = state.m_region_model;
bool call_discovered = false;
if (tree fn_decl = model->get_fndecl_for_call(call,&ctxt))
create_dynamic_call (call,
call_discovered = maybe_create_dynamic_call (call,
fn_decl,
node,
next_state,
next_point,
&uncertainty,
logger);
else
if (!call_discovered)
{
/* An unknown function was called at this point, in such
case, don't terminate the analysis of the current
function.
/* An unknown function or a special 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
The analyzer handles calls to such functions while
analysing the stmt itself, so the the function call
must have been handled by the anlyzer till now. */
exploded_node *next

View File

@ -816,7 +816,7 @@ 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,
bool maybe_create_dynamic_call (const gcall *call,
tree fn_decl,
exploded_node *node,
program_state next_state,

View File

@ -3178,7 +3178,8 @@ region_model::maybe_update_for_edge (const superedge &edge,
void
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
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));
}
/* Get the function * from the call. */
if(!callee)
{
/* Get the function * from the gcall. */
tree fn_decl = get_fndecl_for_call (call_stmt,ctxt);
function *fun = DECL_STRUCT_FUNCTION (fn_decl);
push_frame (fun, &arg_svals, 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
@ -3228,7 +3233,7 @@ 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);
update_for_gcall (call_stmt, ctxt, call_edge.get_callee_function ());
}
/* Extract calling information from the return superedge and update the model

View File

@ -609,7 +609,8 @@ class region_model
rejected_constraint **out);
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,
region_model_context *ctxt);

View File

@ -6,9 +6,10 @@ typedef void (*fn_ptr_t) (void *);
void
calls_free (void *victim)
{
free (victim);
free (victim); /* { dg-warning "double-'free' of 'victim'" } */
}
void
no_op (void *ptr)
{
@ -25,7 +26,6 @@ void test_1 (void *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
the optimizer. */
@ -41,4 +41,3 @@ void test_2 (void *ptr, fn_ptr_t *fn_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))
called_by_test_6a (void *ptr)
{
free (ptr); /* { dg-warning "double-'free'" "" { xfail *-*-* } } */
free (ptr); /* { dg-warning "double-'free'"} */
}
static deallocator_t __attribute__((noinline))