diff options
author | David Malcolm <dmalcolm@redhat.com> | 2022-11-22 17:29:21 -0500 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2022-11-22 17:29:21 -0500 |
commit | 6bd31b33daa3c7635d886ff2cebd915748db2084 (patch) | |
tree | 11cc6160bae24de4c8be221257bf9743ed65aca4 /gcc/analyzer/region-model.cc | |
parent | 936d40b9ba9cdf8571bc5c366f3d3237cabc30c2 (diff) | |
download | gcc-6bd31b33daa3c7635d886ff2cebd915748db2084.zip gcc-6bd31b33daa3c7635d886ff2cebd915748db2084.tar.gz gcc-6bd31b33daa3c7635d886ff2cebd915748db2084.tar.bz2 |
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 <dmalcolm@redhat.com>
Diffstat (limited to 'gcc/analyzer/region-model.cc')
-rw-r--r-- | gcc/analyzer/region-model.cc | 252 |
1 files changed, 34 insertions, 218 deletions
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index e71fd41..92f8b94 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 <const gcall *> (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) |