diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 83bb0011a3f5f663de790dbe51dbb778a7b6d667..d424b43f2de2cf5f099437acd7884d4e77aa787c 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 db1881cd14024e81e252053fe0a45c75ad6c3c4a..0c49bb2687270566f977c9f5b6872c92ecd3c717 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 7341b06848083fa7d56b1a088e39db0f921b68f2..e17350da5ec3c25c9f977fd4b74c9580c4dc288b 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 daf1bc57855d7e6911aa62c082c22129f4a2e4d0..188cb8e034a8abf5444a4777224b17ffb8172635 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 8a44c97eec9492eef2cfcab54612739d7ef0fe1b..6962ffd400f8e20dc607ff7ccf92517c9120e96e 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 e71fd41f62d0cd92b58b29fed8dc39f3604c7ea5..92f8b94b3f09ef180b1aa0ab2a01c0d3a0264713 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 244780eb4f436ba09d47c1b778950ad78d109623..8e4616c28dedccf4bf213169477df8c5a015b79f 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 dd10356155cbc25fa15d1d7586d1338e175ef704..94ca295fae8f6441bda67c2d287688d1950ecb00 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 6fc20f07a37ebda4e19edd3fa0f0519e48e425a8..1da5a46f67774da72b448e6bd36a92a7e12c9821 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