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?

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

Reply via email to