public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-4246] analyzer: eliminate region_model::impl_call_* special cases
@ 2022-11-22 22:32 David Malcolm
0 siblings, 0 replies; only message in thread
From: David Malcolm @ 2022-11-22 22:32 UTC (permalink / raw)
To: gcc-cvs
https://gcc.gnu.org/g:6bd31b33daa3c7635d886ff2cebd915748db2084
commit r13-4246-g6bd31b33daa3c7635d886ff2cebd915748db2084
Author: David Malcolm <dmalcolm@redhat.com>
Date: Tue Nov 22 17:29:21 2022 -0500
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>
Diff:
---
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 <const gcall *> (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 <const greturn *> (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<known_function> 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<known_function> 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<known_function_manager *>(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<known_function> 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<known_function> kf);
+ void add (enum internal_fn ifn, std::unique_ptr<known_function> 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<tree, known_function *> 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 <error.h>.
+/* Handler for "error" and "error_at_line" from GNU's non-standard <error.h>.
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<strchr_call_info> (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<kf_expect> ());
+ kfm.add (IFN_UBSAN_BOUNDS, make_unique<kf_ubsan_bounds> ());
+ }
+
+ /* Built-ins the analyzer has known_functions for. */
+ {
+ kfm.add (BUILT_IN_ALLOCA, make_unique<kf_alloca> ());
+ kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, make_unique<kf_alloca> ());
+ kfm.add (BUILT_IN_CALLOC, make_unique<kf_calloc> ());
+ kfm.add (BUILT_IN_EXPECT, make_unique<kf_expect> ());
+ kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, make_unique<kf_expect> ());
+ kfm.add (BUILT_IN_FPRINTF, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_FPRINTF_UNLOCKED, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_FPUTC, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_FPUTC_UNLOCKED, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_FPUTS, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_FPUTS_UNLOCKED, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_FREE, make_unique<kf_free> ());
+ kfm.add (BUILT_IN_FWRITE, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_FWRITE_UNLOCKED, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_MALLOC, make_unique<kf_malloc> ());
+ kfm.add (BUILT_IN_MEMCPY, make_unique<kf_memcpy> ());
+ kfm.add (BUILT_IN_MEMCPY_CHK, make_unique<kf_memcpy> ());
+ kfm.add (BUILT_IN_MEMSET, make_unique<kf_memset> ());
+ kfm.add (BUILT_IN_MEMSET_CHK, make_unique<kf_memset> ());
+ kfm.add (BUILT_IN_PRINTF, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_PRINTF_UNLOCKED, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_PUTC, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_PUTCHAR, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_PUTCHAR_UNLOCKED, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_PUTC_UNLOCKED, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_PUTS, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_PUTS_UNLOCKED, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_REALLOC, make_unique<kf_realloc> ());
+ kfm.add (BUILT_IN_STACK_RESTORE, make_unique<kf_stack_restore> ());
+ kfm.add (BUILT_IN_STACK_SAVE, make_unique<kf_stack_save> ());
+ kfm.add (BUILT_IN_STRCHR, make_unique<kf_strchr> ());
+ kfm.add (BUILT_IN_STRCPY, make_unique<kf_strcpy> (2));
+ kfm.add (BUILT_IN_STRCPY_CHK, make_unique<kf_strcpy> (3));
+ kfm.add (BUILT_IN_STRLEN, make_unique<kf_strlen> ());
+ kfm.add (BUILT_IN_VFPRINTF, make_unique<kf_stdio_output_fn> ());
+ kfm.add (BUILT_IN_VPRINTF, make_unique<kf_stdio_output_fn> ());
+
+ register_varargs_builtins (kfm);
+ }
+
/* Debugging/test support functions, all with a "__analyzer_" prefix. */
{
kfm.add ("__analyzer_break", make_unique<kf_analyzer_break> ());
@@ -1633,11 +1925,20 @@ register_known_functions (known_function_manager &kfm)
make_unique<kf_analyzer_get_unknown_ptr> ());
}
- /* Known POSIX functions. */
+ /* Known builtins and C standard library functions. */
+ {
+ kfm.add ("getchar", make_unique<kf_getchar> ());
+ kfm.add ("memset", make_unique<kf_memset> ());
+ }
+
+ /* Known POSIX functions, and some non-standard extensions. */
{
kfm.add ("accept", make_unique<kf_accept> ());
kfm.add ("bind", make_unique<kf_bind> ());
kfm.add ("connect", make_unique<kf_connect> ());
+ kfm.add ("fgets", make_unique<kf_fgets> ());
+ kfm.add ("fgets_unlocked", make_unique<kf_fgets> ()); // non-standard
+ kfm.add ("fread", make_unique<kf_fread> ());
kfm.add ("listen", make_unique<kf_listen> ());
kfm.add ("pipe", make_unique<kf_pipe> (1));
kfm.add ("pipe2", make_unique<kf_pipe> (2));
@@ -1648,6 +1949,8 @@ register_known_functions (known_function_manager &kfm)
/* glibc functions. */
{
kfm.add ("__errno_location", make_unique<kf_errno_location> ());
+ kfm.add ("error", make_unique<kf_error> (3));
+ kfm.add ("error_at_line", make_unique<kf_error> (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 <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)
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<kf_va_start> ());
+ kfm.add (BUILT_IN_VA_COPY, make_unique<kf_va_copy> ());
+ kfm.add (IFN_VA_ARG, make_unique<kf_va_arg> ());
+ kfm.add (BUILT_IN_VA_END, make_unique<kf_va_end> ());
}
} // namespace ana
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2022-11-22 22:32 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-22 22:32 [gcc r13-4246] analyzer: eliminate region_model::impl_call_* special cases David Malcolm
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).