2
0
mirror of git://gcc.gnu.org/git/gcc.git synced 2025-04-09 15:51:08 +08:00

analyzer: split out more stuff from region-model-impl-calls.cc

gcc/ChangeLog:
	* Makefile.in (ANALYZER_OBJS): Add analyzer/call-details.o,
	analyzer/kf-analyzer.o, and kf-lang-cp.o.

gcc/analyzer/ChangeLog:
	* analyzer.h (register_known_analyzer_functions): New decl.
	(register_known_functions_lang_cp): New decl.
	* call-details.cc: New file, split out from
	region-model-impl-calls.cc.
	* call-details.h: New file, split out from region-model.h.
	* call-info.cc: Include "analyzer/call-details.h".
	* call-summary.h: Likewise.
	* kf-analyzer.cc: New file, split out from
	region-model-impl-calls.cc.
	* kf-lang-cp.cc: Likewise.
	* known-function-manager.cc: Include "analyzer/call-details.h".
	* region-model-impl-calls.cc: Move definitions of call_details's
	member functions to call-details.cc.  Move class kf_analyzer_* to
	kf-analyzer.cc.  Move kf_operator_new and kf_operator_delete to
	kf-lang-cp.cc.  Refresh #includes accordingly.
	(register_known_functions): Replace registration of __analyzer_*
	functions with a call to register_known_analyzer_functions.
	Replace registration of C++ support functions with a call to
	register_known_functions_lang_cp.
	* region-model.h (class call_details): Move to new call-details.h.
	* sm-fd.cc: Include "analyzer/call-details.h".
	* sm-file.cc: Likewise.
	* sm-malloc.cc: Likewise.
	* varargs.cc: Likewise.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/analyzer_kernel_plugin.c: Include
	"analyzer/call-details.h".
	* gcc.dg/plugin/analyzer_known_fns_plugin.c: Likewise.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2022-12-06 13:26:56 -05:00
parent 81476bc4f4
commit 861c917a97
17 changed files with 827 additions and 662 deletions

@ -1256,6 +1256,7 @@ ANALYZER_OBJS = \
analyzer/analyzer-selftests.o \
analyzer/bar-chart.o \
analyzer/bounds-checking.o \
analyzer/call-details.o \
analyzer/call-info.o \
analyzer/call-string.o \
analyzer/call-summary.o \
@ -1268,6 +1269,8 @@ ANALYZER_OBJS = \
analyzer/feasible-graph.o \
analyzer/function-set.o \
analyzer/infinite-recursion.o \
analyzer/kf-analyzer.o \
analyzer/kf-lang-cp.o \
analyzer/known-function-manager.o \
analyzer/pending-diagnostic.o \
analyzer/program-point.o \

@ -259,8 +259,10 @@ public:
};
extern void register_known_functions (known_function_manager &mgr);
extern void register_known_analyzer_functions (known_function_manager &kfm);
extern void register_known_fd_functions (known_function_manager &kfm);
extern void register_known_file_functions (known_function_manager &kfm);
extern void register_known_functions_lang_cp (known_function_manager &kfm);
extern void register_varargs_builtins (known_function_manager &kfm);
/* Passed by pointer to PLUGIN_ANALYZER_INIT callbacks. */

