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)
