aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2023-08-09 16:17:04 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2023-08-09 16:17:04 -0400
commit73da34a538ddc2ad17e80e93b64b526f704693d2 (patch)
treedb8de59479d798a1c3ba42788cb36617c53470f3
parente5fe7f2fd69d5a950d8c1e2a068669bda376b2d4 (diff)
downloadgcc-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>
-rw-r--r--gcc/analyzer/analyzer.h10
-rw-r--r--gcc/analyzer/call-details.cc129
-rw-r--r--gcc/analyzer/call-details.h1
-rw-r--r--gcc/analyzer/diagnostic-manager.cc3
-rw-r--r--gcc/analyzer/kf.cc18
-rw-r--r--gcc/analyzer/region-model-reachability.cc21
-rw-r--r--gcc/analyzer/region-model.cc156
-rw-r--r--gcc/analyzer/sm-fd.cc51
-rw-r--r--gcc/analyzer/sm-file.cc14
-rw-r--r--gcc/analyzer/varargs.cc2
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c3
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c3
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c9
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c6
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-1.c12
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-2.c15
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/allocation-size-multiline-3.c10
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/memset-1.c3
-rw-r--r--gcc/testsuite/gcc.dg/plugin/analyzer_kernel_plugin.c7
-rw-r--r--gcc/testsuite/gcc.dg/plugin/analyzer_known_fns_plugin.c7
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,