@ -0,0 +1,231 @@
/* Helper class for handling a call with specific arguments.
Copyright (C) 2020-2022 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#define INCLUDE_MEMORY
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "function.h"
#include "basic-block.h"
#include "gimple.h"
#include "diagnostic-core.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "diagnostic.h"
#include "tree-diagnostic.h" /* for default_tree_printer. */
#include "gimple-pretty-print.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#if ENABLE_ANALYZER
namespace ana {
/* class call_details. */
/* call_details's ctor. */
call_details::call_details (const gcall *call, region_model *model,
region_model_context *ctxt)
: m_call (call), m_model (model), m_ctxt (ctxt),
m_lhs_type (NULL_TREE), m_lhs_region (NULL)
{
m_lhs_type = NULL_TREE;
if (tree lhs = gimple_call_lhs (call))
{
m_lhs_region = model->get_lvalue (lhs, ctxt);
m_lhs_type = TREE_TYPE (lhs);
}
}
/* Get the manager from m_model. */
region_model_manager *
call_details::get_manager () const
{
return m_model->get_manager ();
}
/* Get any logger associated with this object. */
logger *
call_details::get_logger () const
{
if (m_ctxt)
return m_ctxt->get_logger ();
else
return NULL;
}
/* Get any uncertainty_t associated with the region_model_context. */
uncertainty_t *
call_details::get_uncertainty () const
{
if (m_ctxt)
return m_ctxt->get_uncertainty ();
else
return NULL;
}
/* If the callsite has a left-hand-side region, set it to RESULT
and return true.
Otherwise do nothing and return false. */
bool
call_details::maybe_set_lhs (const svalue *result) const
{
gcc_assert (result);
if (m_lhs_region)
{
m_model->set_value (m_lhs_region, result, m_ctxt);
return true;
}
else
return false;
}
/* Return the number of arguments used by the call statement. */
unsigned
call_details::num_args () const
{
return gimple_call_num_args (m_call);
}
/* Return true if argument IDX is a size_t (or compatible with it). */
bool
call_details::arg_is_size_p (unsigned idx) const
{
return types_compatible_p (get_arg_type (idx), size_type_node);
}
/* Get the location of the call statement. */
location_t
call_details::get_location () const
{
return m_call->location;
}
/* Get argument IDX at the callsite as a tree. */
tree
call_details::get_arg_tree (unsigned idx) const
{
return gimple_call_arg (m_call, idx);
}
/* Get the type of argument IDX. */
tree
call_details::get_arg_type (unsigned idx) const
{
return TREE_TYPE (gimple_call_arg (m_call, idx));
}
/* Get argument IDX at the callsite as an svalue. */
const svalue *
call_details::get_arg_svalue (unsigned idx) const
{
tree arg = get_arg_tree (idx);
return m_model->get_rvalue (arg, m_ctxt);
}
/* Attempt to get the string literal for argument IDX, or return NULL
otherwise.
For use when implementing "__analyzer_*" functions that take
string literals. */
const char *
call_details::get_arg_string_literal (unsigned idx) const
{
const svalue *str_arg = get_arg_svalue (idx);
if (const region *pointee = str_arg->maybe_get_region ())
if (const string_region *string_reg = pointee->dyn_cast_string_region ())
{
tree string_cst = string_reg->get_string_cst ();
return TREE_STRING_POINTER (string_cst);
}
return NULL;
}
/* Attempt to get the fndecl used at this call, if known, or NULL_TREE
otherwise. */
tree
call_details::get_fndecl_for_call () const
{
return m_model->get_fndecl_for_call (m_call, m_ctxt);
}
/* Dump a multiline representation of this call to PP. */
void
call_details::dump_to_pp (pretty_printer *pp, bool simple) const
{
pp_string (pp, "gcall: ");
pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
pp_newline (pp);
pp_string (pp, "return region: ");
if (m_lhs_region)
m_lhs_region->dump_to_pp (pp, simple);
else
pp_string (pp, "NULL");
pp_newline (pp);
for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
{
const svalue *arg_sval = get_arg_svalue (i);
pp_printf (pp, "arg %i: ", i);
arg_sval->dump_to_pp (pp, simple);
pp_newline (pp);
}
}
/* Dump a multiline representation of this call to stderr. */
DEBUG_FUNCTION void
call_details::dump (bool simple) const
{
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_show_color (&pp) = pp_show_color (global_dc->printer);
pp.buffer->stream = stderr;
dump_to_pp (&pp, simple);
pp_flush (&pp);
}
/* Get a conjured_svalue for this call for REG,
and purge any state already relating to that conjured_svalue. */
const svalue *
call_details::get_or_create_conjured_svalue (const region *reg) const
{
region_model_manager *mgr = m_model->get_manager ();
return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
conjured_purge (m_model, m_ctxt));
}
} // namespace ana
#endif /* #if ENABLE_ANALYZER */

@ -0,0 +1,77 @@
/* Helper class for handling a call with specific arguments.
Copyright (C) 2019-2022 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_ANALYZER_CALL_DETAILS_H
#define GCC_ANALYZER_CALL_DETAILS_H
namespace ana {
/* Helper class for handling calls to functions with known behavior. */
class call_details
{
public:
call_details (const gcall *call, region_model *model,
region_model_context *ctxt);
region_model *get_model () const { return m_model; }
region_model_manager *get_manager () const;
region_model_context *get_ctxt () const { return m_ctxt; }
logger *get_logger () const;
uncertainty_t *get_uncertainty () const;
tree get_lhs_type () const { return m_lhs_type; }
const region *get_lhs_region () const { return m_lhs_region; }
bool maybe_set_lhs (const svalue *result) const;
unsigned num_args () const;
bool arg_is_pointer_p (unsigned idx) const
{
return POINTER_TYPE_P (get_arg_type (idx));
}
bool arg_is_size_p (unsigned idx) const;
const gcall *get_call_stmt () const { return m_call; }
location_t get_location () const;
tree get_arg_tree (unsigned idx) const;
tree get_arg_type (unsigned idx) const;
const svalue *get_arg_svalue (unsigned idx) const;
const char *get_arg_string_literal (unsigned idx) const;
tree get_fndecl_for_call () const;
void dump_to_pp (pretty_printer *pp, bool simple) const;
void dump (bool simple) const;
const svalue *get_or_create_conjured_svalue (const region *) const;
private:
const gcall *m_call;
region_model *m_model;
region_model_context *m_ctxt;
tree m_lhs_type;
const region *m_lhs_region;
};
} // namespace ana
#endif /* GCC_ANALYZER_CALL_DETAILS_H */

@ -54,6 +54,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/checker-path.h"
#include "analyzer/diagnostic-manager.h"
#include "analyzer/exploded-graph.h"
#include "analyzer/call-details.h"
#include "analyzer/call-info.h"
#include "make-unique.h"

