https://gcc.gnu.org/g:0ce6f1d493e2511c745621fb14b93fb4e54c0b06

commit r16-8449-g0ce6f1d493e2511c745621fb14b93fb4e54c0b06
Author: Jakub Jelinek <[email protected]>
Date:   Fri Apr 3 20:55:26 2026 +0200

    strlen: Fix up memcpy/strcpy/strcat handling [PR124754]
    
    The following testcase ICEs because it tries to use a freed strinfo.
    strinfo uses refcounting and vectors of pointers to those are either shared
    in between bbs or unshared when some later bb needs to modify something.
    
    The ICE is due to the second memcpy handling.  Since r10-5451 it first
    calls get_stridx on dst (&a), then get_strinfo with the returned index and
    then get_stridx on src (&a + 24).  &a has been referenced earlier and
    the pass knows some details on its string length (let's ignore that the
    testcase invokes UB for now, I think it can happen even on valid code,
    just haven't managed to adjust the testcase, but supposedly the unreduced
    original is valid), so the strinfo is shared with other bbs.
    WHen get_stridx is called for &a + 24, it sees it doesn't know anything
    about that yet and wants to create a strinfo for it.  But related strinfos
    are chained, so &a's next should be &a + 24 in this testcase, that means
    we need to unshare the &a strinfo so that we can modify it while keeping
    the one entry used by earlier bbs unmodified.  The problem is that
    handle_builtin_memcpy called get_strinfo before this, so it got hands on
    the old still not unshared strinfo for &a and because it isn't refreshed
    after the get_stridx call by another get_strinfo call, we try to unshare
    it again and break the refcounting.
    
    So, basically for calls which call get_stridx twice, we need top make sure
    we do both get_stridx calls before the first get_strinfo call, or repeat
    get_strinfo after the second get_stridx call.
    
    In handle_builtin_memcpy it used to be right and got broken by r10-5451,
    but I'm in strcpy/strcat handling I've screwed it up since the pass was
    added.
    
    Fixed thusly.
    
    2026-04-03  Jakub Jelinek  <[email protected]>
    
            PR tree-optimization/124754
            * tree-ssa-strlen.cc (strlen_pass::handle_builtin_strcpy): Make
            sure both get_stridx calls are done before first get_strinfo call.
            (strlen_pass::handle_builtin_memcpy): Likewise.
            (strlen_pass::handle_builtin_strcat): Likewise.
    
            * g++.dg/tree-ssa/strlenopt-3.C: New test.
    
    Reviewed-by: Jeffrey Law <[email protected]>

Diff:
---
 gcc/testsuite/g++.dg/tree-ssa/strlenopt-3.C | 21 +++++++++++++++++++++
 gcc/tree-ssa-strlen.cc                      | 10 +++++-----
 2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/gcc/testsuite/g++.dg/tree-ssa/strlenopt-3.C 
b/gcc/testsuite/g++.dg/tree-ssa/strlenopt-3.C
new file mode 100644
index 000000000000..92d20d71fa25
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/strlenopt-3.C
@@ -0,0 +1,21 @@
+// PR tree-optimization/124754
+// { dg-do compile }
+// { dg-options "-O2 -Wno-stringop-overflow -Wno-stringop-overread" }
+
+char a[3], *c, *d;
+
+void
+foo ()
+{
+  try
+    {
+      __builtin_memcpy (&a, "ABCDEFGHIJKLMNOPQRSTUVWX", 24);
+      c = new char ('@');
+      d = new char ('@');
+      __builtin_memcpy (&a, &a + 8, 1);
+    }
+  catch (...)
+    {
+      __builtin_abort ();
+    }
+}
diff --git a/gcc/tree-ssa-strlen.cc b/gcc/tree-ssa-strlen.cc
index 2781a74cc864..c698998f5d87 100644
--- a/gcc/tree-ssa-strlen.cc
+++ b/gcc/tree-ssa-strlen.cc
@@ -2523,11 +2523,11 @@ strlen_pass::handle_builtin_strcpy (built_in_function 
bcode)
   dst = gimple_call_arg (stmt, 0);
   lhs = gimple_call_lhs (stmt);
   idx = get_stridx (src, stmt);
+  didx = get_stridx (dst, stmt);
   si = NULL;
   if (idx > 0)
     si = get_strinfo (idx);
 
-  didx = get_stridx (dst, stmt);
   olddsi = NULL;
   oldlen = NULL_TREE;
   if (didx > 0)
@@ -3341,11 +3341,12 @@ strlen_pass::handle_builtin_memcpy (built_in_function 
bcode)
   tree dst = gimple_call_arg (stmt, 0);
 
   int didx = get_stridx (dst, stmt);
+  if (didx < 0)
+    return;
+  int idx = get_stridx (src, stmt);
   strinfo *olddsi = NULL;
   if (didx > 0)
     olddsi = get_strinfo (didx);
-  else if (didx < 0)
-    return;
 
   if (olddsi != NULL
       && !integer_zerop (len))
@@ -3355,7 +3356,6 @@ strlen_pass::handle_builtin_memcpy (built_in_function 
bcode)
        adjust_last_stmt (olddsi, stmt, false);
     }
 
-  int idx = get_stridx (src, stmt);
   if (idx == 0)
     return;
 
@@ -3533,6 +3533,7 @@ strlen_pass::handle_builtin_strcat (built_in_function 
bcode)
   didx = get_stridx (dst, stmt);
   if (didx < 0)
     return;
+  idx = get_stridx (src, stmt);
 
   dsi = NULL;
   if (didx > 0)
@@ -3540,7 +3541,6 @@ strlen_pass::handle_builtin_strcat (built_in_function 
bcode)
 
   srclen = NULL_TREE;
   si = NULL;
-  idx = get_stridx (src, stmt);
   if (idx < 0)
     srclen = build_int_cst (size_type_node, ~idx);
   else if (idx > 0)

Reply via email to