diff options
author | David Malcolm <dmalcolm@redhat.com> | 2023-08-09 16:17:04 -0400 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2023-08-09 16:17:04 -0400 |
commit | 73da34a538ddc2ad17e80e93b64b526f704693d2 (patch) | |
tree | db8de59479d798a1c3ba42788cb36617c53470f3 | |
parent | e5fe7f2fd69d5a950d8c1e2a068669bda376b2d4 (diff) | |
download | gcc-73da34a538ddc2ad17e80e93b64b526f704693d2.zip gcc-73da34a538ddc2ad17e80e93b64b526f704693d2.tar.gz gcc-73da34a538ddc2ad17e80e93b64b526f704693d2.tar.bz2 |
analyzer: remove default return value from region_model::on_call_pre
Previously, the code for simulating calls to external functions in
region_model::on_call_pre wrote a default svalue to the LHS of the
call statement, which could be further overwritten by known_function
subclasses.
Unfortunately, this led to messy hacks, such as when the default svalue
was an allocation: the LHS would be written to with two different
heap-allocated regions, requiring special-case cleanups to avoid the
stray state from the first heap allocation leading to state explosions;
see r14-3001-g021077b94741c9.
The following patch eliminates this write of a default svalue to the LHS
of callsite. Instead, all known_function implementations that have a
return value are now responsible for set the LHS themselves. A new
call_details::set_any_lhs_with_defaults function is provided to make it
easy to get the old behavior.
On working through the various known_function subclasses, I noticed that
memset was using the default behavior. That patch updates this so that
it's now known to return its first parameter.
Cleaning this up eliminates various doubling of saved_diagnostics (e.g.
for dubious_allocation_size) where it was generating a diagnostic for
both writes to the LHS, deduplicating them to the first diagnostic (with
the default LHS), and then failing to create a region_creation_event
when emitting the diagnostic, leading to the fallback wording in
dubious_allocation_size::describe_final_event, such as:
(1) allocated 42 bytes and assigned to ‘int32_t *’ {aka ‘int *’} here; ‘sizeof (int32_t {aka int})’ is ‘4’
Without the double write to the LHS, it creates a region_creation_event,
so we get the allocation and the assignment as two separate events in
the diagnostic path, e.g.:
(1) allocated 42 bytes here
(2) assigned to ‘int32_t *’ {aka ‘int *’} here; ‘sizeof (int32_t {aka int})’ is ‘4’
gcc/analyzer/ChangeLog:
* analyzer.h (class pure_known_function_with_default_return): New
subclass.
* call-details.cc (const_fn_p): Move here from region-model.cc.
(maybe_get_const_fn_result): Likewise.
(get_result_size_in_bytes): Likewise.
(call_details::set_any_lhs_with_defaults): New function, based on
code in region_model::on_call_pre.
* call-details.h (call_details::set_any_lhs_with_defaults): New
decl.
* diagnostic-manager.cc
(diagnostic_manager::emit_saved_diagnostic): Log the index of the
saved_diagnostic.
* kf.cc (pure_known_function_with_default_return::impl_call_pre):
New.
(kf_memset::impl_call_pre): Set the LHS to the first param.
(kf_putenv::impl_call_pre): Call cd.set_any_lhs_with_defaults.
(kf_sprintf::impl_call_pre): Call cd.set_any_lhs_with_defaults.
(class kf_stack_restore): Derive from
pure_known_function_with_default_return.
(class kf_stack_save): Likewise.
(kf_strlen::impl_call_pre): Call cd.set_any_lhs_with_defaults.
* region-model-reachability.cc (reachable_regions::handle_sval):
Remove logic for symbolic regions for pointers.
* region-model.cc (region_model::canonicalize): Remove purging of
dynamic extents workaround for surplus values from
region_model::on_call_pre's default LHS code.
(const_fn_p): Move to call-details.cc.
(maybe_get_const_fn_result): Likewise.
(get_result_size_in_bytes): Likewise.
(region_model::update_for_nonzero_return): Call
cd.set_any_lhs_with_defaults.
(region_model::on_call_pre): Remove the assignment to the LHS of a
default return value, instead requiring all known_function
implementations to write to any LHS of the call. Use
cd.set_any_lhs_with_defaults on the non-kf paths.
* sm-fd.cc (kf_socket::outcome_of_socket::update_model): Use
cd.set_any_lhs_with_defaults when failing to get at fd state.
(kf_bind::outcome_of_bind::update_model): Likewise.
(kf_listen::outcome_of_listen::update_model): Likewise.
(kf_accept::outcome_of_accept::update_model): Likewise.
(kf_connect::outcome_of_connect::update_model): Likewise.
(kf_read::impl_call_pre): Use cd.set_any_lhs_with_defaults.
* sm-file.cc (class kf_stdio_output_fn): Derive from
pure_known_function_with_default_return.
(class kf_ferror): Likewise.
(class kf_fileno): Likewise.
(kf_fgets::impl_call_pre): Use cd.set_any_lhs_with_defaults.
(kf_read::impl_call_pre): Likewise.
(class kf_getc): Derive from
pure_known_function_with_default_return.
(class kf_getchar): Likewise.
* varargs.cc (kf_va_arg::impl_call_pre): Use
cd.set_any_lhs_with_defaults.
gcc/testsuite/ChangeLog:
* gcc.dg/analyzer/allocation-size-1.c: Update expected results
to reflect splitting of allocation size and assignment messages
from a single event into pairs of events
* gcc.dg/analyzer/allocation-size-2.c: Likewise.
* gcc.dg/analyzer/allocation-size-3.c: Likewise.
* gcc.dg/analyzer/allocation-size-4.c: Likewise.
* gcc.dg/analyzer/allocation-size-multiline-1.c: Likewise.
* gcc.dg/analyzer/allocation-size-multiline-2.c: Likewise.
* gcc.dg/analyzer/allocation-size-multiline-3.c: Likewise.
* gcc.dg/analyzer/memset-1.c (test_1): Verify that the return
value is the initial argument.
* gcc.dg/plugin/analyzer_kernel_plugin.c
(copy_across_boundary_fn::impl_call_pre): Ensure the LHS is set on
the "known zero size" case.
* gcc.dg/plugin/analyzer_known_fns_plugin.c
(known_function_attempt_to_copy::impl_call_pre): Likewise.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
20 files changed, 266 insertions, 214 deletions
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 579517c..93a28b4 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -293,6 +293,16 @@ public: } }; +/* Abstract subclass of known_function that merely sets the return + value of the function (based on function attributes), and assumes + it has no side-effects. */ + +class pure_known_function_with_default_return : public known_function +{ +public: + void impl_call_pre (const call_details &cd) const override; +}; + extern void register_known_functions (known_function_manager &mgr); extern void register_known_analyzer_functions (known_function_manager &kfm); extern void register_known_fd_functions (known_function_manager &kfm); diff --git a/gcc/analyzer/call-details.cc b/gcc/analyzer/call-details.cc index 793317e..93f4846 100644 --- a/gcc/analyzer/call-details.cc +++ b/gcc/analyzer/call-details.cc @@ -105,6 +105,135 @@ call_details::maybe_set_lhs (const svalue *result) const return false; } +/* Return true if CD is known to be a call to a function with + __attribute__((const)). */ + +static bool +const_fn_p (const call_details &cd) +{ + tree fndecl = cd.get_fndecl_for_call (); + if (!fndecl) + return false; + gcc_assert (DECL_P (fndecl)); + return TREE_READONLY (fndecl); +} + +/* If this CD is known to be a call to a function with + __attribute__((const)), attempt to get a const_fn_result_svalue + based on the arguments, or return NULL otherwise. */ + +static const svalue * +maybe_get_const_fn_result (const call_details &cd) +{ + if (!const_fn_p (cd)) + return NULL; + + unsigned num_args = cd.num_args (); + if (num_args > const_fn_result_svalue::MAX_INPUTS) + /* Too many arguments. */ + return NULL; + + auto_vec<const svalue *> inputs (num_args); + for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++) + { + const svalue *arg_sval = cd.get_arg_svalue (arg_idx); + if (!arg_sval->can_have_associated_state_p ()) + return NULL; + inputs.quick_push (arg_sval); + } + + region_model_manager *mgr = cd.get_manager (); + const svalue *sval + = mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (), + cd.get_fndecl_for_call (), + inputs); + return sval; +} + +/* Look for attribute "alloc_size" on the called function and, if found, + return a symbolic value of type size_type_node for the allocation size + based on the call's parameters. + Otherwise, return null. */ + +static const svalue * +get_result_size_in_bytes (const call_details &cd) +{ + const tree attr = cd.lookup_function_attribute ("alloc_size"); + if (!attr) + return nullptr; + + const tree atval_1 = TREE_VALUE (attr); + if (!atval_1) + return nullptr; + + unsigned argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval_1)) - 1; + if (cd.num_args () <= argidx1) + return nullptr; + + const svalue *sval_arg1 = cd.get_arg_svalue (argidx1); + + if (const tree atval_2 = TREE_CHAIN (atval_1)) + { + /* Two arguments. */ + unsigned argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval_2)) - 1; + if (cd.num_args () <= argidx2) + return nullptr; + const svalue *sval_arg2 = cd.get_arg_svalue (argidx2); + /* TODO: ideally we shouldn't need this cast here; + see PR analyzer/110902. */ + return cd.get_manager ()->get_or_create_cast + (size_type_node, + cd.get_manager ()->get_or_create_binop (size_type_node, + MULT_EXPR, + sval_arg1, sval_arg2)); + } + else + /* Single argument. */ + return cd.get_manager ()->get_or_create_cast (size_type_node, sval_arg1); +} + +/* If this call has an LHS, assign a value to it based on attributes + of the function: + - if __attribute__((const)), use a const_fn_result_svalue, + - if __attribute__((malloc)), use a heap-allocated region with + unknown content + - otherwise, use a conjured_svalue. + + If __attribute__((alloc_size), set the dynamic extents on the region + pointed to. */ + +void +call_details::set_any_lhs_with_defaults () const +{ + if (!m_lhs_region) + return; + + const svalue *sval = maybe_get_const_fn_result (*this); + if (!sval) + { + region_model_manager *mgr = get_manager (); + if (lookup_function_attribute ("malloc")) + { + const region *new_reg + = m_model->get_or_create_region_for_heap_alloc (NULL, m_ctxt); + m_model->mark_region_as_unknown (new_reg, NULL); + sval = mgr->get_ptr_svalue (get_lhs_type (), new_reg); + } + else + /* For the common case of functions without __attribute__((const)), + use a conjured value, and purge any prior state involving that + value (in case this is in a loop). */ + sval = get_or_create_conjured_svalue (m_lhs_region); + if (const svalue *size_in_bytes = get_result_size_in_bytes (*this)) + { + const region *reg + = m_model->deref_rvalue (sval, NULL_TREE, m_ctxt, false); + m_model->set_dynamic_extents (reg, size_in_bytes, m_ctxt); + } + } + maybe_set_lhs (sval); +} + /* Return the number of arguments used by the call statement. */ unsigned diff --git a/gcc/analyzer/call-details.h b/gcc/analyzer/call-details.h index 25ea554..24be224 100644 --- a/gcc/analyzer/call-details.h +++ b/gcc/analyzer/call-details.h @@ -41,6 +41,7 @@ public: const region *get_lhs_region () const { return m_lhs_region; } bool maybe_set_lhs (const svalue *result) const; + void set_any_lhs_with_defaults () const; unsigned num_args () const; bool arg_is_pointer_p (unsigned idx) const diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index cfca305..8bc84c8 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -1372,7 +1372,8 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg, const saved_diagnostic &sd) { LOG_SCOPE (get_logger ()); - log ("sd: %qs at SN: %i", sd.m_d->get_kind (), sd.m_snode->m_index); + log ("sd[%i]: %qs at SN: %i", + sd.get_index (), sd.m_d->get_kind (), sd.m_snode->m_index); log ("num dupes: %i", sd.get_num_dupes ()); pretty_printer *pp = global_dc->printer->clone (); diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc index 3e319a0..b9ee2e4 100644 --- a/gcc/analyzer/kf.cc +++ b/gcc/analyzer/kf.cc @@ -40,6 +40,15 @@ along with GCC; see the file COPYING3. If not see namespace ana { +/* class pure_known_function_with_default_return : public known_function. */ + +void +pure_known_function_with_default_return:: +impl_call_pre (const call_details &cd) const +{ + cd.set_any_lhs_with_defaults (); +} + /* Implementations of specific functions. */ /* Handler for "alloca". */ @@ -557,6 +566,8 @@ kf_memset::impl_call_pre (const call_details &cd) const nullptr, cd.get_ctxt ()); model->fill_region (sized_dest_reg, fill_value_u8); + + cd.maybe_set_lhs (dest_sval); } /* A subclass of pending_diagnostic for complaining about 'putenv' @@ -683,6 +694,7 @@ public: ctxt->warn (make_unique<putenv_of_auto_var> (fndecl, reg)); break; } + cd.set_any_lhs_with_defaults (); } }; @@ -1034,12 +1046,13 @@ public: = model->deref_rvalue (dst_ptr, cd.get_arg_tree (0), ctxt); const svalue *content = cd.get_or_create_conjured_svalue (dst_reg); model->set_value (dst_reg, content, ctxt); + cd.set_any_lhs_with_defaults (); } }; /* Handler for "__builtin_stack_restore". */ -class kf_stack_restore : public known_function +class kf_stack_restore : public pure_known_function_with_default_return { public: bool matches_call_types_p (const call_details &) const final override @@ -1052,7 +1065,7 @@ public: /* Handler for "__builtin_stack_save". */ -class kf_stack_save : public known_function +class kf_stack_save : public pure_known_function_with_default_return { public: bool matches_call_types_p (const call_details &) const final override @@ -1175,6 +1188,7 @@ kf_strlen::impl_call_pre (const call_details &cd) const } } /* Otherwise a conjured value. */ + cd.set_any_lhs_with_defaults (); } /* Handler for "strndup" and "__builtin_strndup". */ diff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc index 1c747e1..a5c12f4 100644 --- a/gcc/analyzer/region-model-reachability.cc +++ b/gcc/analyzer/region-model-reachability.cc @@ -184,27 +184,6 @@ reachable_regions::handle_sval (const svalue *sval) } add (pointee, ptr_is_mutable); } - else if (sval->get_type () - && TREE_CODE (sval->get_type ()) == POINTER_TYPE - && sval->get_kind () == SK_CONJURED) - { - /* Also add symbolic regions for pointers, but only for conjured svalues - for the LHS of a stmt. Doing it for more leads to state explosions - on chains of calls to external functions, due to each conjured svalue - potentially being modified at each successive call, recursively. */ - const conjured_svalue *conjured_sval = (const conjured_svalue *)sval; - if (conjured_sval->lhs_value_p ()) - { - const region *pointee - = m_model->get_manager ()->get_symbolic_region (sval); - /* Use const-ness of pointer type to affect mutability. */ - bool ptr_is_mutable = true; - if (TYPE_READONLY (TREE_TYPE (sval->get_type ()))) - ptr_is_mutable = false; - add (pointee, ptr_is_mutable); - } - } - /* Treat all svalues within a compound_svalue as reachable. */ if (const compound_svalue *compound_sval = sval->dyn_cast_compound_svalue ()) diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index e92b3f7..094b7af 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -441,29 +441,6 @@ region_model::canonicalize () { m_store.canonicalize (m_mgr->get_store_manager ()); m_constraints->canonicalize (); - - if (!m_dynamic_extents.is_empty ()) - { - /* Purge any dynamic extents for regions that aren't referenced. - Normally these are eliminated when leaks are detected, but we - can also gain stray heap_allocated_regions that aren't seen - by the leak-detection code. This happens when - region_model::on_call_pre provides a default result for a - function with both attributes "malloc" and "alloc_size" that - also has a known_function implementation. - Purge dynamic extent information for such regions. */ - auto_bitmap referenced_base_region_ids; - get_referenced_base_regions (referenced_base_region_ids); - auto_vec<const region *> purgable_dyn_extents; - for (auto iter : m_dynamic_extents) - { - const region *reg = iter.first; - if (!bitmap_bit_p (referenced_base_region_ids, reg->get_id ())) - purgable_dyn_extents.safe_push (reg); - } - for (auto reg : purgable_dyn_extents) - m_dynamic_extents.remove (reg); - } } /* Return true if this region_model is in canonical form. */ @@ -1304,51 +1281,6 @@ region_model::check_call_args (const call_details &cd) const cd.get_arg_svalue (arg_idx); } -/* Return true if CD is known to be a call to a function with - __attribute__((const)). */ - -static bool -const_fn_p (const call_details &cd) -{ - tree fndecl = cd.get_fndecl_for_call (); - if (!fndecl) - return false; - gcc_assert (DECL_P (fndecl)); - return TREE_READONLY (fndecl); -} - -/* If this CD is known to be a call to a function with - __attribute__((const)), attempt to get a const_fn_result_svalue - based on the arguments, or return NULL otherwise. */ - -static const svalue * -maybe_get_const_fn_result (const call_details &cd) -{ - if (!const_fn_p (cd)) - return NULL; - - unsigned num_args = cd.num_args (); - if (num_args > const_fn_result_svalue::MAX_INPUTS) - /* Too many arguments. */ - return NULL; - - auto_vec<const svalue *> inputs (num_args); - for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++) - { - const svalue *arg_sval = cd.get_arg_svalue (arg_idx); - if (!arg_sval->can_have_associated_state_p ()) - return NULL; - inputs.quick_push (arg_sval); - } - - region_model_manager *mgr = cd.get_manager (); - const svalue *sval - = mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (), - cd.get_fndecl_for_call (), - inputs); - return sval; -} - /* Update this model for an outcome of a call that returns a specific integer constant. If UNMERGEABLE, then make the result unmergeable, e.g. to prevent @@ -1381,7 +1313,9 @@ region_model::update_for_zero_return (const call_details &cd, update_for_int_cst_return (cd, 0, unmergeable); } -/* Update this model for an outcome of a call that returns non-zero. */ +/* Update this model for an outcome of a call that returns non-zero. + Specifically, assign an svalue to the LHS, and add a constraint that + that svalue is non-zero. */ void region_model::update_for_nonzero_return (const call_details &cd) @@ -1390,6 +1324,7 @@ region_model::update_for_nonzero_return (const call_details &cd) return; if (TREE_CODE (cd.get_lhs_type ()) != INTEGER_TYPE) return; + cd.set_any_lhs_with_defaults (); const svalue *zero = m_mgr->get_or_create_int_cst (cd.get_lhs_type (), 0); const svalue *result @@ -1485,48 +1420,6 @@ region_model::get_known_function (enum internal_fn ifn) const return known_fn_mgr->get_internal_fn (ifn); } -/* Look for attribute "alloc_size" on the called function and, if found, - return a symbolic value of type size_type_node for the allocation size - based on the call's parameters. - Otherwise, return null. */ - -static const svalue * -get_result_size_in_bytes (const call_details &cd) -{ - const tree attr = cd.lookup_function_attribute ("alloc_size"); - if (!attr) - return nullptr; - - const tree atval_1 = TREE_VALUE (attr); - if (!atval_1) - return nullptr; - - unsigned argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval_1)) - 1; - if (cd.num_args () <= argidx1) - return nullptr; - - const svalue *sval_arg1 = cd.get_arg_svalue (argidx1); - - if (const tree atval_2 = TREE_CHAIN (atval_1)) - { - /* Two arguments. */ - unsigned argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval_2)) - 1; - if (cd.num_args () <= argidx2) - return nullptr; - const svalue *sval_arg2 = cd.get_arg_svalue (argidx2); - /* TODO: ideally we shouldn't need this cast here; - see PR analyzer/110902. */ - return cd.get_manager ()->get_or_create_cast - (size_type_node, - cd.get_manager ()->get_or_create_binop (size_type_node, - MULT_EXPR, - sval_arg1, sval_arg2)); - } - else - /* Single argument. */ - return cd.get_manager ()->get_or_create_cast (size_type_node, sval_arg1); -} - /* Update this model for the CALL stmt, using CTXT to report any diagnostics - the first half. @@ -1562,40 +1455,6 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) tree callee_fndecl = get_fndecl_for_call (call, ctxt); - /* Some of the cases below update the lhs of the call based on the - return value, but not all. Provide a default value, which may - get overwritten below. */ - if (tree lhs = gimple_call_lhs (call)) - { - const region *lhs_region = get_lvalue (lhs, ctxt); - const svalue *sval = maybe_get_const_fn_result (cd); - if (!sval) - { - if (callee_fndecl - && lookup_attribute ("malloc", DECL_ATTRIBUTES (callee_fndecl))) - { - const region *new_reg - = get_or_create_region_for_heap_alloc (NULL, ctxt); - mark_region_as_unknown (new_reg, NULL); - sval = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg); - } - else - /* For the common case of functions without __attribute__((const)), - use a conjured value, and purge any prior state involving that - value (in case this is in a loop). */ - sval = m_mgr->get_or_create_conjured_svalue (TREE_TYPE (lhs), call, - lhs_region, - conjured_purge (this, - ctxt)); - if (const svalue *size_in_bytes = get_result_size_in_bytes (cd)) - { - const region *reg = deref_rvalue (sval, NULL_TREE, ctxt, false); - set_dynamic_extents (reg, size_in_bytes, ctxt); - } - } - set_value (lhs_region, sval, ctxt); - } - if (gimple_call_internal_p (call)) if (const known_function *kf = get_known_function (gimple_call_internal_fn (call))) @@ -1605,7 +1464,10 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) } if (!callee_fndecl) - return true; /* Unknown side effects. */ + { + cd.set_any_lhs_with_defaults (); + return true; /* Unknown side effects. */ + } if (const known_function *kf = get_known_function (callee_fndecl, cd)) { @@ -1613,6 +1475,8 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) return false; /* No further side effects. */ } + cd.set_any_lhs_with_defaults (); + const int callee_fndecl_flags = flags_from_decl_or_type (callee_fndecl); if (callee_fndecl_flags & (ECF_CONST | ECF_PURE)) return false; /* No side effects. */ diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index 03ad359..c75744f 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -2282,10 +2282,16 @@ public: const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } const extrinsic_state *ext_state = ctxt->get_ext_state (); if (!ext_state) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } return fd_sm->on_socket (cd, m_success, sm_ctxt.get (), *ext_state); } @@ -2329,10 +2335,16 @@ public: const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } const extrinsic_state *ext_state = ctxt->get_ext_state (); if (!ext_state) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } return fd_sm->on_bind (cd, m_success, sm_ctxt.get (), *ext_state); } }; @@ -2374,10 +2386,16 @@ class kf_listen : public known_function const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } const extrinsic_state *ext_state = ctxt->get_ext_state (); if (!ext_state) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } return fd_sm->on_listen (cd, m_success, sm_ctxt.get (), *ext_state); } @@ -2420,10 +2438,16 @@ class kf_accept : public known_function const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } const extrinsic_state *ext_state = ctxt->get_ext_state (); if (!ext_state) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } return fd_sm->on_accept (cd, m_success, sm_ctxt.get (), *ext_state); } @@ -2469,10 +2493,16 @@ public: const fd_state_machine *fd_sm; std::unique_ptr<sm_context> sm_ctxt; if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt)) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } const extrinsic_state *ext_state = ctxt->get_ext_state (); if (!ext_state) - return true; + { + cd.set_any_lhs_with_defaults (); + return true; + } return fd_sm->on_connect (cd, m_success, sm_ctxt.get (), *ext_state); } @@ -2687,6 +2717,7 @@ public: const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg); model->set_value (base_reg, new_sval, cd.get_ctxt ()); } + cd.set_any_lhs_with_defaults (); } }; diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index 0cfe682..0252b39 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -494,7 +494,7 @@ make_fileptr_state_machine (logger *logger) 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 +class kf_stdio_output_fn : public pure_known_function_with_default_return { public: bool matches_call_types_p (const call_details &) const final override @@ -507,7 +507,7 @@ public: /* Handler for "ferror"". */ -class kf_ferror : public known_function +class kf_ferror : public pure_known_function_with_default_return { public: bool matches_call_types_p (const call_details &cd) const final override @@ -521,7 +521,7 @@ public: /* Handler for "fileno"". */ -class kf_fileno : public known_function +class kf_fileno : public pure_known_function_with_default_return { public: bool matches_call_types_p (const call_details &cd) const final override @@ -557,6 +557,7 @@ public: const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg); model->set_value (base_reg, new_sval, cd.get_ctxt ()); } + cd.set_any_lhs_with_defaults (); } }; @@ -592,12 +593,13 @@ public: const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg); model->set_value (base_reg, new_sval, cd.get_ctxt ()); } + cd.set_any_lhs_with_defaults (); } }; /* Handler for "getc"". */ -class kf_getc : public known_function +class kf_getc : public pure_known_function_with_default_return { public: bool matches_call_types_p (const call_details &cd) const final override @@ -605,13 +607,11 @@ public: return (cd.num_args () == 1 && cd.arg_is_pointer_p (0)); } - - /* No side effects. */ }; /* Handler for "getchar"". */ -class kf_getchar : public known_function +class kf_getchar : public pure_known_function_with_default_return { public: bool matches_call_types_p (const call_details &cd) const final override diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc index 72e1b31..f79b2a7 100644 --- a/gcc/analyzer/varargs.cc +++ b/gcc/analyzer/varargs.cc @@ -1007,6 +1007,8 @@ kf_va_arg::impl_call_pre (const call_details &cd) const tree va_list_tree = get_va_list_diag_arg (cd.get_arg_tree (0)); ap_sval = model->check_for_poison (ap_sval, va_list_tree, ap_reg, ctxt); + cd.set_any_lhs_with_defaults (); + if (const region *impl_reg = ap_sval->maybe_get_region ()) { const svalue *old_impl_sval = model->get_store_value (impl_reg, ctxt); diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c index dcffc11..003914e 100644 --- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c @@ -16,7 +16,8 @@ void test_2 (void) free (ptr); /* { dg-warning "allocated buffer size is not a multiple of the pointee's size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc2 } */ - /* { dg-message "allocated 42 bytes and assigned to 'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc2 } */ + /* { dg-message "42 bytes" "note" { target *-*-* } malloc2 } */ + /* { dg-message "'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc2 } */ } void test_3 (void) diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c index d26c267..eb770f7 100644 --- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c @@ -19,7 +19,8 @@ void test_2 (int32_t n) free (ptr); /* { dg-warning "allocated buffer size is not a multiple of the pointee's size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc2 } */ - /* { dg-message "allocated '\[a-z0-9\\*\\(\\)\\s\]*' bytes and assigned to 'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4" "note" { target *-*-* } malloc2 } */ + /* { dg-message "'\[a-z0-9\\*\\(\\)\\s\]*' bytes" "note" { target *-*-* } malloc2 } */ + /* { dg-message "'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4" "note" { target *-*-* } malloc2 } */ } void test_3 (int32_t n) diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c index 6b75307..6751441 100644 --- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c @@ -20,7 +20,8 @@ void test_1 (void) free (id_sequence); /* { dg-warning "allocated buffer size is not a multiple of the pointee's size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc1 } */ - /* { dg-message "allocated 3 bytes and assigned to 'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc1 } */ + /* { dg-message "3 bytes" "note" { target *-*-* } malloc1 } */ + /* { dg-message "'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc1 } */ } void test_2 (void) @@ -29,7 +30,8 @@ void test_2 (void) free (ptr); /* { dg-warning "allocated buffer size is not a multiple of the pointee's size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc2 } */ - /* { dg-message "allocated 14 bytes and assigned to 'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc2 } */ + /* { dg-message "14 bytes" "note" { target *-*-* } malloc2 } */ + /* { dg-message "'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc2 } */ } void test_3 (int32_t n) @@ -38,7 +40,8 @@ void test_3 (int32_t n) free (ptr); /* { dg-warning "allocated buffer size is not a multiple of the pointee's size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc3 } */ - /* { dg-message "allocated '\[a-z0-9\\+\\(\\)\\s\]*' bytes and assigned to 'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc3 } */ + /* { dg-message "'\[a-z0-9\\+\\(\\)\\s\]*' bytes" "note" { target *-*-* } malloc3 } */ + /* { dg-message "'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc3 } */ } void test_4 (int32_t n, int32_t m) diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c index 642e8f5..a56b25b 100644 --- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c @@ -30,7 +30,8 @@ void test_2 (void) free (ptr); /* { dg-warning "allocated buffer size is not a multiple of the pointee's size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc2 } */ - /* { dg-message "allocated \\d+ bytes and assigned to 'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc2 } */ + /* { dg-message "\\d+ bytes" "note" { target *-*-* } malloc2 } */ + /* { dg-message "'int32_t \\*' (\\\{aka '(long )?int \\*'\\\})? here; 'sizeof \\(int32_t (\\\{aka (long )?int\\\})?\\)' is '4'" "note" { target *-*-* } malloc2 } */ } void test_3 (void) @@ -55,5 +56,6 @@ void test_5 (void) free (ptr); /* { dg-warning "allocated buffer size is not a multiple of the pointee's size \\\[CWE-131\\\]" "warning" { target *-*-* } malloc5 } */ - /* { dg-message "allocated 1 bytes and assigned to 'struct base \\*' here; 'sizeof \\(struct base\\)' is '\\d+'" "note" { target *-*-* } malloc5 } */ + /* { dg-message "allocated 1 byte here" "note" { target *-*-* } malloc5 } */ + /* { dg-message "'struct base \\*' here; 'sizeof \\(struct base\\)' is '\\d+'" "note" { target *-*-* } malloc5 } */ } diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-1.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-1.c index 9938ba2..7251665 100644 --- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-1.c @@ -11,12 +11,13 @@ void test_constant_1 (void) /* { dg-begin-multiline-output "" } int32_t *ptr = __builtin_malloc (1); ^~~~~~~~~~~~~~~~~~~~ - 'test_constant_1': event 1 + 'test_constant_1': events 1-2 | | int32_t *ptr = __builtin_malloc (1); | ^~~~~~~~~~~~~~~~~~~~ | | - | (1) allocated 1 bytes and assigned to 'int32_t *' {aka 'int *'} here; 'sizeof (int32_t {aka int})' is '4' + | (1) allocated 1 byte here + | (2) assigned to 'int32_t *' | { dg-end-multiline-output "" } */ @@ -29,12 +30,13 @@ void test_constant_2 (void) /* { dg-begin-multiline-output "" } int32_t *ptr = __builtin_malloc (2); ^~~~~~~~~~~~~~~~~~~~ - 'test_constant_2': event 1 + 'test_constant_2': events 1-2 | | int32_t *ptr = __builtin_malloc (2); | ^~~~~~~~~~~~~~~~~~~~ | | - | (1) allocated 2 bytes and assigned to 'int32_t *' {aka 'int *'} here; 'sizeof (int32_t {aka int})' is '4' + | (1) allocated 2 bytes here + | (2) assigned to 'int32_t *' | { dg-end-multiline-output "" } */ @@ -52,6 +54,6 @@ void test_symbolic (int n) | int32_t *ptr = __builtin_malloc (n * 2); | ^~~~~~~~~~~~~~~~~~~~~~~~ | | - | (1) allocated 'n * 2' bytes and assigned to 'int32_t *' {aka 'int *'} here; 'sizeof (int32_t {aka int})' is '4' + | (1) allocated 'n * 2' bytes and assigned to 'int32_t *' | { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-2.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-2.c index 9e1269c..7cadbb7 100644 --- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-2.c +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-2.c @@ -11,12 +11,13 @@ void test_constant_1 (void) /* { dg-begin-multiline-output "" } int32_t *ptr = __builtin_alloca (1); ^~~~~~~~~~~~~~~~~~~~ - 'test_constant_1': event 1 + 'test_constant_1': events 1-2 | | int32_t *ptr = __builtin_alloca (1); | ^~~~~~~~~~~~~~~~~~~~ | | - | (1) allocated 1 bytes and assigned to 'int32_t *' {aka 'int *'} here; 'sizeof (int32_t {aka int})' is '4' + | (1) allocated 1 byte here + | (2) assigned to 'int32_t *' | { dg-end-multiline-output "" } */ @@ -28,12 +29,13 @@ void test_constant_2 (void) /* { dg-begin-multiline-output "" } int32_t *ptr = __builtin_alloca (2); ^~~~~~~~~~~~~~~~~~~~ - 'test_constant_2': event 1 + 'test_constant_2': events 1-2 | | int32_t *ptr = __builtin_alloca (2); | ^~~~~~~~~~~~~~~~~~~~ | | - | (1) allocated 2 bytes and assigned to 'int32_t *' {aka 'int *'} here; 'sizeof (int32_t {aka int})' is '4' + | (1) allocated 2 bytes here + | (2) assigned to 'int32_t *' | { dg-end-multiline-output "" } */ @@ -45,12 +47,13 @@ void test_symbolic (int n) /* { dg-begin-multiline-output "" } int32_t *ptr = __builtin_alloca (n * 2); ^~~~~~~~~~~~~~~~~~~~~~~~ - 'test_symbolic': event 1 + 'test_symbolic': events 1-2 | | int32_t *ptr = __builtin_alloca (n * 2); | ^~~~~~~~~~~~~~~~~~~~~~~~ | | - | (1) allocated 'n * 2' bytes and assigned to 'int32_t *' {aka 'int *'} here; 'sizeof (int32_t {aka int})' is '4' + | (1) allocated 'n * 2' bytes here + | (2) assigned to 'int32_t *' | { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-3.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-3.c index 71790d9..b3de582 100644 --- a/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-3.c +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-3.c @@ -15,12 +15,13 @@ void test_constant_99 (void) /* { dg-begin-multiline-output "" } int32_t *ptr = alloca (99); ^~~~~~ - 'test_constant_99': event 1 + 'test_constant_99': events 1-2 | | int32_t *ptr = alloca (99); | ^~~~~~ | | - | (1) allocated 99 bytes and assigned to 'int32_t *' {aka 'int *'} here; 'sizeof (int32_t {aka int})' is '4' + | (1) allocated 99 bytes here + | (2) assigned to 'int32_t *' {aka '{re:long :re?}int *'} here; 'sizeof (int32_t {aka {re:long :re?}int})' is '4' | { dg-end-multiline-output "" } */ @@ -32,11 +33,12 @@ void test_symbolic (int n) /* { dg-begin-multiline-output "" } int32_t *ptr = alloca (n * 2); ^~~~~~ - 'test_symbolic': event 1 + 'test_symbolic': events 1-2 | | int32_t *ptr = alloca (n * 2); | ^~~~~~ | | - | (1) allocated 'n * 2' bytes and assigned to 'int32_t *' {aka 'int *'} here; 'sizeof (int32_t {aka int})' is '4' + | (1) allocated 'n * 2' bytes here + | (2) assigned to 'int32_t *' {aka '{re:long :re?}int *'} here; 'sizeof (int32_t {aka {re:long :re?}int})' is '4' | { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/memset-1.c b/gcc/testsuite/gcc.dg/analyzer/memset-1.c index 94c5a1b..75aef53 100644 --- a/gcc/testsuite/gcc.dg/analyzer/memset-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/memset-1.c @@ -6,8 +6,9 @@ void test_1 (void) { char buf[256]; - memset (buf, 0, 256); + void *p = memset (buf, 0, 256); __analyzer_eval (buf[42] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (p == buf); /* { dg-warning "TRUE" } */ } /* As above, but with __builtin_memset. */ diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c index 57bccf4..02dba7a 100644 --- a/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c @@ -86,8 +86,11 @@ class copy_across_boundary_fn : public known_function if (tree cst = num_bytes_sval->maybe_get_constant ()) if (zerop (cst)) - /* No-op. */ - return; + { + /* No-op. */ + model->update_for_zero_return (cd, true); + return; + } const region *sized_src_reg = mgr->get_sized_region (src_reg, NULL_TREE, diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c index de887db..806cb90 100644 --- a/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c @@ -147,8 +147,11 @@ public: if (tree cst = num_bytes_sval->maybe_get_constant ()) if (zerop (cst)) - /* No-op. */ - return; + { + /* No-op. */ + cd.set_any_lhs_with_defaults (); + return; + } const region *sized_src_reg = mgr->get_sized_region (src_reg, NULL_TREE, |