@ -20,6 +20,8 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_ANALYZER_CALL_SUMMARY_H
#define GCC_ANALYZER_CALL_SUMMARY_H
#include "call-details.h"
namespace ana {
/* A class summarizing one particular outcome of a function that

386
gcc/analyzer/kf-analyzer.cc Normal file

@ -0,0 +1,386 @@
/* Handling for the various __analyzer_* known functions.
Copyright (C) 2020-2022 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#define INCLUDE_MEMORY
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "function.h"
#include "basic-block.h"
#include "gimple.h"
#include "diagnostic-core.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "diagnostic.h"
#include "tree-diagnostic.h" /* for default_tree_printer. */
#include "analyzer/region-model.h"
#include "analyzer/pending-diagnostic.h"
#include "analyzer/call-details.h"
#include "make-unique.h"
#if ENABLE_ANALYZER
namespace ana {
/* Handle calls to "__analyzer_break" by triggering a breakpoint within
the analyzer. */
class kf_analyzer_break : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &) const final override
{
/* TODO: is there a good cross-platform way to do this? */
raise (SIGINT);
}
};
/* Handler for calls to "__analyzer_describe".
Emit a warning describing the 2nd argument (which can be of any
type), at the given verbosity level. This is for use when
debugging, and may be of use in DejaGnu tests. */
class kf_analyzer_describe : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 2;
}
void impl_call_pre (const call_details &cd) const final override
{
if (!cd.get_ctxt ())
return;
tree t_verbosity = cd.get_arg_tree (0);
const svalue *sval = cd.get_arg_svalue (1);
bool simple = zerop (t_verbosity);
label_text desc = sval->get_desc (simple);
warning_at (cd.get_location (), 0, "svalue: %qs", desc.get ());
}
};
/* Handler for calls to "__analyzer_dump_capacity".
Emit a warning describing the capacity of the base region of
the region pointed to by the 1st argument.
This is for use when debugging, and may be of use in DejaGnu tests. */
class kf_analyzer_dump_capacity : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return (cd.num_args () == 1
&& cd.arg_is_pointer_p (0));
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
region_model *model = cd.get_model ();
tree t_ptr = cd.get_arg_tree (0);
const svalue *sval_ptr = model->get_rvalue (t_ptr, ctxt);
const region *reg = model->deref_rvalue (sval_ptr, t_ptr, ctxt);
const region *base_reg = reg->get_base_region ();
const svalue *capacity = model->get_capacity (base_reg);
label_text desc = capacity->get_desc (true);
warning_at (cd.get_call_stmt ()->location, 0,
"capacity: %qs", desc.get ());
}
};
/* Compare D1 and D2 using their names, and then IDs to order them. */
static int
cmp_decls (tree d1, tree d2)
{
gcc_assert (DECL_P (d1));
gcc_assert (DECL_P (d2));
if (DECL_NAME (d1) && DECL_NAME (d2))
if (int cmp = strcmp (IDENTIFIER_POINTER (DECL_NAME (d1)),
IDENTIFIER_POINTER (DECL_NAME (d2))))
return cmp;
return (int)DECL_UID (d1) - (int)DECL_UID (d2);
}
/* Comparator for use by vec<tree>::qsort,
using their names, and then IDs to order them. */
static int
cmp_decls_ptr_ptr (const void *p1, const void *p2)
{
tree const *d1 = (tree const *)p1;
tree const *d2 = (tree const *)p2;
return cmp_decls (*d1, *d2);
}
/* Handler for calls to "__analyzer_dump_escaped".
Emit a warning giving the number of decls that have escaped, followed
by a comma-separated list of their names, in alphabetical order.
This is for use when debugging, and may be of use in DejaGnu tests. */
class kf_analyzer_dump_escaped : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
region_model *model = cd.get_model ();
auto_vec<tree> escaped_decls;
for (auto iter : *model->get_store ())
{
const binding_cluster *c = iter.second;
if (!c->escaped_p ())
continue;
if (tree decl = c->get_base_region ()->maybe_get_decl ())
escaped_decls.safe_push (decl);
}
/* Sort them into deterministic order; alphabetical is
probably most user-friendly. */
escaped_decls.qsort (cmp_decls_ptr_ptr);
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_show_color (&pp) = pp_show_color (global_dc->printer);
bool first = true;
for (auto iter : escaped_decls)
{
if (first)
first = false;
else
pp_string (&pp, ", ");
pp_printf (&pp, "%qD", iter);
}
/* Print the number to make it easier to write DejaGnu tests for
the "nothing has escaped" case. */
warning_at (cd.get_location (), 0, "escaped: %i: %s",
escaped_decls.length (),
pp_formatted_text (&pp));
}
};
/* Placeholder handler for calls to "__analyzer_dump_exploded_nodes".
This is a no-op; the real implementation happens when the
exploded_graph is postprocessed. */
class kf_analyzer_dump_exploded_nodes : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 1;
}
};
/* Handler for calls to "__analyzer_dump_named_constant".
Look up the given name, and emit a warning describing the
state of the corresponding stashed value.
This is for use when debugging, and for DejaGnu tests. */
class kf_analyzer_dump_named_constant : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 1;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
const char *name = cd.get_arg_string_literal (0);
if (!name)
{
error_at (cd.get_location (), "cannot determine name");
return;
}
tree value = get_stashed_constant_by_name (name);
if (value)
warning_at (cd.get_location (), 0, "named constant %qs has value %qE",
name, value);
else
warning_at (cd.get_location (), 0, "named constant %qs has unknown value",
name);
}
};
/* A pending_diagnostic subclass for implementing "__analyzer_dump_path". */
class dump_path_diagnostic
: public pending_diagnostic_subclass<dump_path_diagnostic>
{
public:
int get_controlling_option () const final override
{
return 0;
}
bool emit (rich_location *richloc) final override
{
inform (richloc, "path");
return true;
}
const char *get_kind () const final override
{
return "dump_path_diagnostic";
}
bool operator== (const dump_path_diagnostic &) const
{
return true;
}
};
/* Handle calls to "__analyzer_dump_path" by queuing a diagnostic at this
exploded_node. */
class kf_analyzer_dump_path : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
ctxt->warn (make_unique<dump_path_diagnostic> ());
}
};
/* Handle calls to "__analyzer_dump_region_model" by dumping
the region model's state to stderr. */
class kf_analyzer_dump_region_model : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
region_model *model = cd.get_model ();
model->dump (false);
}
};
/* Handle a call to "__analyzer_eval" by evaluating the input
and dumping as a dummy warning, so that test cases can use
dg-warning to validate the result (and so unexpected warnings will
lead to DejaGnu failures).
Broken out as a subroutine to make it easier to put a breakpoint on it
- though typically this doesn't help, as we have an SSA name as the arg,
and what's more interesting is usually the def stmt for that name. */
class kf_analyzer_eval : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 1;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
region_model *model = cd.get_model ();
tree t_arg = cd.get_arg_tree (0);
tristate t = model->eval_condition (t_arg, NE_EXPR, integer_zero_node,
ctxt);
warning_at (cd.get_location (), 0, "%s", t.as_string ());
}
};
/* Handler for "__analyzer_get_unknown_ptr". */
class kf_analyzer_get_unknown_ptr : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_manager *mgr = cd.get_manager ();
const svalue *ptr_sval
= mgr->get_or_create_unknown_svalue (cd.get_lhs_type ());
cd.maybe_set_lhs (ptr_sval);
}
};
/* Populate KFM with instances of known functions used for debugging the
analyzer and for writing DejaGnu tests, all with a "__analyzer_" prefix. */
void
register_known_analyzer_functions (known_function_manager &kfm)
{
kfm.add ("__analyzer_break", make_unique<kf_analyzer_break> ());
kfm.add ("__analyzer_describe", make_unique<kf_analyzer_describe> ());
kfm.add ("__analyzer_dump_capacity",
make_unique<kf_analyzer_dump_capacity> ());
kfm.add ("__analyzer_dump_escaped", make_unique<kf_analyzer_dump_escaped> ());
kfm.add ("__analyzer_dump_exploded_nodes",
make_unique<kf_analyzer_dump_exploded_nodes> ());
kfm.add ("__analyzer_dump_named_constant",
make_unique<kf_analyzer_dump_named_constant> ());
kfm.add ("__analyzer_dump_path", make_unique<kf_analyzer_dump_path> ());
kfm.add ("__analyzer_dump_region_model",
make_unique<kf_analyzer_dump_region_model> ());
kfm.add ("__analyzer_eval", make_unique<kf_analyzer_eval> ());
kfm.add ("__analyzer_get_unknown_ptr",
make_unique<kf_analyzer_get_unknown_ptr> ());
}
} // namespace ana
#endif /* #if ENABLE_ANALYZER */

