diff options
author | David Malcolm <dmalcolm@redhat.com> | 2023-01-11 16:27:06 -0500 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2023-01-11 16:27:06 -0500 |
commit | 688fc162b76dc6747a30fcfd470f4770da0f4924 (patch) | |
tree | ff0f5ea0fcfdc97d7508791b82793a27de1128f9 /gcc/analyzer/region-model.cc | |
parent | 23b4ce18379cd336d99d7c71701be28118905b57 (diff) | |
download | gcc-688fc162b76dc6747a30fcfd470f4770da0f4924.zip gcc-688fc162b76dc6747a30fcfd470f4770da0f4924.tar.gz gcc-688fc162b76dc6747a30fcfd470f4770da0f4924.tar.bz2 |
analyzer: fix leak false positives on "*UNKNOWN = PTR;" [PR108252]
PR analyzer/108252 reports a false positive from -Wanalyzer-malloc-leak on
code like this:
*ptr_ptr = strdup(EXPR);
where ptr_ptr is an UNKNOWN_VALUE.
When we handle:
*UNKNOWN = PTR;
store::set_value normally marks *PTR as having escaped, and this means
we don't report PTR as leaking when the last usage of PTR is lost.
However this only works for cases where PTR is a region_svalue.
In the example in the bug, it's a conjured_svalue, rather than a
region_svalue. A similar problem can arise for FDs, which aren't
pointers.
This patch fixes the bug by updating store::set_value to mark any
values stored via *UNKNOWN = VAL as not leaking.
Additionally, sm-malloc.cc's known_allocator_p hardcodes strdup and
strndup as allocators (and thus transitioning their result to
"unchecked"), but we don't implement known_functions for these, leading
to the LHS being a CONJURED_SVALUE, rather than a region_svalue to a
heap-allocated region. A similar issue happens with functions marked
with __attribute__((malloc)). As part of a "belt and braces" fix, the
patch also updates the handling of these functions, so that they use
heap-allocated regions.
gcc/analyzer/ChangeLog:
PR analyzer/108252
* kf.cc (class kf_strdup): New.
(class kf_strndup): New.
(register_known_functions): Register them.
* region-model.cc (region_model::on_call_pre): Use
&HEAP_ALLOCATED_REGION for the default result of an external
function with the "malloc" attribute, rather than CONJURED_SVALUE.
(region_model::get_or_create_region_for_heap_alloc): Allow
"size_in_bytes" to be NULL.
* store.cc (store::set_value): When handling *UNKNOWN = VAL,
mark VAL as "maybe bound".
gcc/testsuite/ChangeLog:
PR analyzer/108252
* gcc.dg/analyzer/attr-malloc-pr108252.c: New test.
* gcc.dg/analyzer/fd-leak-pr108252.c: New test.
* gcc.dg/analyzer/flex-with-call-summaries.c: Remove xfail from
warning false +ve directives.
* gcc.dg/analyzer/pr103217-2.c: Add -Wno-analyzer-too-complex.
* gcc.dg/analyzer/pr103217-3.c: Likewise.
* gcc.dg/analyzer/strdup-pr108252.c: New test.
* gcc.dg/analyzer/strndup-pr108252.c: New test.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'gcc/analyzer/region-model.cc')
-rw-r--r-- | gcc/analyzer/region-model.cc | 32 |
1 files changed, 22 insertions, 10 deletions
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 5506440..2e59fba 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -1441,6 +1441,8 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) if (ctxt) check_call_args (cd); + 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. */ @@ -1450,13 +1452,22 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) const svalue *sval = maybe_get_const_fn_result (cd); if (!sval) { - /* 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 (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)); } set_value (lhs_region, sval, ctxt); } @@ -1469,7 +1480,7 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt) return false; } - if (tree callee_fndecl = get_fndecl_for_call (call, ctxt)) + if (callee_fndecl) { int callee_fndecl_flags = flags_from_decl_or_type (callee_fndecl); @@ -4909,8 +4920,9 @@ region_model::get_or_create_region_for_heap_alloc (const svalue *size_in_bytes, get_referenced_base_regions (base_regs_in_use); const region *reg = m_mgr->get_or_create_region_for_heap_alloc (base_regs_in_use); - if (compat_types_p (size_in_bytes->get_type (), size_type_node)) - set_dynamic_extents (reg, size_in_bytes, ctxt); + if (size_in_bytes) + if (compat_types_p (size_in_bytes->get_type (), size_type_node)) + set_dynamic_extents (reg, size_in_bytes, ctxt); return reg; } |