From patchwork Tue Dec 6 18:36:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 61601 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1B653383B6AB for ; Tue, 6 Dec 2022 18:37:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1B653383B6AB DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1670351835; bh=GNZCZn2GbNSvN7heRxOlpG9C3B8160PBt0gahM4/UA8=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=BQBnKP36zp2yB6J5P+1DmUKpVVtApjttFDU81AvKJcca44Kt/Ch3b1I1RNrDbJZBo zAlA8CHNnQodrCMNdLcqJQJsusOlictH0UssEaXt+O/ryHvPT4rrd7lg3sIILkCebv 9d9R8BDuO2nEAuZH5yvq60EYOx0ouI+BLPGo1bMs= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 79197383B697 for ; Tue, 6 Dec 2022 18:36:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 79197383B697 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-17-AuQ_4JIzOJeOUWBfmOMrWw-1; Tue, 06 Dec 2022 13:36:35 -0500 X-MC-Unique: AuQ_4JIzOJeOUWBfmOMrWw-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 9AA9887D36E for ; Tue, 6 Dec 2022 18:36:35 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.2.17.99]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5893C140EBF5; Tue, 6 Dec 2022 18:36:35 +0000 (UTC) To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [committed] analyzer: split out more stuff from region-model-impl-calls.cc Date: Tue, 6 Dec 2022 13:36:31 -0500 Message-Id: <20221206183631.4095755-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: David Malcolm via Gcc-patches From: David Malcolm Reply-To: David Malcolm Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r13-4517-g861c917a972dc9. 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 --- gcc/Makefile.in | 3 + gcc/analyzer/analyzer.h | 2 + gcc/analyzer/call-details.cc | 231 +++++++ gcc/analyzer/call-details.h | 77 +++ gcc/analyzer/call-info.cc | 1 + gcc/analyzer/call-summary.h | 2 + gcc/analyzer/kf-analyzer.cc | 386 +++++++++++ gcc/analyzer/kf-lang-cp.cc | 111 ++++ gcc/analyzer/known-function-manager.cc | 1 + gcc/analyzer/region-model-impl-calls.cc | 619 +----------------- gcc/analyzer/region-model.h | 50 -- gcc/analyzer/sm-fd.cc | 1 + gcc/analyzer/sm-file.cc | 1 + gcc/analyzer/sm-malloc.cc | 1 + gcc/analyzer/varargs.cc | 1 + .../gcc.dg/plugin/analyzer_kernel_plugin.c | 1 + .../gcc.dg/plugin/analyzer_known_fns_plugin.c | 1 + 17 files changed, 827 insertions(+), 662 deletions(-) create mode 100644 gcc/analyzer/call-details.cc create mode 100644 gcc/analyzer/call-details.h create mode 100644 gcc/analyzer/kf-analyzer.cc create mode 100644 gcc/analyzer/kf-lang-cp.cc diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 615a07089ee..7bcc5e501de 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -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 \ diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index a2d363f1249..418d4210755 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -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. */ diff --git a/gcc/analyzer/call-details.cc b/gcc/analyzer/call-details.cc new file mode 100644 index 00000000000..b63b8a38d88 --- /dev/null +++ b/gcc/analyzer/call-details.cc @@ -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 . + +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 +. */ + +#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 */ diff --git a/gcc/analyzer/call-details.h b/gcc/analyzer/call-details.h new file mode 100644 index 00000000000..144ca0ce868 --- /dev/null +++ b/gcc/analyzer/call-details.h @@ -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 . + +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 +. */ + +#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 */ diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc index 44a66be075a..cbd21f7472b 100644 --- a/gcc/analyzer/call-info.cc +++ b/gcc/analyzer/call-info.cc @@ -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" diff --git a/gcc/analyzer/call-summary.h b/gcc/analyzer/call-summary.h index 07cd3f53ce6..73f21ac7282 100644 --- a/gcc/analyzer/call-summary.h +++ b/gcc/analyzer/call-summary.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 diff --git a/gcc/analyzer/kf-analyzer.cc b/gcc/analyzer/kf-analyzer.cc new file mode 100644 index 00000000000..b233418beb7 --- /dev/null +++ b/gcc/analyzer/kf-analyzer.cc @@ -0,0 +1,386 @@ +/* Handling for the various __analyzer_* known functions. + Copyright (C) 2020-2022 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#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::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 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 +{ +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 ()); + } +}; + +/* 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 ()); + kfm.add ("__analyzer_describe", make_unique ()); + kfm.add ("__analyzer_dump_capacity", + make_unique ()); + kfm.add ("__analyzer_dump_escaped", make_unique ()); + kfm.add ("__analyzer_dump_exploded_nodes", + make_unique ()); + kfm.add ("__analyzer_dump_named_constant", + make_unique ()); + kfm.add ("__analyzer_dump_path", make_unique ()); + kfm.add ("__analyzer_dump_region_model", + make_unique ()); + kfm.add ("__analyzer_eval", make_unique ()); + kfm.add ("__analyzer_get_unknown_ptr", + make_unique ()); +} + +} // namespace ana + +#endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/kf-lang-cp.cc b/gcc/analyzer/kf-lang-cp.cc new file mode 100644 index 00000000000..9dca3669994 --- /dev/null +++ b/gcc/analyzer/kf-lang-cp.cc @@ -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 . + +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 +. */ + +#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 ()); + kfm.add ("operator new []", make_unique ()); + kfm.add ("operator delete", make_unique (1)); + kfm.add ("operator delete", make_unique (2)); + kfm.add ("operator delete []", make_unique (1)); +} + +} // namespace ana + +#endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/known-function-manager.cc b/gcc/analyzer/known-function-manager.cc index c1074bcb6e5..dc514a7fe57 100644 --- a/gcc/analyzer/known-function-manager.cc +++ b/gcc/analyzer/known-function-manager.cc @@ -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 diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 8ba644c33cd..6aeb9281bff 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -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::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 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 -{ -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 ()); - } -}; - -/* 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 ()); @@ -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 ()); - kfm.add ("__analyzer_describe", make_unique ()); - kfm.add ("__analyzer_dump_capacity", - make_unique ()); - kfm.add ("__analyzer_dump_escaped", - make_unique ()); - kfm.add ("__analyzer_dump_exploded_nodes", - make_unique ()); - kfm.add ("__analyzer_dump_named_constant", - make_unique ()); - kfm.add ("__analyzer_dump_path", make_unique ()); - kfm.add ("__analyzer_dump_region_model", - make_unique ()); - kfm.add ("__analyzer_eval", - make_unique ()); - kfm.add ("__analyzer_get_unknown_ptr", - make_unique ()); - } - /* Known builtins and C standard library functions. */ { kfm.add ("memset", make_unique ()); @@ -1575,14 +976,8 @@ register_known_functions (known_function_manager &kfm) kfm.add ("__error", make_unique ()); } - /* C++ support functions. */ - { - kfm.add ("operator new", make_unique ()); - kfm.add ("operator new []", make_unique ()); - kfm.add ("operator delete", make_unique (1)); - kfm.add ("operator delete", make_unique (2)); - kfm.add ("operator delete []", make_unique (1)); - } + /* Language-specific support functions. */ + register_known_functions_lang_cp (kfm); } } // namespace ana diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 86bfb4ff8a0..3b93d3e16b8 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -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 diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index 799847cb8e8..50e1313f85b 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -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 diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index d2dcb4312a2..1bd594b1263 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -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 diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 94ca295fae8..b520c9b5566 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -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" diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc index 519b32d693a..1a3bddee4b2 100644 --- a/gcc/analyzer/varargs.cc +++ b/gcc/analyzer/varargs.cc @@ -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 diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c index b424337aad1..57bccf4f2eb 100644 --- a/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c @@ -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" diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c index 1435b383674..de887dbad83 100644 --- a/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c @@ -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"