111
gcc/analyzer/kf-lang-cp.cc Normal file

@ -0,0 +1,111 @@
/* Handling for the known behavior of various functions specific to C++.
Copyright (C) 2020-2022 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#define INCLUDE_MEMORY
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "function.h"
#include "basic-block.h"
#include "gimple.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "diagnostic.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#include "make-unique.h"
#if ENABLE_ANALYZER
namespace ana {
/* Implementations of specific functions. */
/* Handler for "operator new" and "operator new []". */
class kf_operator_new : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 1;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model *model = cd.get_model ();
region_model_manager *mgr = cd.get_manager ();
const svalue *size_sval = cd.get_arg_svalue (0);
const region *new_reg
= model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
if (cd.get_lhs_type ())
{
const svalue *ptr_sval
= mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
cd.maybe_set_lhs (ptr_sval);
}
}
};
/* Handler for "operator delete", both the sized and unsized variants
(2 arguments and 1 argument respectively), and for "operator delete []" */
class kf_operator_delete : public known_function
{
public:
kf_operator_delete (unsigned num_args) : m_num_args (num_args) {}
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == m_num_args;
}
void impl_call_post (const call_details &cd) const final override
{
region_model *model = cd.get_model ();
const svalue *ptr_sval = cd.get_arg_svalue (0);
if (const region *freed_reg = ptr_sval->maybe_get_region ())
{
/* If the ptr points to an underlying heap region, delete it,
poisoning pointers. */
model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED);
}
}
private:
unsigned m_num_args;
};
/* Populate KFM with instances of known functions relating to C++. */
void
register_known_functions_lang_cp (known_function_manager &kfm)
{
kfm.add ("operator new", make_unique<kf_operator_new> ());
kfm.add ("operator new []", make_unique<kf_operator_new> ());
kfm.add ("operator delete", make_unique<kf_operator_delete> (1));
kfm.add ("operator delete", make_unique<kf_operator_delete> (2));
kfm.add ("operator delete []", make_unique<kf_operator_delete> (1));
}
} // namespace ana
#endif /* #if ENABLE_ANALYZER */

