From patchwork Tue Nov 22 22:36:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 60991 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 56D2E3853D6E for ; Tue, 22 Nov 2022 22:37:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 56D2E3853D6E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1669156639; bh=+zPVhRPTdzVGcIuYrk+Dk0a9fRJrDlAXjnuWKFyScnM=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=gsCm0yArUJ29aDj6jCYyYgjTo8LzvdYUE54r9LyMs0hS9BSNeGy/VZF9byO1EmQGa uDS1Pz83YGLbla3cAshTqOtb5bxHh20i8W4p1Q81JU9D3tBmnjBojv84ZGAsJ0of6X mM0POr8114Cap9sCjyQvuVtV9oV4MRLc1gdtsvoY= 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.133.124]) by sourceware.org (Postfix) with ESMTPS id 7346B3858D35 for ; Tue, 22 Nov 2022 22:36:42 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 7346B3858D35 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-44-V0ugVXyUO3-rqEvGZqoUrQ-1; Tue, 22 Nov 2022 17:36:40 -0500 X-MC-Unique: V0ugVXyUO3-rqEvGZqoUrQ-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 9D716185A7A8 for ; Tue, 22 Nov 2022 22:36:40 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.2.16.65]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4E3D51415102; Tue, 22 Nov 2022 22:36:40 +0000 (UTC) To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [committed] analyzer: eliminate region_model::impl_call_* special cases Date: Tue, 22 Nov 2022 17:36:33 -0500 Message-Id: <20221122223633.3308746-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=-8.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, SPAM_BODY, 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" Eliminate all of the remaining special cases in class region_model that handle various specific functions, replacing them with uses of known_function subclasses. Add various type-checks that ought to prevent ICEs for cases where functions match the name of a standard C library or POSIX function, but have incompatible arguments. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r13-4246-g6bd31b33daa3c7. gcc/analyzer/ChangeLog: * analyzer.h (class internal_known_function): New. (register_varargs_builtins): New decl. * engine.cc (exploded_node::on_stmt_pre): Remove "out_terminate_path" param from call to region_model::on_stmt_pre. (feasibility_state::maybe_update_for_edge): Likewise. * known-function-manager.cc: Include "basic-block.h", "gimple.h", and "analyzer/region-model.h". (known_function_manager::known_function_manager): Initialize m_combined_fns_arr. (known_function_manager::~known_function_manager): Clean up m_combined_fns_arr. (known_function_manager::get_by_identifier): Make const. (known_function_manager::add): New overloaded definitions for enum built_in_function and enum internal_fn. (known_function_manager::get_by_fndecl): Delete. (known_function_manager::get_match): New. (known_function_manager::get_internal_fn): New. (known_function_manager::get_normal_builtin): New. * known-function-manager.h (known_function_manager::get_by_identifier): Make private and add const qualifier. (known_function_manager::get_by_fndecl): Delete. (known_function_manager::add): Add overloaded decls for enum built_in_function name and enum internal_fn. (known_function_manager::get_match): New decl. (known_function_manager::get_internal_fn): New decl. (known_function_manager::get_normal_builtin): New decl. (known_function_manager::m_combined_fns_arr): New field. * region-model-impl-calls.cc (call_details::arg_is_size_p): New. (class kf_alloca): New. (region_model::impl_call_alloca): Convert to... (kf_alloca::impl_call_pre): ...this. (kf_analyzer_dump_capacity::matches_call_types_p): Rewrite check to use call_details::arg_is_pointer_p. (region_model::impl_call_builtin_expect): Convert to... (class kf_expect): ...this. (class kf_calloc): New, adding check that both arguments are size_t. (region_model::impl_call_calloc): Convert to... (kf_calloc::impl_call_pre): ...this. (kf_connect::matches_call_types_p): Rewrite check to use call_details::arg_is_pointer_p. (region_model::impl_call_error): Convert to... (class kf_error): ...this, and... (kf_error::impl_call_pre): ...this. (class kf_fgets): New, adding checks that args 0 and 2 are pointers. (region_model::impl_call_fgets): Convert to... (kf_fgets::impl_call_pre): ...this. (class kf_fread): New, adding checks on the argument types. (region_model::impl_call_fread): Convert to... (kf_fread::impl_call_pre): ...this. (class kf_free): New, adding check that the argument is a pointer. (region_model::impl_call_free): Convert to... (kf_free::impl_call_post): ...this. (class kf_getchar): New. (class kf_malloc): New, adding check that the argument is a size_t. (region_model::impl_call_malloc): Convert to... (kf_malloc::impl_call_pre): ...this. (class kf_memcpy): New, adding checks on arguments. (region_model::impl_call_memcpy): Convert to... (kf_memcpy::impl_call_pre): ...this. (class kf_memset): New. (region_model::impl_call_memset): Convert to... (kf_memset::impl_call_pre): ...this. (kf_pipe::matches_call_types_p): Rewrite check to use call_details::arg_is_pointer_p. (kf_putenv::matches_call_types_p): Likewise. (class kf_realloc): New, adding checks on the argument types. (region_model::impl_call_realloc): Convert to... (kf_realloc::impl_call_post): ...this. (class kf_strchr): New. (region_model::impl_call_strchr): Convert to... (kf_strchr::impl_call_post): ...this. (class kf_stack_restore): New. (class kf_stack_save): New. (class kf_stdio_output_fn): New. (class kf_strcpy): New, (region_model::impl_call_strcpy): Convert to... (kf_strcpy::impl_call_pre): ...this. (class kf_strlen): New. (region_model::impl_call_strlen): Convert to... (kf_strlen::impl_call_pre): ...this. (class kf_ubsan_bounds): New. (region_model::impl_deallocation_call): Reimplement to avoid call to impl_call_free. (register_known_functions): Add handlers for IFN_BUILTIN_EXPECT and IFN_UBSAN_BOUNDS. Add handlers for BUILT_IN_ALLOCA, BUILT_IN_ALLOCA_WITH_ALIGN, BUILT_IN_CALLOC, BUILT_IN_EXPECT, BUILT_IN_EXPECT_WITH_PROBABILITY, BUILT_IN_FPRINTF, BUILT_IN_FPRINTF_UNLOCKED, BUILT_IN_FPUTC, BUILT_IN_FPUTC_UNLOCKED, BUILT_IN_FPUTS, BUILT_IN_FPUTS_UNLOCKED, BUILT_IN_FREE, BUILT_IN_FWRITE, BUILT_IN_FWRITE_UNLOCKED, BUILT_IN_MALLOC, BUILT_IN_MEMCPY, BUILT_IN_MEMCPY_CHK, BUILT_IN_MEMSET, BUILT_IN_MEMSET_CHK, BUILT_IN_PRINTF, BUILT_IN_PRINTF_UNLOCKED, BUILT_IN_PUTC, BUILT_IN_PUTCHAR, BUILT_IN_PUTCHAR_UNLOCKED, BUILT_IN_PUTC_UNLOCKED, BUILT_IN_PUTS, BUILT_IN_PUTS_UNLOCKED, BUILT_IN_REALLOC, BUILT_IN_STACK_RESTORE, BUILT_IN_STACK_SAVE, BUILT_IN_STRCHR, BUILT_IN_STRCPY, BUILT_IN_STRCPY_CHK, BUILT_IN_STRLEN, BUILT_IN_VFPRINTF, and BUILT_IN_VPRINTF. Call register_varargs_builtins. Add handlers for "getchar", "memset", "fgets", "fgets_unlocked", "fread", "error", and "error_at_line". * region-model.cc (region_model::on_stmt_pre): Drop "out_terminate_path" param. (region_model::get_known_function): Reimplement by calling known_function_manager::get_match, passing new "cd" param. Add overload taking enum internal_fn. (region_model::on_call_pre): Drop "out_terminate_path" param. Remove special-case handling of internal fns IFN_BUILTIN_EXPECT, IFN_UBSAN_BOUNDS, and IFN_VA_ARG, of built-in fns BUILT_IN_ALLOCA, BUILT_IN_ALLOCA_WITH_ALIGN, BUILT_IN_CALLOC, BUILT_IN_EXPECT, BUILT_IN_EXPECT_WITH_PROBABILITY, BUILT_IN_FREE, BUILT_IN_MALLOC, BUILT_IN_MEMCPY, BUILT_IN_MEMCPY_CHK, BUILT_IN_MEMSET, BUILT_IN_MEMSET_CHK, BUILT_IN_REALLOC, BUILT_IN_STRCHR, BUILT_IN_STRCPY, BUILT_IN_STRCPY_CHK, BUILT_IN_STRLEN, BUILT_IN_STACK_SAVE, BUILT_IN_STACK_RESTORE, BUILT_IN_FPRINTF, BUILT_IN_FPRINTF_UNLOCKED, BUILT_IN_PUTC, BUILT_IN_PUTC_UNLOCKED, BUILT_IN_FPUTC, BUILT_IN_FPUTC_UNLOCKED, BUILT_IN_FPUTS, BUILT_IN_FPUTS_UNLOCKED, BUILT_IN_FWRITE, BUILT_IN_FWRITE_UNLOCKED, BUILT_IN_PRINTF, BUILT_IN_PRINTF_UNLOCKED, BUILT_IN_PUTCHAR, BUILT_IN_PUTCHAR_UNLOCKED, BUILT_IN_PUTS, BUILT_IN_PUTS_UNLOCKED, BUILT_IN_VFPRINTF, BUILT_IN_VPRINTF, BUILT_IN_VA_START, and BUILT_IN_VA_COPY, and of named functions "malloc", "calloc", "alloca", "realloc", "error", "error_at_line", "fgets", "fgets_unlocked", "fread", "getchar", "memset", "strchr", and "strlen". Replace all this special-casing with calls to get_known_function for internal fns and for fn decls. (region_model::on_call_post): Remove special-casing handling for "free" and "strchr", and for BUILT_IN_REALLOC, BUILT_IN_STRCHR, and BUILT_IN_VA_END. Replace by consolidating on usage of get_known_function. * region-model.h (call_details::arg_is_size_p): New. (region_model::on_stmt_pre): Drop "out_terminate_path" param. (region_model::on_call_pre): Likewise. (region_model::impl_call_alloca): Delete. (region_model::impl_call_builtin_expect): Delete. (region_model::impl_call_calloc): Delete. (region_model::impl_call_error): Delete. (region_model::impl_call_fgets): Delete. (region_model::impl_call_fread): Delete. (region_model::impl_call_free): Delete. (region_model::impl_call_malloc): Delete. (region_model::impl_call_memcpy): Delete. (region_model::impl_call_memset): Delete. (region_model::impl_call_realloc): Delete. (region_model::impl_call_strchr): Delete. (region_model::impl_call_strcpy): Delete. (region_model::impl_call_strlen): Delete. (region_model::impl_call_va_start): Delete. (region_model::impl_call_va_copy): Delete. (region_model::impl_call_va_arg): Delete. (region_model::impl_call_va_end): Delete. (region_model::check_region_for_write): Public. (region_model::get_known_function): Add "cd" param. Add overloaded decl taking enum internal_fn. * sm-malloc.cc: Update comments. * varargs.cc (class kf_va_start): New. (region_model::impl_call_va_start): Convert to... (kf_va_start::impl_call_pre): ...this. (class kf_va_copy): New. (region_model::impl_call_va_copy): Convert to... (kf_va_copy::impl_call_pre): ...this. (class kf_va_arg): New. (region_model::impl_call_va_arg): Convert to... (kf_va_arg::impl_call_pre): ...this. (class kf_va_end): New. (region_model::impl_call_va_end): Delete. (register_varargs_builtins): New. Signed-off-by: David Malcolm --- gcc/analyzer/analyzer.h | 13 + gcc/analyzer/engine.cc | 5 +- gcc/analyzer/known-function-manager.cc | 85 +++- gcc/analyzer/known-function-manager.h | 14 +- gcc/analyzer/region-model-impl-calls.cc | 523 +++++++++++++++++++----- gcc/analyzer/region-model.cc | 252 ++---------- gcc/analyzer/region-model.h | 36 +- gcc/analyzer/sm-malloc.cc | 4 +- gcc/analyzer/varargs.cc | 131 ++++-- 9 files changed, 648 insertions(+), 415 deletions(-) diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 83bb0011a3f..d424b43f2de 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -245,7 +245,20 @@ public: } }; +/* Subclass of known_function for IFN_* functions. */ + +class internal_known_function : public known_function +{ +public: + bool matches_call_types_p (const call_details &) const final override + { + /* Types are assumed to be correct. */ + return true; + } +}; + extern void register_known_functions (known_function_manager &mgr); +extern void register_varargs_builtins (known_function_manager &kfm); /* Passed by pointer to PLUGIN_ANALYZER_INIT callbacks. */ diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index db1881cd140..0c49bb26872 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -1538,7 +1538,6 @@ exploded_node::on_stmt_pre (exploded_graph &eg, /* Otherwise, defer to m_region_model. */ state->m_region_model->on_stmt_pre (stmt, - out_terminate_path, out_unknown_side_effects, ctxt); } @@ -4839,9 +4838,7 @@ feasibility_state::maybe_update_for_edge (logger *logger, m_model.on_asm_stmt (asm_stmt, NULL); else if (const gcall *call = dyn_cast (stmt)) { - bool terminate_path; - bool unknown_side_effects - = m_model.on_call_pre (call, NULL, &terminate_path); + bool unknown_side_effects = m_model.on_call_pre (call, NULL); m_model.on_call_post (call, unknown_side_effects, NULL); } else if (const greturn *return_ = dyn_cast (stmt)) diff --git a/gcc/analyzer/known-function-manager.cc b/gcc/analyzer/known-function-manager.cc index 7341b068480..e17350da5ec 100644 --- a/gcc/analyzer/known-function-manager.cc +++ b/gcc/analyzer/known-function-manager.cc @@ -27,7 +27,10 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "analyzer/analyzer-logging.h" #include "stringpool.h" +#include "basic-block.h" +#include "gimple.h" #include "analyzer/known-function-manager.h" +#include "analyzer/region-model.h" #if ENABLE_ANALYZER @@ -38,6 +41,7 @@ namespace ana { known_function_manager::known_function_manager (logger *logger) : log_user (logger) { + memset (m_combined_fns_arr, 0, sizeof (m_combined_fns_arr)); } known_function_manager::~known_function_manager () @@ -45,6 +49,8 @@ known_function_manager::~known_function_manager () /* Delete all owned kfs. */ for (auto iter : m_map_id_to_kf) delete iter.second; + for (auto iter : m_combined_fns_arr) + delete iter; } void @@ -56,24 +62,85 @@ known_function_manager::add (const char *name, m_map_id_to_kf.put (id, kf.release ()); } -const known_function * -known_function_manager::get_by_identifier (tree identifier) +void +known_function_manager::add (enum built_in_function name, + std::unique_ptr kf) { - known_function **slot = m_map_id_to_kf.get (identifier); - if (slot) - return *slot; - else - return NULL; + gcc_assert (name < END_BUILTINS); + delete m_combined_fns_arr[name]; + m_combined_fns_arr[name] = kf.release (); } +void +known_function_manager::add (enum internal_fn ifn, + std::unique_ptr kf) +{ + gcc_assert (ifn < IFN_LAST); + delete m_combined_fns_arr[ifn + END_BUILTINS]; + m_combined_fns_arr[ifn + END_BUILTINS] = kf.release (); +} + +/* Get any known_function for FNDECL for call CD. + + The call must match all assumptions made by the known_function (such as + e.g. "argument 1's type must be a pointer type"). + + Return NULL if no known_function is found, or it does not match the + assumption(s). */ + const known_function * -known_function_manager::get_by_fndecl (tree fndecl) +known_function_manager::get_match (tree fndecl, const call_details &cd) const { + if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) + { + if (const known_function *candidate + = get_normal_builtin (DECL_FUNCTION_CODE (fndecl))) + if (gimple_builtin_call_types_compatible_p (cd.get_call_stmt (), + fndecl)) + return candidate; + } if (tree identifier = DECL_NAME (fndecl)) - return get_by_identifier (identifier); + if (const known_function *candidate = get_by_identifier (identifier)) + if (candidate->matches_call_types_p (cd)) + return candidate; return NULL; } +/* Get any known_function for IFN, or NULL. */ + +const known_function * +known_function_manager::get_internal_fn (enum internal_fn ifn) const +{ + gcc_assert (ifn < IFN_LAST); + return m_combined_fns_arr[ifn + END_BUILTINS]; +} + +/* Get any known_function for NAME, without type-checking. + Return NULL if there isn't one. */ + +const known_function * +known_function_manager::get_normal_builtin (enum built_in_function name) const +{ + /* The numbers for built-in functions in enum combined_fn are the same as + for the built_in_function enum. */ + gcc_assert (name < END_BUILTINS); + return m_combined_fns_arr[name]; +} + +/* Get any known_function matching IDENTIFIER, without type-checking. + Return NULL if there isn't one. */ + +const known_function * +known_function_manager::get_by_identifier (tree identifier) const +{ + known_function_manager *mut_this = const_cast(this); + known_function **slot = mut_this->m_map_id_to_kf.get (identifier); + if (slot) + return *slot; + else + return NULL; +} + } // namespace ana #endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/known-function-manager.h b/gcc/analyzer/known-function-manager.h index daf1bc57855..188cb8e034a 100644 --- a/gcc/analyzer/known-function-manager.h +++ b/gcc/analyzer/known-function-manager.h @@ -30,16 +30,26 @@ class known_function_manager : public log_user public: known_function_manager (logger *logger); ~known_function_manager (); + void add (const char *name, std::unique_ptr kf); - const known_function *get_by_identifier (tree identifier); - const known_function *get_by_fndecl (tree fndecl); + void add (enum built_in_function name, std::unique_ptr kf); + void add (enum internal_fn ifn, std::unique_ptr kf); + + const known_function *get_match (tree fndecl, const call_details &cd) const; + const known_function *get_internal_fn (enum internal_fn) const; private: DISABLE_COPY_AND_ASSIGN (known_function_manager); + const known_function *get_normal_builtin (enum built_in_function name) const; + const known_function *get_by_identifier (tree identifier) const; + /* Map from identifier to known_function instance. Has ownership of the latter. */ hash_map m_map_id_to_kf; + + /* Array of known builtins. */ + known_function *m_combined_fns_arr[CFN_LAST]; }; } // namespace ana diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 8a44c97eec9..6962ffd400f 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -133,6 +133,14 @@ 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 @@ -242,15 +250,30 @@ call_details::get_or_create_conjured_svalue (const region *reg) const /* Implementations of specific functions. */ -/* Handle the on_call_pre part of "alloca". */ +/* Handler for "alloca". */ + +class kf_alloca : 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; +}; void -region_model::impl_call_alloca (const call_details &cd) +kf_alloca::impl_call_pre (const call_details &cd) const { const svalue *size_sval = cd.get_arg_svalue (0); - const region *new_reg = create_region_for_alloca (size_sval, cd.get_ctxt ()); + + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); + + const region *new_reg + = model->create_region_for_alloca (size_sval, cd.get_ctxt ()); const svalue *ptr_sval - = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); + = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); cd.maybe_set_lhs (ptr_sval); } @@ -308,7 +331,7 @@ public: bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == 1 - && POINTER_TYPE_P (cd.get_arg_type (0))); + && cd.arg_is_pointer_p (0)); } void impl_call_pre (const call_details &cd) const final override @@ -649,36 +672,53 @@ public: } }; -/* Handle the on_call_pre part of "__builtin_expect" etc. */ +/* Handler for "__builtin_expect" etc. */ -void -region_model::impl_call_builtin_expect (const call_details &cd) +class kf_expect : public internal_known_function { - /* __builtin_expect's return value is its initial argument. */ - const svalue *sval = cd.get_arg_svalue (0); - cd.maybe_set_lhs (sval); -} +public: + void impl_call_pre (const call_details &cd) const final override + { + /* __builtin_expect's return value is its initial argument. */ + const svalue *sval = cd.get_arg_svalue (0); + cd.maybe_set_lhs (sval); + } +}; -/* Handle the on_call_pre part of "calloc". */ +/* Handler for "calloc". */ + +class kf_calloc : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 2 + && cd.arg_is_size_p (0) + && cd.arg_is_size_p (1)); + } + void impl_call_pre (const call_details &cd) const final override; +}; void -region_model::impl_call_calloc (const call_details &cd) +kf_calloc::impl_call_pre (const call_details &cd) const { + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); const svalue *nmemb_sval = cd.get_arg_svalue (0); const svalue *size_sval = cd.get_arg_svalue (1); /* TODO: check for overflow here? */ const svalue *prod_sval - = m_mgr->get_or_create_binop (size_type_node, MULT_EXPR, - nmemb_sval, size_sval); + = mgr->get_or_create_binop (size_type_node, MULT_EXPR, + nmemb_sval, size_sval); const region *new_reg - = create_region_for_heap_alloc (prod_sval, cd.get_ctxt ()); + = model->create_region_for_heap_alloc (prod_sval, cd.get_ctxt ()); const region *sized_reg - = m_mgr->get_sized_region (new_reg, NULL_TREE, prod_sval); - zero_fill_region (sized_reg); + = mgr->get_sized_region (new_reg, NULL_TREE, prod_sval); + model->zero_fill_region (sized_reg); if (cd.get_lhs_type ()) { const svalue *ptr_sval - = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); + = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); cd.maybe_set_lhs (ptr_sval); } } @@ -708,7 +748,7 @@ public: bool matches_call_types_p (const call_details &cd) const final override { return (cd.num_args () == 3 - && POINTER_TYPE_P (cd.get_arg_type (1))); + && cd.arg_is_pointer_p (1)); } void impl_call_post (const call_details &cd) const final override @@ -745,67 +785,102 @@ public: } }; -/* Handle the on_call_pre part of "error" and "error_at_line" from - GNU's non-standard . +/* Handler for "error" and "error_at_line" from GNU's non-standard . MIN_ARGS identifies the minimum number of expected arguments - to be consistent with such a call (3 and 5 respectively). - Return true if handling it as one of these functions. - Write true to *OUT_TERMINATE_PATH if this execution path should be - terminated (e.g. the function call terminates the process). */ + to be consistent with such a call (3 and 5 respectively). */ -bool -region_model::impl_call_error (const call_details &cd, unsigned min_args, - bool *out_terminate_path) +class kf_error : public known_function { - /* Bail if not enough args. */ - if (cd.num_args () < min_args) - return false; +public: + kf_error (unsigned min_args) : m_min_args (min_args) {} - /* Initial argument ought to be of type "int". */ - if (cd.get_arg_type (0) != integer_type_node) - return false; + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () >= m_min_args + && cd.get_arg_type (0) == integer_type_node); + } + + void impl_call_pre (const call_details &cd) const final override; + +private: + unsigned m_min_args; +}; +void +kf_error::impl_call_pre (const call_details &cd) const +{ /* The process exits if status != 0, so it only continues for the case where status == 0. Add that constraint, or terminate this analysis path. */ tree status = cd.get_arg_tree (0); - if (!add_constraint (status, EQ_EXPR, integer_zero_node, cd.get_ctxt ())) - *out_terminate_path = true; - - return true; + region_model_context *ctxt = cd.get_ctxt (); + region_model *model = cd.get_model (); + if (!model->add_constraint (status, EQ_EXPR, integer_zero_node, ctxt)) + if (ctxt) + ctxt->terminate_path (); } -/* Handle the on_call_pre part of "fgets" and "fgets_unlocked". */ +/* Handler for "fgets" and "fgets_unlocked". */ + +class kf_fgets : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 3 + && cd.arg_is_pointer_p (0) + && cd.arg_is_pointer_p (2)); + } + + void impl_call_pre (const call_details &cd) const final override; +}; void -region_model::impl_call_fgets (const call_details &cd) +kf_fgets::impl_call_pre (const call_details &cd) const { /* Ideally we would bifurcate state here between the error vs no error cases. */ + region_model *model = cd.get_model (); const svalue *ptr_sval = cd.get_arg_svalue (0); if (const region *reg = ptr_sval->maybe_get_region ()) { const region *base_reg = reg->get_base_region (); const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg); - set_value (base_reg, new_sval, cd.get_ctxt ()); + model->set_value (base_reg, new_sval, cd.get_ctxt ()); } } -/* Handle the on_call_pre part of "fread". */ +/* Handler for "fread"". */ + +class kf_fread : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 4 + && cd.arg_is_pointer_p (0) + && cd.arg_is_size_p (1) + && cd.arg_is_size_p (2) + && cd.arg_is_pointer_p (3)); + } + + void impl_call_pre (const call_details &cd) const final override; +}; void -region_model::impl_call_fread (const call_details &cd) +kf_fread::impl_call_pre (const call_details &cd) const { + region_model *model = cd.get_model (); const svalue *ptr_sval = cd.get_arg_svalue (0); if (const region *reg = ptr_sval->maybe_get_region ()) { const region *base_reg = reg->get_base_region (); const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg); - set_value (base_reg, new_sval, cd.get_ctxt ()); + model->set_value (base_reg, new_sval, cd.get_ctxt ()); } } -/* Handle the on_call_post part of "free", after sm-handling. +/* Handler for "free", after sm-handling. If the ptr points to an underlying heap region, delete the region, poisoning pointers to it and regions within it. @@ -820,19 +895,44 @@ region_model::impl_call_fread (const call_details &cd) all pointers to the region to the "freed" state together, regardless of casts. */ +class kf_free : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 0 && cd.arg_is_pointer_p (0)); + } + void impl_call_post (const call_details &cd) const final override; +}; + void -region_model::impl_call_free (const call_details &cd) +kf_free::impl_call_post (const call_details &cd) const { 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. */ - unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); - m_dynamic_extents.remove (freed_reg); + region_model *model = cd.get_model (); + model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); + model->unset_dynamic_extents (freed_reg); } } +/* Handler for "getchar"". */ + +class kf_getchar : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return cd.num_args () == 0; + } + + /* Empty. No side-effects (tracking stream state is out-of-scope + for the analyzer). */ +}; + /* Handle calls to "listen". See e.g. https://man7.org/linux/man-pages/man3/listen.3p.html */ @@ -872,66 +972,109 @@ class kf_listen : public known_function /* Handle the on_call_pre part of "malloc". */ +class kf_malloc : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 1 + && cd.arg_is_size_p (0)); + } + void impl_call_pre (const call_details &cd) const final override; +}; + void -region_model::impl_call_malloc (const call_details &cd) +kf_malloc::impl_call_pre (const call_details &cd) const { + 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 - = create_region_for_heap_alloc (size_sval, cd.get_ctxt ()); + = model->create_region_for_heap_alloc (size_sval, cd.get_ctxt ()); if (cd.get_lhs_type ()) { const svalue *ptr_sval - = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); + = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); cd.maybe_set_lhs (ptr_sval); } } -/* Handle the on_call_pre part of "memcpy" and "__builtin_memcpy". */ +/* Handler for "memcpy" and "__builtin_memcpy". */ // TODO: complain about overlapping src and dest. +class kf_memcpy : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 3 + && cd.arg_is_pointer_p (0) + && cd.arg_is_pointer_p (1) + && cd.arg_is_size_p (2)); + } + void impl_call_pre (const call_details &cd) const final override; +}; + void -region_model::impl_call_memcpy (const call_details &cd) +kf_memcpy::impl_call_pre (const call_details &cd) const { const svalue *dest_ptr_sval = cd.get_arg_svalue (0); const svalue *src_ptr_sval = cd.get_arg_svalue (1); const svalue *num_bytes_sval = cd.get_arg_svalue (2); - const region *dest_reg = deref_rvalue (dest_ptr_sval, cd.get_arg_tree (0), - cd.get_ctxt ()); - const region *src_reg = deref_rvalue (src_ptr_sval, cd.get_arg_tree (1), - cd.get_ctxt ()); + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); + + const region *dest_reg + = model->deref_rvalue (dest_ptr_sval, cd.get_arg_tree (0), cd.get_ctxt ()); + const region *src_reg + = model->deref_rvalue (src_ptr_sval, cd.get_arg_tree (1), cd.get_ctxt ()); cd.maybe_set_lhs (dest_ptr_sval); const region *sized_src_reg - = m_mgr->get_sized_region (src_reg, NULL_TREE, num_bytes_sval); + = mgr->get_sized_region (src_reg, NULL_TREE, num_bytes_sval); const region *sized_dest_reg - = m_mgr->get_sized_region (dest_reg, NULL_TREE, num_bytes_sval); + = mgr->get_sized_region (dest_reg, NULL_TREE, num_bytes_sval); const svalue *src_contents_sval - = get_store_value (sized_src_reg, cd.get_ctxt ()); - set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ()); + = model->get_store_value (sized_src_reg, cd.get_ctxt ()); + model->set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ()); } -/* Handle the on_call_pre part of "memset" and "__builtin_memset". */ +/* Handler for "memset" and "__builtin_memset". */ + +class kf_memset : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 3 && cd.arg_is_pointer_p (0)); + } + + void impl_call_pre (const call_details &cd) const final override; +}; void -region_model::impl_call_memset (const call_details &cd) +kf_memset::impl_call_pre (const call_details &cd) const { const svalue *dest_sval = cd.get_arg_svalue (0); const svalue *fill_value_sval = cd.get_arg_svalue (1); const svalue *num_bytes_sval = cd.get_arg_svalue (2); - const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0), - cd.get_ctxt ()); + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); + + const region *dest_reg + = model->deref_rvalue (dest_sval, cd.get_arg_tree (0), cd.get_ctxt ()); const svalue *fill_value_u8 - = m_mgr->get_or_create_cast (unsigned_char_type_node, fill_value_sval); + = mgr->get_or_create_cast (unsigned_char_type_node, fill_value_sval); - const region *sized_dest_reg = m_mgr->get_sized_region (dest_reg, - NULL_TREE, - num_bytes_sval); - check_region_for_write (sized_dest_reg, cd.get_ctxt ()); - fill_region (sized_dest_reg, fill_value_u8); + const region *sized_dest_reg = mgr->get_sized_region (dest_reg, + NULL_TREE, + num_bytes_sval); + model->check_region_for_write (sized_dest_reg, cd.get_ctxt ()); + model->fill_region (sized_dest_reg, fill_value_u8); } /* Handler for calls to "pipe" and "pipe2". @@ -989,7 +1132,6 @@ class kf_pipe : public known_function p); model->set_value (element_reg, fd_sval, cd.get_ctxt ()); model->mark_as_valid_fd (fd_sval, cd.get_ctxt ()); - } return true; } @@ -1004,8 +1146,7 @@ public: bool matches_call_types_p (const call_details &cd) const final override { - return (cd.num_args () == m_num_args - && POINTER_TYPE_P (cd.get_arg_type (0))); + return (cd.num_args () == m_num_args && cd.arg_is_pointer_p (0)); } void impl_call_post (const call_details &cd) const final override @@ -1117,8 +1258,7 @@ class kf_putenv : public known_function public: bool matches_call_types_p (const call_details &cd) const final override { - return (cd.num_args () == 1 - && POINTER_TYPE_P (cd.get_arg_type (0))); + return (cd.num_args () == 1 && cd.arg_is_pointer_p (0)); } void impl_call_pre (const call_details &cd) const final override @@ -1205,7 +1345,7 @@ private: unsigned m_num_args; }; -/* Handle the on_call_post part of "realloc": +/* Handler for "realloc": void *realloc(void *ptr, size_t size); @@ -1228,8 +1368,20 @@ private: Each of these has a custom_edge_info subclass, which updates the region_model and sm-state of the destination state. */ +class kf_realloc : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 2 + && cd.arg_is_pointer_p (0) + && cd.arg_is_size_p (1)); + } + void impl_call_post (const call_details &cd) const final override; +}; + void -region_model::impl_call_realloc (const call_details &cd) +kf_realloc::impl_call_post (const call_details &cd) const { /* Three custom subclasses of custom_edge_info, for handling the various outcomes of "realloc". */ @@ -1249,10 +1401,11 @@ region_model::impl_call_realloc (const call_details &cd) { /* Return NULL; everything else is unchanged. */ const call_details cd (get_call_details (model, ctxt)); + region_model_manager *mgr = cd.get_manager (); if (cd.get_lhs_type ()) { const svalue *zero - = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); + = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); model->set_value (cd.get_lhs_region (), zero, cd.get_ctxt ()); @@ -1284,13 +1437,14 @@ region_model::impl_call_realloc (const call_details &cd) { /* Update size of buffer and return the ptr unchanged. */ const call_details cd (get_call_details (model, ctxt)); + region_model_manager *mgr = cd.get_manager (); const svalue *ptr_sval = cd.get_arg_svalue (0); const svalue *size_sval = cd.get_arg_svalue (1); /* We can only grow in place with a non-NULL pointer. */ { const svalue *null_ptr - = model->m_mgr->get_or_create_int_cst (ptr_sval->get_type (), 0); + = mgr->get_or_create_int_cst (ptr_sval->get_type (), 0); if (!model->add_constraint (ptr_sval, NE_EXPR, null_ptr, cd.get_ctxt ())) return false; @@ -1304,8 +1458,8 @@ region_model::impl_call_realloc (const call_details &cd) { model->set_value (cd.get_lhs_region (), ptr_sval, cd.get_ctxt ()); const svalue *zero - = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); - return model->add_constraint (ptr_sval, NE_EXPR, zero, cd.get_ctxt ()); + = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); + return model->add_constraint (ptr_sval, NE_EXPR, zero, ctxt); } else return true; @@ -1334,6 +1488,7 @@ region_model::impl_call_realloc (const call_details &cd) region_model_context *ctxt) const final override { const call_details cd (get_call_details (model, ctxt)); + region_model_manager *mgr = cd.get_manager (); const svalue *old_ptr_sval = cd.get_arg_svalue (0); const svalue *new_size_sval = cd.get_arg_svalue (1); @@ -1341,7 +1496,7 @@ region_model::impl_call_realloc (const call_details &cd) const region *new_reg = model->create_region_for_heap_alloc (new_size_sval, ctxt); const svalue *new_ptr_sval - = model->m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); + = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); if (!model->add_constraint (new_ptr_sval, NE_EXPR, old_ptr_sval, cd.get_ctxt ())) return false; @@ -1359,13 +1514,11 @@ region_model::impl_call_realloc (const call_details &cd) const svalue *copied_size_sval = get_copied_size (model, old_size_sval, new_size_sval); const region *copied_old_reg - = model->m_mgr->get_sized_region (freed_reg, NULL, - copied_size_sval); + = mgr->get_sized_region (freed_reg, NULL, copied_size_sval); const svalue *buffer_content_sval = model->get_store_value (copied_old_reg, cd.get_ctxt ()); const region *copied_new_reg - = model->m_mgr->get_sized_region (new_reg, NULL, - copied_size_sval); + = mgr->get_sized_region (new_reg, NULL, copied_size_sval); model->set_value (copied_new_reg, buffer_content_sval, cd.get_ctxt ()); } @@ -1383,7 +1536,7 @@ region_model::impl_call_realloc (const call_details &cd) /* If the ptr points to an underlying heap region, delete it, poisoning pointers. */ model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED); - model->m_dynamic_extents.remove (freed_reg); + model->unset_dynamic_extents (freed_reg); } /* Update the sm-state: mark the old_ptr_sval as "freed", @@ -1393,7 +1546,7 @@ region_model::impl_call_realloc (const call_details &cd) if (cd.get_lhs_type ()) { const svalue *zero - = model->m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); + = mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); return model->add_constraint (new_ptr_sval, NE_EXPR, zero, cd.get_ctxt ()); } @@ -1423,7 +1576,7 @@ region_model::impl_call_realloc (const call_details &cd) } }; - /* Body of region_model::impl_call_realloc. */ + /* Body of kf_realloc::impl_call_post. */ if (cd.get_ctxt ()) { @@ -1472,10 +1625,20 @@ public: } }; -/* Handle the on_call_post part of "strchr" and "__builtin_strchr". */ +/* Handler for "strchr" and "__builtin_strchr". */ + +class kf_strchr : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 2 && cd.arg_is_pointer_p (0)); + } + void impl_call_post (const call_details &cd) const final override; +}; void -region_model::impl_call_strchr (const call_details &cd) +kf_strchr::impl_call_post (const call_details &cd) const { class strchr_call_info : public call_info { @@ -1534,7 +1697,7 @@ region_model::impl_call_strchr (const call_details &cd) bool m_found; }; - /* Body of region_model::impl_call_strchr. */ + /* Body of kf_strchr::impl_call_post. */ if (cd.get_ctxt ()) { cd.get_ctxt ()->bifurcate (make_unique (cd, false)); @@ -1543,41 +1706,116 @@ region_model::impl_call_strchr (const call_details &cd) } } -/* Handle the on_call_pre part of "strcpy" and "__builtin_strcpy_chk". */ +/* Handler for "__builtin_stack_restore". */ + +class kf_stack_restore : public known_function +{ +public: + bool matches_call_types_p (const call_details &) const final override + { + return true; + } + + /* Currently a no-op. */ +}; + +/* Handler for "__builtin_stack_save". */ + +class kf_stack_save : public known_function +{ +public: + bool matches_call_types_p (const call_details &) const final override + { + return true; + } + + /* Currently a no-op. */ +}; + +/* Handler for various stdio-related builtins that merely have external + effects that are out of scope for the analyzer: we only want to model + the effects on the return value. */ + +class kf_stdio_output_fn : public known_function +{ +public: + bool matches_call_types_p (const call_details &) const final override + { + return true; + } + + /* A no-op; we just want the conjured return value. */ +}; + +/* Handler for "strcpy" and "__builtin_strcpy_chk". */ + +class kf_strcpy : public known_function +{ +public: + kf_strcpy (unsigned int 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 + && cd.arg_is_pointer_p (0) + && cd.arg_is_pointer_p (1)); + } + + void impl_call_pre (const call_details &cd) const final override; + +private: + unsigned int m_num_args; +}; void -region_model::impl_call_strcpy (const call_details &cd) +kf_strcpy::impl_call_pre (const call_details &cd) const { + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); + const svalue *dest_sval = cd.get_arg_svalue (0); - const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0), + const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0), cd.get_ctxt ()); const svalue *src_sval = cd.get_arg_svalue (1); - const region *src_reg = deref_rvalue (src_sval, cd.get_arg_tree (1), + const region *src_reg = model->deref_rvalue (src_sval, cd.get_arg_tree (1), cd.get_ctxt ()); - const svalue *src_contents_sval = get_store_value (src_reg, - cd.get_ctxt ()); + const svalue *src_contents_sval = model->get_store_value (src_reg, + cd.get_ctxt ()); cd.maybe_set_lhs (dest_sval); /* Try to get the string size if SRC_REG is a string_region. */ - const svalue *copied_bytes_sval = get_string_size (src_reg); + const svalue *copied_bytes_sval = model->get_string_size (src_reg); /* Otherwise, check if the contents of SRC_REG is a string. */ if (copied_bytes_sval->get_kind () == SK_UNKNOWN) - copied_bytes_sval = get_string_size (src_contents_sval); + copied_bytes_sval = model->get_string_size (src_contents_sval); const region *sized_dest_reg - = m_mgr->get_sized_region (dest_reg, NULL_TREE, copied_bytes_sval); - set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ()); + = mgr->get_sized_region (dest_reg, NULL_TREE, copied_bytes_sval); + model->set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ()); } /* Handle the on_call_pre part of "strlen". */ +class kf_strlen : 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; +}; + void -region_model::impl_call_strlen (const call_details &cd) +kf_strlen::impl_call_pre (const call_details &cd) const { region_model_context *ctxt = cd.get_ctxt (); + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); + const svalue *arg_sval = cd.get_arg_svalue (0); - const region *buf_reg = deref_rvalue (arg_sval, cd.get_arg_tree (0), ctxt); + const region *buf_reg + = model->deref_rvalue (arg_sval, cd.get_arg_tree (0), ctxt); if (const string_region *str_reg = buf_reg->dyn_cast_string_region ()) { @@ -1589,7 +1827,7 @@ region_model::impl_call_strlen (const call_details &cd) { tree t_cst = build_int_cst (cd.get_lhs_type (), strlen_cst); const svalue *result_sval - = m_mgr->get_or_create_constant_svalue (t_cst); + = mgr->get_or_create_constant_svalue (t_cst); cd.maybe_set_lhs (result_sval); return; } @@ -1597,13 +1835,19 @@ region_model::impl_call_strlen (const call_details &cd) /* Otherwise a conjured value. */ } +class kf_ubsan_bounds : public internal_known_function +{ + /* Empty. */ +}; + /* Handle calls to functions referenced by __attribute__((malloc(FOO))). */ void region_model::impl_deallocation_call (const call_details &cd) { - impl_call_free (cd); + kf_free kf; + kf.impl_call_post (cd); } /* Populate KFM with instances of known functions supported by the core of the @@ -1612,6 +1856,54 @@ region_model::impl_deallocation_call (const call_details &cd) void register_known_functions (known_function_manager &kfm) { + /* Internal fns the analyzer has known_functions for. */ + { + kfm.add (IFN_BUILTIN_EXPECT, make_unique ()); + kfm.add (IFN_UBSAN_BOUNDS, make_unique ()); + } + + /* Built-ins the analyzer has known_functions for. */ + { + kfm.add (BUILT_IN_ALLOCA, make_unique ()); + kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, make_unique ()); + kfm.add (BUILT_IN_CALLOC, make_unique ()); + kfm.add (BUILT_IN_EXPECT, make_unique ()); + kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, make_unique ()); + kfm.add (BUILT_IN_FPRINTF, make_unique ()); + kfm.add (BUILT_IN_FPRINTF_UNLOCKED, make_unique ()); + kfm.add (BUILT_IN_FPUTC, make_unique ()); + kfm.add (BUILT_IN_FPUTC_UNLOCKED, make_unique ()); + kfm.add (BUILT_IN_FPUTS, make_unique ()); + kfm.add (BUILT_IN_FPUTS_UNLOCKED, make_unique ()); + kfm.add (BUILT_IN_FREE, make_unique ()); + kfm.add (BUILT_IN_FWRITE, make_unique ()); + kfm.add (BUILT_IN_FWRITE_UNLOCKED, make_unique ()); + kfm.add (BUILT_IN_MALLOC, make_unique ()); + kfm.add (BUILT_IN_MEMCPY, make_unique ()); + kfm.add (BUILT_IN_MEMCPY_CHK, make_unique ()); + kfm.add (BUILT_IN_MEMSET, make_unique ()); + kfm.add (BUILT_IN_MEMSET_CHK, make_unique ()); + kfm.add (BUILT_IN_PRINTF, make_unique ()); + kfm.add (BUILT_IN_PRINTF_UNLOCKED, make_unique ()); + kfm.add (BUILT_IN_PUTC, make_unique ()); + kfm.add (BUILT_IN_PUTCHAR, make_unique ()); + kfm.add (BUILT_IN_PUTCHAR_UNLOCKED, make_unique ()); + kfm.add (BUILT_IN_PUTC_UNLOCKED, make_unique ()); + kfm.add (BUILT_IN_PUTS, make_unique ()); + kfm.add (BUILT_IN_PUTS_UNLOCKED, make_unique ()); + kfm.add (BUILT_IN_REALLOC, make_unique ()); + kfm.add (BUILT_IN_STACK_RESTORE, make_unique ()); + kfm.add (BUILT_IN_STACK_SAVE, make_unique ()); + kfm.add (BUILT_IN_STRCHR, make_unique ()); + kfm.add (BUILT_IN_STRCPY, make_unique (2)); + kfm.add (BUILT_IN_STRCPY_CHK, make_unique (3)); + kfm.add (BUILT_IN_STRLEN, make_unique ()); + kfm.add (BUILT_IN_VFPRINTF, make_unique ()); + kfm.add (BUILT_IN_VPRINTF, make_unique ()); + + register_varargs_builtins (kfm); + } + /* Debugging/test support functions, all with a "__analyzer_" prefix. */ { kfm.add ("__analyzer_break", make_unique ()); @@ -1633,11 +1925,20 @@ register_known_functions (known_function_manager &kfm) make_unique ()); } - /* Known POSIX functions. */ + /* Known builtins and C standard library functions. */ + { + kfm.add ("getchar", make_unique ()); + kfm.add ("memset", make_unique ()); + } + + /* Known POSIX functions, and some non-standard extensions. */ { kfm.add ("accept", make_unique ()); kfm.add ("bind", make_unique ()); kfm.add ("connect", make_unique ()); + kfm.add ("fgets", make_unique ()); + kfm.add ("fgets_unlocked", make_unique ()); // non-standard + kfm.add ("fread", make_unique ()); kfm.add ("listen", make_unique ()); kfm.add ("pipe", make_unique (1)); kfm.add ("pipe2", make_unique (2)); @@ -1648,6 +1949,8 @@ register_known_functions (known_function_manager &kfm) /* glibc functions. */ { kfm.add ("__errno_location", make_unique ()); + kfm.add ("error", make_unique (3)); + kfm.add ("error_at_line", make_unique (5)); } /* C++ support functions. */ diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index e71fd41f62d..92f8b94b3f0 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -1160,13 +1160,11 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt) } /* Handle the pre-sm-state part of STMT, modifying this object in-place. - Write true to *OUT_TERMINATE_PATH if the path should be terminated. Write true to *OUT_UNKNOWN_SIDE_EFFECTS if the stmt has unknown side effects. */ void region_model::on_stmt_pre (const gimple *stmt, - bool *out_terminate_path, bool *out_unknown_side_effects, region_model_context *ctxt) { @@ -1196,8 +1194,7 @@ region_model::on_stmt_pre (const gimple *stmt, anything, for which we don't have a function body, or for which we don't know the fndecl. */ const gcall *call = as_a (stmt); - *out_unknown_side_effects - = on_call_pre (call, ctxt, out_terminate_path); + *out_unknown_side_effects = on_call_pre (call, ctxt); } break; @@ -2030,13 +2027,28 @@ region_model::maybe_get_copy_bounds (const region *src_reg, return NULL; } -/* Get any known_function for FNDECL, or NULL if there is none. */ +/* Get any known_function for FNDECL for call CD. + + The call must match all assumptions made by the known_function (such as + e.g. "argument 1's type must be a pointer type"). + + Return NULL if no known_function is found, or it does not match the + assumption(s). */ + +const known_function * +region_model::get_known_function (tree fndecl, const call_details &cd) const +{ + known_function_manager *known_fn_mgr = m_mgr->get_known_function_manager (); + return known_fn_mgr->get_match (fndecl, cd); +} + +/* Get any known_function for IFN, or NULL. */ const known_function * -region_model::get_known_function (tree fndecl) const +region_model::get_known_function (enum internal_fn ifn) const { known_function_manager *known_fn_mgr = m_mgr->get_known_function_manager (); - return known_fn_mgr->get_by_fndecl (fndecl); + return known_fn_mgr->get_internal_fn (ifn); } /* Update this model for the CALL stmt, using CTXT to report any @@ -2048,14 +2060,10 @@ region_model::get_known_function (tree fndecl) const Return true if the function call has unknown side effects (it wasn't recognized and we don't have a body for it, or are unable to tell which - fndecl it is). - - Write true to *OUT_TERMINATE_PATH if this execution path should be - terminated (e.g. the function call terminates the process). */ + fndecl it is). */ bool -region_model::on_call_pre (const gcall *call, region_model_context *ctxt, - bool *out_terminate_path) +region_model::on_call_pre (const gcall *call, region_model_context *ctxt) { call_details cd (call, this, ctxt); @@ -2099,188 +2107,28 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt, } if (gimple_call_internal_p (call)) - { - switch (gimple_call_internal_fn (call)) - { - default: - break; - case IFN_BUILTIN_EXPECT: - impl_call_builtin_expect (cd); - return false; - case IFN_UBSAN_BOUNDS: - return false; - case IFN_VA_ARG: - impl_call_va_arg (cd); - return false; - } - } + if (const known_function *kf + = get_known_function (gimple_call_internal_fn (call))) + { + kf->impl_call_pre (cd); + return false; + } if (tree callee_fndecl = get_fndecl_for_call (call, ctxt)) { - /* The various impl_call_* member functions are implemented - in region-model-impl-calls.cc. - Having them split out into separate functions makes it easier - to put breakpoints on the handling of specific functions. */ int callee_fndecl_flags = flags_from_decl_or_type (callee_fndecl); - if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL) - && gimple_builtin_call_types_compatible_p (call, callee_fndecl)) - switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl)) - { - default: - if (!(callee_fndecl_flags & (ECF_CONST | ECF_PURE))) - unknown_side_effects = true; - break; - case BUILT_IN_ALLOCA: - case BUILT_IN_ALLOCA_WITH_ALIGN: - impl_call_alloca (cd); - return false; - case BUILT_IN_CALLOC: - impl_call_calloc (cd); - return false; - case BUILT_IN_EXPECT: - case BUILT_IN_EXPECT_WITH_PROBABILITY: - impl_call_builtin_expect (cd); - return false; - case BUILT_IN_FREE: - /* Handle in "on_call_post". */ - break; - case BUILT_IN_MALLOC: - impl_call_malloc (cd); - return false; - case BUILT_IN_MEMCPY: - case BUILT_IN_MEMCPY_CHK: - impl_call_memcpy (cd); - return false; - case BUILT_IN_MEMSET: - case BUILT_IN_MEMSET_CHK: - impl_call_memset (cd); - return false; - break; - case BUILT_IN_REALLOC: - return false; - case BUILT_IN_STRCHR: - /* Handle in "on_call_post". */ - return false; - case BUILT_IN_STRCPY: - case BUILT_IN_STRCPY_CHK: - impl_call_strcpy (cd); - return false; - case BUILT_IN_STRLEN: - impl_call_strlen (cd); - return false; - - case BUILT_IN_STACK_SAVE: - case BUILT_IN_STACK_RESTORE: - return false; - - /* Stdio builtins. */ - case BUILT_IN_FPRINTF: - case BUILT_IN_FPRINTF_UNLOCKED: - case BUILT_IN_PUTC: - case BUILT_IN_PUTC_UNLOCKED: - case BUILT_IN_FPUTC: - case BUILT_IN_FPUTC_UNLOCKED: - case BUILT_IN_FPUTS: - case BUILT_IN_FPUTS_UNLOCKED: - case BUILT_IN_FWRITE: - case BUILT_IN_FWRITE_UNLOCKED: - case BUILT_IN_PRINTF: - case BUILT_IN_PRINTF_UNLOCKED: - case BUILT_IN_PUTCHAR: - case BUILT_IN_PUTCHAR_UNLOCKED: - case BUILT_IN_PUTS: - case BUILT_IN_PUTS_UNLOCKED: - case BUILT_IN_VFPRINTF: - case BUILT_IN_VPRINTF: - /* These stdio builtins have external effects that are out - of scope for the analyzer: we only want to model the effects - on the return value. */ - break; - - case BUILT_IN_VA_START: - impl_call_va_start (cd); - return false; - case BUILT_IN_VA_COPY: - impl_call_va_copy (cd); - return false; - } - else if (is_named_call_p (callee_fndecl, "malloc", call, 1)) - { - impl_call_malloc (cd); - return false; - } - else if (is_named_call_p (callee_fndecl, "calloc", call, 2)) - { - impl_call_calloc (cd); - return false; - } - else if (is_named_call_p (callee_fndecl, "alloca", call, 1)) + if (const known_function *kf = get_known_function (callee_fndecl, cd)) { - impl_call_alloca (cd); + kf->impl_call_pre (cd); return false; } - else if (is_named_call_p (callee_fndecl, "realloc", call, 2)) - { - impl_call_realloc (cd); - return false; - } - else if (is_named_call_p (callee_fndecl, "error")) + else if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL) + && gimple_builtin_call_types_compatible_p (call, callee_fndecl)) { - if (impl_call_error (cd, 3, out_terminate_path)) - return false; - else + if (!(callee_fndecl_flags & (ECF_CONST | ECF_PURE))) unknown_side_effects = true; } - else if (is_named_call_p (callee_fndecl, "error_at_line")) - { - if (impl_call_error (cd, 5, out_terminate_path)) - return false; - else - unknown_side_effects = true; - } - else if (is_named_call_p (callee_fndecl, "fgets", call, 3) - || is_named_call_p (callee_fndecl, "fgets_unlocked", call, 3)) - { - impl_call_fgets (cd); - return false; - } - else if (is_named_call_p (callee_fndecl, "fread", call, 4)) - { - impl_call_fread (cd); - return false; - } - else if (is_named_call_p (callee_fndecl, "getchar", call, 0)) - { - /* No side-effects (tracking stream state is out-of-scope - for the analyzer). */ - } - else if (is_named_call_p (callee_fndecl, "memset", call, 3) - && POINTER_TYPE_P (cd.get_arg_type (0))) - { - impl_call_memset (cd); - return false; - } - else if (is_named_call_p (callee_fndecl, "strchr", call, 2) - && POINTER_TYPE_P (cd.get_arg_type (0))) - { - /* Handle in "on_call_post". */ - return false; - } - else if (is_named_call_p (callee_fndecl, "strlen", call, 1) - && POINTER_TYPE_P (cd.get_arg_type (0))) - { - impl_call_strlen (cd); - return false; - } - else if (const known_function *kf = get_known_function (callee_fndecl)) - { - if (kf->matches_call_types_p (cd)) - { - kf->impl_call_pre (cd); - return false; - } - } else if (!fndecl_has_gimple_body_p (callee_fndecl) && (!(callee_fndecl_flags & (ECF_CONST | ECF_PURE))) && !fndecl_built_in_p (callee_fndecl)) @@ -2310,25 +2158,11 @@ region_model::on_call_post (const gcall *call, if (tree callee_fndecl = get_fndecl_for_call (call, ctxt)) { call_details cd (call, this, ctxt); - if (is_named_call_p (callee_fndecl, "free", call, 1)) + if (const known_function *kf = get_known_function (callee_fndecl, cd)) { - impl_call_free (cd); + kf->impl_call_post (cd); return; } - else if (is_named_call_p (callee_fndecl, "strchr", call, 2) - && POINTER_TYPE_P (cd.get_arg_type (0))) - { - impl_call_strchr (cd); - return; - } - else if (const known_function *kf = get_known_function (callee_fndecl)) - { - if (kf->matches_call_types_p (cd)) - { - kf->impl_call_post (cd); - return; - } - } /* Was this fndecl referenced by __attribute__((malloc(FOO)))? */ if (lookup_attribute ("*dealloc", DECL_ATTRIBUTES (callee_fndecl))) @@ -2336,24 +2170,6 @@ region_model::on_call_post (const gcall *call, impl_deallocation_call (cd); return; } - if (fndecl_built_in_p (callee_fndecl, BUILT_IN_NORMAL) - && gimple_builtin_call_types_compatible_p (call, callee_fndecl)) - switch (DECL_UNCHECKED_FUNCTION_CODE (callee_fndecl)) - { - default: - break; - case BUILT_IN_REALLOC: - impl_call_realloc (cd); - return; - - case BUILT_IN_STRCHR: - impl_call_strchr (cd); - return; - - case BUILT_IN_VA_END: - impl_call_va_end (cd); - return; - } } if (unknown_side_effects) diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 244780eb4f4..8e4616c28de 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -260,6 +260,7 @@ public: { 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; @@ -326,7 +327,6 @@ class region_model void on_stmt_pre (const gimple *stmt, - bool *out_terminate_path, bool *out_unknown_side_effects, region_model_context *ctxt); @@ -334,38 +334,15 @@ class region_model const svalue *get_gassign_result (const gassign *assign, region_model_context *ctxt); void on_asm_stmt (const gasm *asm_stmt, region_model_context *ctxt); - bool on_call_pre (const gcall *stmt, region_model_context *ctxt, - bool *out_terminate_path); + bool on_call_pre (const gcall *stmt, region_model_context *ctxt); void on_call_post (const gcall *stmt, bool unknown_side_effects, region_model_context *ctxt); void purge_state_involving (const svalue *sval, region_model_context *ctxt); - /* Specific handling for on_call_pre. */ - void impl_call_alloca (const call_details &cd); - void impl_call_builtin_expect (const call_details &cd); - void impl_call_calloc (const call_details &cd); - bool impl_call_error (const call_details &cd, unsigned min_args, - bool *out_terminate_path); - void impl_call_fgets (const call_details &cd); - void impl_call_fread (const call_details &cd); - void impl_call_free (const call_details &cd); - void impl_call_malloc (const call_details &cd); - void impl_call_memcpy (const call_details &cd); - void impl_call_memset (const call_details &cd); - void impl_call_realloc (const call_details &cd); - void impl_call_strchr (const call_details &cd); - void impl_call_strcpy (const call_details &cd); - void impl_call_strlen (const call_details &cd); void impl_deallocation_call (const call_details &cd); - /* Implemented in varargs.cc. */ - void impl_call_va_start (const call_details &cd); - void impl_call_va_copy (const call_details &cd); - void impl_call_va_arg (const call_details &cd); - void impl_call_va_end (const call_details &cd); - const svalue *maybe_get_copy_bounds (const region *src_reg, const svalue *num_bytes_sval); void update_for_int_cst_return (const call_details &cd, @@ -562,6 +539,9 @@ class region_model tree expr, region_model_context *ctxt) const; + void check_region_for_write (const region *dest_reg, + region_model_context *ctxt) const; + private: const region *get_lvalue_1 (path_var pv, region_model_context *ctxt) const; const svalue *get_rvalue_1 (path_var pv, region_model_context *ctxt) const; @@ -573,7 +553,9 @@ private: get_representative_path_var_1 (const region *reg, svalue_set *visited) const; - const known_function *get_known_function (tree fndecl) const; + const known_function *get_known_function (tree fndecl, + const call_details &cd) const; + const known_function *get_known_function (enum internal_fn) const; bool add_constraints_from_binop (const svalue *outer_lhs, enum tree_code outer_op, @@ -622,8 +604,6 @@ private: void check_region_access (const region *reg, enum access_direction dir, region_model_context *ctxt) const; - void check_region_for_write (const region *dest_reg, - region_model_context *ctxt) const; void check_region_for_read (const region *src_reg, region_model_context *ctxt) const; void check_region_size (const region *lhs_reg, const svalue *rhs_sval, diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index dd10356155c..94ca295fae8 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -2175,7 +2175,7 @@ malloc_state_machine::on_deallocator_call (sm_context *sm_ctxt, Check for free of non-heap or mismatching allocators, transitioning to the "stop" state for such cases. - Otherwise, region_model::impl_call_realloc will later + Otherwise, kf_realloc::impl_call_post will later get called (which will handle other sm-state transitions when the state is bifurcated). */ @@ -2432,7 +2432,7 @@ make_malloc_state_machine (logger *logger) } /* Specialcase hook for handling realloc, for use by - region_model::impl_call_realloc::success_with_move::update_model. */ + kf_realloc::impl_call_post::success_with_move::update_model. */ void region_model::on_realloc_with_move (const call_details &cd, diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc index 6fc20f07a37..1da5a46f677 100644 --- a/gcc/analyzer/varargs.cc +++ b/gcc/analyzer/varargs.cc @@ -647,33 +647,45 @@ make_va_list_state_machine (logger *logger) return new va_list_state_machine (logger); } -/* Handle the on_call_pre part of "__builtin_va_start". */ +/* Handler for "__builtin_va_start". */ + +class kf_va_start : public known_function +{ +public: + bool matches_call_types_p (const call_details &) const + { + return true; + } + void impl_call_pre (const call_details &cd) const final override; +}; void -region_model::impl_call_va_start (const call_details &cd) +kf_va_start::impl_call_pre (const call_details &cd) const { + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); const svalue *out_ptr = cd.get_arg_svalue (0); const region *out_reg - = deref_rvalue (out_ptr, cd.get_arg_tree (0), cd.get_ctxt ()); + = model->deref_rvalue (out_ptr, cd.get_arg_tree (0), cd.get_ctxt ()); + const frame_region *frame = model->get_current_frame (); /* "*out_ptr = &IMPL_REGION;". */ - const region *impl_reg = m_mgr->create_region_for_alloca (m_current_frame); + const region *impl_reg = mgr->create_region_for_alloca (frame); /* We abuse the types here, since va_list_type isn't necessarily anything to do with a pointer. */ - const svalue *ptr_to_impl_reg = m_mgr->get_ptr_svalue (NULL_TREE, impl_reg); - set_value (out_reg, ptr_to_impl_reg, cd.get_ctxt ()); + const svalue *ptr_to_impl_reg = mgr->get_ptr_svalue (NULL_TREE, impl_reg); + model->set_value (out_reg, ptr_to_impl_reg, cd.get_ctxt ()); - if (get_stack_depth () > 1) + if (model->get_stack_depth () > 1) { /* The interprocedural case: the frame containing the va_start call will have been populated with any variadic aruguments. Initialize IMPL_REGION with a ptr to var_arg_region 0. */ - const region *init_var_arg_reg - = m_mgr->get_var_arg_region (get_current_frame (), 0); + const region *init_var_arg_reg = mgr->get_var_arg_region (frame, 0); const svalue *ap_sval - = m_mgr->get_ptr_svalue (NULL_TREE, init_var_arg_reg); - set_value (impl_reg, ap_sval, cd.get_ctxt ()); + = mgr->get_ptr_svalue (NULL_TREE, init_var_arg_reg); + model->set_value (impl_reg, ap_sval, cd.get_ctxt ()); } else { @@ -682,40 +694,52 @@ region_model::impl_call_va_start (const call_details &cd) Initialize IMPL_REGION as the UNKNOWN_SVALUE to avoid state explosions on repeated calls to va_arg. */ const svalue *unknown_sval - = m_mgr->get_or_create_unknown_svalue (NULL_TREE); - set_value (impl_reg, unknown_sval, cd.get_ctxt ()); + = mgr->get_or_create_unknown_svalue (NULL_TREE); + model->set_value (impl_reg, unknown_sval, cd.get_ctxt ()); } } -/* Handle the on_call_pre part of "__builtin_va_copy". */ +/* Handler for "__builtin_va_copy". */ + +class kf_va_copy : public known_function +{ +public: + bool matches_call_types_p (const call_details &cd) const + { + return true; + } + void impl_call_pre (const call_details &cd) const final override; +}; void -region_model::impl_call_va_copy (const call_details &cd) +kf_va_copy::impl_call_pre (const call_details &cd) const { + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); const svalue *out_dst_ptr = cd.get_arg_svalue (0); const svalue *in_va_list - = get_va_copy_arg (this, cd.get_ctxt (), cd.get_call_stmt (), 1); - in_va_list = check_for_poison (in_va_list, - get_va_list_diag_arg (cd.get_arg_tree (1)), - cd.get_ctxt ()); + = get_va_copy_arg (model, cd.get_ctxt (), cd.get_call_stmt (), 1); + in_va_list + = model->check_for_poison (in_va_list, + get_va_list_diag_arg (cd.get_arg_tree (1)), + cd.get_ctxt ()); const region *out_dst_reg - = deref_rvalue (out_dst_ptr, cd.get_arg_tree (0), cd.get_ctxt ()); + = model->deref_rvalue (out_dst_ptr, cd.get_arg_tree (0), cd.get_ctxt ()); /* "*out_dst_ptr = &NEW_IMPL_REGION;". */ const region *new_impl_reg - = m_mgr->create_region_for_alloca (m_current_frame); + = mgr->create_region_for_alloca (model->get_current_frame ()); const svalue *ptr_to_new_impl_reg - = m_mgr->get_ptr_svalue (NULL_TREE, new_impl_reg); - set_value (out_dst_reg, ptr_to_new_impl_reg, cd.get_ctxt ()); + = mgr->get_ptr_svalue (NULL_TREE, new_impl_reg); + model->set_value (out_dst_reg, ptr_to_new_impl_reg, cd.get_ctxt ()); if (const region *old_impl_reg = in_va_list->maybe_get_region ()) { - /* "(NEW_IMPL_REGION) = (OLD_IMPL_REGION);". */ const svalue *existing_sval - = get_store_value (old_impl_reg, cd.get_ctxt ()); - set_value (new_impl_reg, existing_sval, cd.get_ctxt ()); + = model->get_store_value (old_impl_reg, cd.get_ctxt ()); + model->set_value (new_impl_reg, existing_sval, cd.get_ctxt ()); } } @@ -956,26 +980,35 @@ maybe_get_var_arg_region (const svalue *ap_sval) return NULL; } -/* Handle the on_call_pre part of "__builtin_va_arg". */ +/* Handler for "__builtin_va_arg". */ + +class kf_va_arg : public internal_known_function +{ +public: + void impl_call_pre (const call_details &cd) const final override; +}; void -region_model::impl_call_va_arg (const call_details &cd) +kf_va_arg::impl_call_pre (const call_details &cd) const { region_model_context *ctxt = cd.get_ctxt (); + region_model *model = cd.get_model (); + region_model_manager *mgr = cd.get_manager (); const svalue *in_ptr = cd.get_arg_svalue (0); - const region *ap_reg = deref_rvalue (in_ptr, cd.get_arg_tree (0), ctxt); + const region *ap_reg + = model->deref_rvalue (in_ptr, cd.get_arg_tree (0), ctxt); - const svalue *ap_sval = get_store_value (ap_reg, ctxt); + const svalue *ap_sval = model->get_store_value (ap_reg, ctxt); if (const svalue *cast = ap_sval->maybe_undo_cast ()) ap_sval = cast; tree va_list_tree = get_va_list_diag_arg (cd.get_arg_tree (0)); - ap_sval = check_for_poison (ap_sval, va_list_tree, ctxt); + ap_sval = model->check_for_poison (ap_sval, va_list_tree, ctxt); if (const region *impl_reg = ap_sval->maybe_get_region ()) { - const svalue *old_impl_sval = get_store_value (impl_reg, ctxt); + const svalue *old_impl_sval = model->get_store_value (impl_reg, ctxt); if (const var_arg_region *arg_reg = maybe_get_var_arg_region (old_impl_sval)) { @@ -992,8 +1025,8 @@ region_model::impl_call_va_arg (const call_details &cd) has a conjured_svalue), or warn if there's a problem (incompatible types, or if we've run out of args). */ if (const svalue *arg_sval - = m_store.get_any_binding (m_mgr->get_store_manager (), - arg_reg)) + = model->get_store ()->get_any_binding + (mgr->get_store_manager (), arg_reg)) { tree lhs_type = cd.get_lhs_type (); tree arg_type = arg_sval->get_type (); @@ -1031,28 +1064,42 @@ region_model::impl_call_va_arg (const call_details &cd) { /* Set impl_reg to UNKNOWN to suppress further warnings. */ const svalue *new_ap_sval - = m_mgr->get_or_create_unknown_svalue (impl_reg->get_type ()); - set_value (impl_reg, new_ap_sval, ctxt); + = mgr->get_or_create_unknown_svalue (impl_reg->get_type ()); + model->set_value (impl_reg, new_ap_sval, ctxt); } else { /* Update impl_reg to advance to the next arg. */ const region *next_var_arg_region - = m_mgr->get_var_arg_region (frame_reg, next_arg_idx + 1); + = mgr->get_var_arg_region (frame_reg, next_arg_idx + 1); const svalue *new_ap_sval - = m_mgr->get_ptr_svalue (NULL_TREE, next_var_arg_region); - set_value (impl_reg, new_ap_sval, ctxt); + = mgr->get_ptr_svalue (NULL_TREE, next_var_arg_region); + model->set_value (impl_reg, new_ap_sval, ctxt); } } } } -/* Handle the on_call_post part of "__builtin_va_end". */ +/* Handler for "__builtin_va_end". */ + +class kf_va_end : public known_function +{ +public: + bool matches_call_types_p (const call_details &) const + { + return true; + } +}; + +/* Populate KFM with instances of known functions relating to varargs. */ void -region_model::impl_call_va_end (const call_details &) +register_varargs_builtins (known_function_manager &kfm) { - /* No-op. */ + kfm.add (BUILT_IN_VA_START, make_unique ()); + kfm.add (BUILT_IN_VA_COPY, make_unique ()); + kfm.add (IFN_VA_ARG, make_unique ()); + kfm.add (BUILT_IN_VA_END, make_unique ()); } } // namespace ana