> Am 14.12.2024 um 09:20 schrieb Jakub Jelinek <ja...@redhat.com>:
>
> Hi!
>
> The following testcase ICEs because of a bug in matching_alloc_calls_p.
> The loop was apparently meant to be walking the two attribute chains
> in lock-step, but doesn't really do that. If the first lookup_attribute
> returns non-NULL, the second one is not done, so rmats in that case can
> be some random unrelated attribute rather than "malloc" attribute; the
> body assumes even rmats if non-NULL is "malloc" attribute and relies
> on its argument to be a "malloc" argument and if it is some other
> attribute with incompatible attribute, it just crashes.
>
> Now, fixing that in the obvious way, instead of doing
> (amats = lookup_attribute ("malloc", amats))
> || (rmats = lookup_attribute ("malloc", rmats))
> in the condition do
> ((amats = lookup_attribute ("malloc", amats)),
> (rmats = lookup_attribute ("malloc", rmats)),
> (amats || rmats))
> fixes the testcase but regresses Wmismatched-dealloc-{2,3}.c tests.
> The problem is that walking the attribute lists in a lock-step is obviously
> a very bad idea, there is no requirement that the same deallocators are
> present in the same order on both decls, e.g. there could be an extra malloc
> attribute without argument in just one of the lists, or the order of say
> free/realloc could be swapped, etc. We don't generally document nor enforce
> any particular ordering of attributes (even when for some attributes we just
> handle the first one rather than all).
>
> So, this patch instead simply splits it into two loops, the first one walks
> alloc_decl attributes, the second one walks dealloc_decl attributes.
> If the malloc attribute argument is a built-in, that doesn't change
> anything, and otherwise we have the chance to populate the whole
> common_deallocs hash_set in the first loop and then can check it in the
> second one (and don't need to use more expensive add method on it, can just
> check contains there). Not to mention that it also fixes the case when
> the function would incorrectly return true if there wasn't a common
> deallocator between the two, but dealloc_decl had 2 malloc attributes with
> the same deallocator.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
Ok
Thanks,
Richard
> 2024-12-14 Jakub Jelinek <ja...@redhat.com>
>
> PR middle-end/118024
> * gimple-ssa-warn-access.cc (matching_alloc_calls_p): Walk malloc
> attributes of alloc_decl and dealloc_decl in separate loops rather
> than in lock-step. Use common_deallocs.contains rather than
> common_deallocs.add in the second loop.
>
> * gcc.dg/pr118024.c: New test.
>
> --- gcc/gimple-ssa-warn-access.cc.jj 2024-12-07 11:35:49.467439817 +0100
> +++ gcc/gimple-ssa-warn-access.cc 2024-12-13 13:03:13.626700818 +0100
> @@ -1935,52 +1935,49 @@ matching_alloc_calls_p (tree alloc_decl,
> headers.
> With AMATS set to the Allocator's Malloc ATtributes,
> and RMATS set to Reallocator's Malloc ATtributes... */
> - for (tree amats = DECL_ATTRIBUTES (alloc_decl),
> - rmats = DECL_ATTRIBUTES (dealloc_decl);
> - (amats = lookup_attribute ("malloc", amats))
> - || (rmats = lookup_attribute ("malloc", rmats));
> - amats = amats ? TREE_CHAIN (amats) : NULL_TREE,
> - rmats = rmats ? TREE_CHAIN (rmats) : NULL_TREE)
> - {
> - if (tree args = amats ? TREE_VALUE (amats) : NULL_TREE)
> - if (tree adealloc = TREE_VALUE (args))
> - {
> - if (DECL_P (adealloc)
> - && fndecl_built_in_p (adealloc, BUILT_IN_NORMAL))
> - {
> - built_in_function fncode = DECL_FUNCTION_CODE (adealloc);
> - if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
> - {
> - if (realloc_kind == alloc_kind_t::builtin)
> - return true;
> - alloc_dealloc_kind = alloc_kind_t::builtin;
> - }
> - continue;
> - }
> + for (tree amats = DECL_ATTRIBUTES (alloc_decl);
> + (amats = lookup_attribute ("malloc", amats));
> + amats = amats ? TREE_CHAIN (amats) : NULL_TREE)
> + if (tree args = amats ? TREE_VALUE (amats) : NULL_TREE)
> + if (tree adealloc = TREE_VALUE (args))
> + {
> + if (DECL_P (adealloc)
> + && fndecl_built_in_p (adealloc, BUILT_IN_NORMAL))
> + {
> + built_in_function fncode = DECL_FUNCTION_CODE (adealloc);
> + if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
> + {
> + if (realloc_kind == alloc_kind_t::builtin)
> + return true;
> + alloc_dealloc_kind = alloc_kind_t::builtin;
> + }
> + continue;
> + }
>
> - common_deallocs.add (adealloc);
> - }
> + common_deallocs.add (adealloc);
> + }
> + for (tree rmats = DECL_ATTRIBUTES (dealloc_decl);
> + (rmats = lookup_attribute ("malloc", rmats));
> + rmats = rmats ? TREE_CHAIN (rmats) : NULL_TREE)
> + if (tree args = rmats ? TREE_VALUE (rmats) : NULL_TREE)
> + if (tree ddealloc = TREE_VALUE (args))
> + {
> + if (DECL_P (ddealloc)
> + && fndecl_built_in_p (ddealloc, BUILT_IN_NORMAL))
> + {
> + built_in_function fncode = DECL_FUNCTION_CODE (ddealloc);
> + if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
> + {
> + if (alloc_dealloc_kind == alloc_kind_t::builtin)
> + return true;
> + realloc_dealloc_kind = alloc_kind_t::builtin;
> + }
> + continue;
> + }
>
> - if (tree args = rmats ? TREE_VALUE (rmats) : NULL_TREE)
> - if (tree ddealloc = TREE_VALUE (args))
> - {
> - if (DECL_P (ddealloc)
> - && fndecl_built_in_p (ddealloc, BUILT_IN_NORMAL))
> - {
> - built_in_function fncode = DECL_FUNCTION_CODE (ddealloc);
> - if (fncode == BUILT_IN_FREE || fncode == BUILT_IN_REALLOC)
> - {
> - if (alloc_dealloc_kind == alloc_kind_t::builtin)
> - return true;
> - realloc_dealloc_kind = alloc_kind_t::builtin;
> - }
> - continue;
> - }
> -
> - if (common_deallocs.add (ddealloc))
> - return true;
> - }
> - }
> + if (common_deallocs.contains (ddealloc))
> + return true;
> + }
>
> /* Succeed only if ALLOC_DECL and the reallocator DEALLOC_DECL share
> a built-in deallocator. */
> --- gcc/testsuite/gcc.dg/pr118024.c.jj 2024-12-13 13:58:41.022588554 +0100
> +++ gcc/testsuite/gcc.dg/pr118024.c 2024-12-13 13:58:17.189925679 +0100
> @@ -0,0 +1,15 @@
> +/* PR middle-end/118024 */
> +/* { dg-do compile } */
> +/* { dg-options "-fstrub=all" } */
> +/* { dg-require-effective-target strub } */
> +
> +void *realloc (void *, __SIZE_TYPE__);
> +void *reallocarray (void *);
> +void *reallocarray (void *) __attribute__((__malloc__(reallocarray)));
> +
> +void *
> +foo (void)
> +{
> + char *buf = reallocarray (0);
> + return realloc (buf, 1);
> +}
>
> Jakub
>