@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h"
#include "analyzer/known-function-manager.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#if ENABLE_ANALYZER

@ -26,228 +26,20 @@ along with GCC; see the file COPYING3. If not see
#include "function.h"
#include "basic-block.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "diagnostic-core.h"
#include "graphviz.h"
#include "options.h"
#include "cgraph.h"
#include "tree-dfa.h"
#include "stringpool.h"
#include "convert.h"
#include "target.h"
#include "fold-const.h"
#include "tree-pretty-print.h"
#include "diagnostic-color.h"
#include "diagnostic-metadata.h"
#include "bitmap.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "ordered-hash-map.h"
#include "options.h"
#include "analyzer/supergraph.h"
#include "sbitmap.h"
#include "analyzer/call-string.h"
#include "analyzer/program-point.h"
#include "analyzer/store.h"
#include "diagnostic.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#include "analyzer/call-info.h"
#include "analyzer/sm.h"
#include "diagnostic-path.h"
#include "analyzer/pending-diagnostic.h"
#include "gimple-pretty-print.h"
#include "make-unique.h"
#if ENABLE_ANALYZER
namespace ana {
/* class call_details. */
/* call_details's ctor. */
call_details::call_details (const gcall *call, region_model *model,
region_model_context *ctxt)
: m_call (call), m_model (model), m_ctxt (ctxt),
m_lhs_type (NULL_TREE), m_lhs_region (NULL)
{
m_lhs_type = NULL_TREE;
if (tree lhs = gimple_call_lhs (call))
{
m_lhs_region = model->get_lvalue (lhs, ctxt);
m_lhs_type = TREE_TYPE (lhs);
}
}
/* Get the manager from m_model. */
region_model_manager *
call_details::get_manager () const
{
return m_model->get_manager ();
}
/* Get any logger associated with this object. */
logger *
call_details::get_logger () const
{
if (m_ctxt)
return m_ctxt->get_logger ();
else
return NULL;
}
/* Get any uncertainty_t associated with the region_model_context. */
uncertainty_t *
call_details::get_uncertainty () const
{
if (m_ctxt)
return m_ctxt->get_uncertainty ();
else
return NULL;
}
/* If the callsite has a left-hand-side region, set it to RESULT
and return true.
Otherwise do nothing and return false. */
bool
call_details::maybe_set_lhs (const svalue *result) const
{
gcc_assert (result);
if (m_lhs_region)
{
m_model->set_value (m_lhs_region, result, m_ctxt);
return true;
}
else
return false;
}
/* Return the number of arguments used by the call statement. */
unsigned
call_details::num_args () const
{
return gimple_call_num_args (m_call);
}
/* Return true if argument IDX is a size_t (or compatible with it). */
bool
call_details::arg_is_size_p (unsigned idx) const
{
return types_compatible_p (get_arg_type (idx), size_type_node);
}
/* Get the location of the call statement. */
location_t
call_details::get_location () const
{
return m_call->location;
}
/* Get argument IDX at the callsite as a tree. */
tree
call_details::get_arg_tree (unsigned idx) const
{
return gimple_call_arg (m_call, idx);
}
/* Get the type of argument IDX. */
tree
call_details::get_arg_type (unsigned idx) const
{
return TREE_TYPE (gimple_call_arg (m_call, idx));
}
/* Get argument IDX at the callsite as an svalue. */
const svalue *
call_details::get_arg_svalue (unsigned idx) const
{
tree arg = get_arg_tree (idx);
return m_model->get_rvalue (arg, m_ctxt);
}
/* Attempt to get the string literal for argument IDX, or return NULL
otherwise.
For use when implementing "__analyzer_*" functions that take
string literals. */
const char *
call_details::get_arg_string_literal (unsigned idx) const
{
const svalue *str_arg = get_arg_svalue (idx);
if (const region *pointee = str_arg->maybe_get_region ())
if (const string_region *string_reg = pointee->dyn_cast_string_region ())
{
tree string_cst = string_reg->get_string_cst ();
return TREE_STRING_POINTER (string_cst);
}
return NULL;
}
/* Attempt to get the fndecl used at this call, if known, or NULL_TREE
otherwise. */
tree
call_details::get_fndecl_for_call () const
{
return m_model->get_fndecl_for_call (m_call, m_ctxt);
}
/* Dump a multiline representation of this call to PP. */
void
call_details::dump_to_pp (pretty_printer *pp, bool simple) const
{
pp_string (pp, "gcall: ");
pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
pp_newline (pp);
pp_string (pp, "return region: ");
if (m_lhs_region)
m_lhs_region->dump_to_pp (pp, simple);
else
pp_string (pp, "NULL");
pp_newline (pp);
for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
{
const svalue *arg_sval = get_arg_svalue (i);
pp_printf (pp, "arg %i: ", i);
arg_sval->dump_to_pp (pp, simple);
pp_newline (pp);
}
}
/* Dump a multiline representation of this call to stderr. */
DEBUG_FUNCTION void
call_details::dump (bool simple) const
{
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_show_color (&pp) = pp_show_color (global_dc->printer);
pp.buffer->stream = stderr;
dump_to_pp (&pp, simple);
pp_flush (&pp);
}
/* Get a conjured_svalue for this call for REG,
and purge any state already relating to that conjured_svalue. */
const svalue *
call_details::get_or_create_conjured_svalue (const region *reg) const
{
region_model_manager *mgr = m_model->get_manager ();
return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
conjured_purge (m_model, m_ctxt));
}
/* Implementations of specific functions. */
/* Handler for "alloca". */
@ -277,324 +69,6 @@ kf_alloca::impl_call_pre (const call_details &cd) const
cd.maybe_set_lhs (ptr_sval);
}
/* Handle calls to "__analyzer_break" by triggering a breakpoint within
the analyzer. */
class kf_analyzer_break : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &) const final override
{
/* TODO: is there a good cross-platform way to do this? */
raise (SIGINT);
}
};
/* Handler for calls to "__analyzer_describe".
Emit a warning describing the 2nd argument (which can be of any
type), at the given verbosity level. This is for use when
debugging, and may be of use in DejaGnu tests. */
class kf_analyzer_describe : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 2;
}
void impl_call_pre (const call_details &cd) const final override
{
if (!cd.get_ctxt ())
return;
tree t_verbosity = cd.get_arg_tree (0);
const svalue *sval = cd.get_arg_svalue (1);
bool simple = zerop (t_verbosity);
label_text desc = sval->get_desc (simple);
warning_at (cd.get_location (), 0, "svalue: %qs", desc.get ());
}
};
/* Handler for calls to "__analyzer_dump_capacity".
Emit a warning describing the capacity of the base region of
the region pointed to by the 1st argument.
This is for use when debugging, and may be of use in DejaGnu tests. */
class kf_analyzer_dump_capacity : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return (cd.num_args () == 1
&& cd.arg_is_pointer_p (0));
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
region_model *model = cd.get_model ();
tree t_ptr = cd.get_arg_tree (0);
const svalue *sval_ptr = model->get_rvalue (t_ptr, ctxt);
const region *reg = model->deref_rvalue (sval_ptr, t_ptr, ctxt);
const region *base_reg = reg->get_base_region ();
const svalue *capacity = model->get_capacity (base_reg);
label_text desc = capacity->get_desc (true);
warning_at (cd.get_call_stmt ()->location, 0,
"capacity: %qs", desc.get ());
}
};
/* Compare D1 and D2 using their names, and then IDs to order them. */
static int
cmp_decls (tree d1, tree d2)
{
gcc_assert (DECL_P (d1));
gcc_assert (DECL_P (d2));
if (DECL_NAME (d1) && DECL_NAME (d2))
if (int cmp = strcmp (IDENTIFIER_POINTER (DECL_NAME (d1)),
IDENTIFIER_POINTER (DECL_NAME (d2))))
return cmp;
return (int)DECL_UID (d1) - (int)DECL_UID (d2);
}
/* Comparator for use by vec<tree>::qsort,
using their names, and then IDs to order them. */
static int
cmp_decls_ptr_ptr (const void *p1, const void *p2)
{
tree const *d1 = (tree const *)p1;
tree const *d2 = (tree const *)p2;
return cmp_decls (*d1, *d2);
}
/* Handler for calls to "__analyzer_dump_escaped".
Emit a warning giving the number of decls that have escaped, followed
by a comma-separated list of their names, in alphabetical order.
This is for use when debugging, and may be of use in DejaGnu tests. */
class kf_analyzer_dump_escaped : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
region_model *model = cd.get_model ();
auto_vec<tree> escaped_decls;
for (auto iter : *model->get_store ())
{
const binding_cluster *c = iter.second;
if (!c->escaped_p ())
continue;
if (tree decl = c->get_base_region ()->maybe_get_decl ())
escaped_decls.safe_push (decl);
}
/* Sort them into deterministic order; alphabetical is
probably most user-friendly. */
escaped_decls.qsort (cmp_decls_ptr_ptr);
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_show_color (&pp) = pp_show_color (global_dc->printer);
bool first = true;
for (auto iter : escaped_decls)
{
if (first)
first = false;
else
pp_string (&pp, ", ");
pp_printf (&pp, "%qD", iter);
}
/* Print the number to make it easier to write DejaGnu tests for
the "nothing has escaped" case. */
warning_at (cd.get_location (), 0, "escaped: %i: %s",
escaped_decls.length (),
pp_formatted_text (&pp));
}
};
/* Placeholder handler for calls to "__analyzer_dump_exploded_nodes".
This is a no-op; the real implementation happens when the
exploded_graph is postprocessed. */
class kf_analyzer_dump_exploded_nodes : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 1;
}
};
/* Handler for calls to "__analyzer_dump_named_constant".
Look up the given name, and emit a warning describing the
state of the corresponding stashed value.
This is for use when debugging, and for DejaGnu tests. */
class kf_analyzer_dump_named_constant : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 1;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
const char *name = cd.get_arg_string_literal (0);
if (!name)
{
error_at (cd.get_location (), "cannot determine name");
return;
}
tree value = get_stashed_constant_by_name (name);
if (value)
warning_at (cd.get_location (), 0, "named constant %qs has value %qE",
name, value);
else
warning_at (cd.get_location (), 0, "named constant %qs has unknown value",
name);
}
};
/* A pending_diagnostic subclass for implementing "__analyzer_dump_path". */
class dump_path_diagnostic
: public pending_diagnostic_subclass<dump_path_diagnostic>
{
public:
int get_controlling_option () const final override
{
return 0;
}
bool emit (rich_location *richloc) final override
{
inform (richloc, "path");
return true;
}
const char *get_kind () const final override
{
return "dump_path_diagnostic";
}
bool operator== (const dump_path_diagnostic &) const
{
return true;
}
};
/* Handle calls to "__analyzer_dump_path" by queuing a diagnostic at this
exploded_node. */
class kf_analyzer_dump_path : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
ctxt->warn (make_unique<dump_path_diagnostic> ());
}
};
/* Handle calls to "__analyzer_dump_region_model" by dumping
the region model's state to stderr. */
class kf_analyzer_dump_region_model : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
region_model *model = cd.get_model ();
model->dump (false);
}
};
/* Handle a call to "__analyzer_eval" by evaluating the input
and dumping as a dummy warning, so that test cases can use
dg-warning to validate the result (and so unexpected warnings will
lead to DejaGnu failures).
Broken out as a subroutine to make it easier to put a breakpoint on it
- though typically this doesn't help, as we have an SSA name as the arg,
and what's more interesting is usually the def stmt for that name. */
class kf_analyzer_eval : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 1;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_context *ctxt = cd.get_ctxt ();
if (!ctxt)
return;
region_model *model = cd.get_model ();
tree t_arg = cd.get_arg_tree (0);
tristate t = model->eval_condition (t_arg, NE_EXPR, integer_zero_node,
ctxt);
warning_at (cd.get_location (), 0, "%s", t.as_string ());
}
};
/* Handler for "__analyzer_get_unknown_ptr". */
class kf_analyzer_get_unknown_ptr : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 0;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model_manager *mgr = cd.get_manager ();
const svalue *ptr_sval
= mgr->get_or_create_unknown_svalue (cd.get_lhs_type ());
cd.maybe_set_lhs (ptr_sval);
}
};
/* Handler for "__builtin_expect" etc. */
class kf_expect : public internal_known_function
@ -977,61 +451,6 @@ public:
}
};
/* Handler for "operator new" and "operator new []". */
class kf_operator_new : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == 1;
}
void impl_call_pre (const call_details &cd) const final override
{
region_model *model = cd.get_model ();
region_model_manager *mgr = cd.get_manager ();
const svalue *size_sval = cd.get_arg_svalue (0);
const region *new_reg
= model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
if (cd.get_lhs_type ())
{
const svalue *ptr_sval
= mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
cd.maybe_set_lhs (ptr_sval);
}
}
};
/* Handler for "operator delete", both the sized and unsized variants
(2 arguments and 1 argument respectively), and for "operator delete []" */
class kf_operator_delete : public known_function
{
public:
kf_operator_delete (unsigned num_args) : m_num_args (num_args) {}
bool matches_call_types_p (const call_details &cd) const final override
{
return cd.num_args () == m_num_args;
}
void impl_call_post (const call_details &cd) const final override
{
region_model *model = cd.get_model ();
const svalue *ptr_sval = cd.get_arg_svalue (0);
if (const region *freed_reg = ptr_sval->maybe_get_region ())
{
/* If the ptr points to an underlying heap region, delete it,
poisoning pointers. */
model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED);
}
}
private:
unsigned m_num_args;
};
/* Handler for "realloc":
void *realloc(void *ptr, size_t size);
@ -1490,6 +909,9 @@ region_model::impl_deallocation_call (const call_details &cd)
void
register_known_functions (known_function_manager &kfm)
{
/* Debugging/test support functions, all with a "__analyzer_" prefix. */
register_known_analyzer_functions (kfm);
/* Internal fns the analyzer has known_functions for. */
{
kfm.add (IFN_BUILTIN_EXPECT, make_unique<kf_expect> ());
@ -1520,27 +942,6 @@ register_known_functions (known_function_manager &kfm)
register_varargs_builtins (kfm);
}
/* Debugging/test support functions, all with a "__analyzer_" prefix. */
{
kfm.add ("__analyzer_break", make_unique<kf_analyzer_break> ());
kfm.add ("__analyzer_describe", make_unique<kf_analyzer_describe> ());
kfm.add ("__analyzer_dump_capacity",
make_unique<kf_analyzer_dump_capacity> ());
kfm.add ("__analyzer_dump_escaped",
make_unique<kf_analyzer_dump_escaped> ());
kfm.add ("__analyzer_dump_exploded_nodes",
make_unique<kf_analyzer_dump_exploded_nodes> ());
kfm.add ("__analyzer_dump_named_constant",
make_unique<kf_analyzer_dump_named_constant> ());
kfm.add ("__analyzer_dump_path", make_unique<kf_analyzer_dump_path> ());
kfm.add ("__analyzer_dump_region_model",
make_unique<kf_analyzer_dump_region_model> ());
kfm.add ("__analyzer_eval",
make_unique<kf_analyzer_eval> ());
kfm.add ("__analyzer_get_unknown_ptr",
make_unique<kf_analyzer_get_unknown_ptr> ());
}
/* Known builtins and C standard library functions. */
{
kfm.add ("memset", make_unique<kf_memset> ());
@ -1575,14 +976,8 @@ register_known_functions (known_function_manager &kfm)
kfm.add ("__error", make_unique<kf_errno_location> ());
}
/* C++ support functions. */
{
kfm.add ("operator new", make_unique<kf_operator_new> ());
kfm.add ("operator new []", make_unique<kf_operator_new> ());
kfm.add ("operator delete", make_unique<kf_operator_delete> (1));
kfm.add ("operator delete", make_unique<kf_operator_delete> (2));
kfm.add ("operator delete []", make_unique<kf_operator_delete> (1));
}
/* Language-specific support functions. */
register_known_functions_lang_cp (kfm);
}
} // namespace ana

