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:
parent
81476bc4f4
commit
861c917a97
@ -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. */
|
||||
|
231
gcc/analyzer/call-details.cc
Normal file
231
gcc/analyzer/call-details.cc
Normal file
@ -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 */
|
77
gcc/analyzer/call-details.h
Normal file
77
gcc/analyzer/call-details.h
Normal file
@ -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
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
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"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user