aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/region-model.cc
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2022-11-22 17:29:21 -0500
committerDavid Malcolm <dmalcolm@redhat.com>2022-11-22 17:29:21 -0500
commit6bd31b33daa3c7635d886ff2cebd915748db2084 (patch)
tree11cc6160bae24de4c8be221257bf9743ed65aca4 /gcc/analyzer/region-model.cc
parent936d40b9ba9cdf8571bc5c366f3d3237cabc30c2 (diff)
downloadgcc-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.cc252
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)