From 6bd31b33daa3c7635d886ff2cebd915748db2084 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Tue, 22 Nov 2022 17:29:21 -0500 Subject: analyzer: eliminate region_model::impl_call_* special cases 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. 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/known-function-manager.cc | 85 ++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 9 deletions(-) (limited to 'gcc/analyzer/known-function-manager.cc') diff --git a/gcc/analyzer/known-function-manager.cc b/gcc/analyzer/known-function-manager.cc index 7341b06..e17350d 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 */ -- cgit v1.1