@ -236,56 +236,6 @@ public:
struct append_regions_cb_data;
/* Helper class for handling calls to functions with known behavior.
Implemented in region-model-impl-calls.c. */
class call_details
{
public:
call_details (const gcall *call, region_model *model,
region_model_context *ctxt);
region_model *get_model () const { return m_model; }
region_model_manager *get_manager () const;
region_model_context *get_ctxt () const { return m_ctxt; }
logger *get_logger () const;
uncertainty_t *get_uncertainty () const;
tree get_lhs_type () const { return m_lhs_type; }
const region *get_lhs_region () const { return m_lhs_region; }
bool maybe_set_lhs (const svalue *result) const;
unsigned num_args () const;
bool arg_is_pointer_p (unsigned idx) const
{
return POINTER_TYPE_P (get_arg_type (idx));
}
bool arg_is_size_p (unsigned idx) const;
const gcall *get_call_stmt () const { return m_call; }
location_t get_location () const;
tree get_arg_tree (unsigned idx) const;
tree get_arg_type (unsigned idx) const;
const svalue *get_arg_svalue (unsigned idx) const;
const char *get_arg_string_literal (unsigned idx) const;
tree get_fndecl_for_call () const;
void dump_to_pp (pretty_printer *pp, bool simple) const;
void dump (bool simple) const;
const svalue *get_or_create_conjured_svalue (const region *) const;
private:
const gcall *m_call;
region_model *m_model;
region_model_context *m_ctxt;
tree m_lhs_type;
const region *m_lhs_region;
};
/* A region_model encapsulates a representation of the state of memory, with
a tree of regions, along with their associated values.
The representation is graph-like because values can be pointers to

@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/program-state.h"
#include "analyzer/supergraph.h"
#include "analyzer/analyzer-language.h"
#include "analyzer/call-details.h"
#include "analyzer/call-info.h"
#if ENABLE_ANALYZER

@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/program-point.h"
#include "analyzer/store.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#if ENABLE_ANALYZER

@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/program-point.h"
#include "analyzer/store.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#include "stringpool.h"
#include "attribs.h"
#include "analyzer/function-set.h"

@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/diagnostic-manager.h"
#include "analyzer/exploded-graph.h"
#include "diagnostic-metadata.h"
#include "analyzer/call-details.h"
#if ENABLE_ANALYZER

@ -41,6 +41,7 @@
#include "analyzer/program-point.h"
#include "analyzer/store.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#include "analyzer/call-info.h"
#include "make-unique.h"

@ -41,6 +41,7 @@
#include "analyzer/program-point.h"
#include "analyzer/store.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#include "analyzer/call-info.h"
#include "make-unique.h"