https://gcc.gnu.org/g:fa2e03effa5251a6f7c8b79a8e3be81c90fb5e4f

commit r15-9855-gfa2e03effa5251a6f7c8b79a8e3be81c90fb5e4f
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Mon Jun 23 16:08:34 2025 +0200

    tailc: Allow musttail tail calls with -fsanitize=address [PR120608]
    
    These testcases show another problem with -fsanitize=address
    vs. musttail tail calls.  In particular, there can be
      .ASAN_MARK (POISON, &a, 4);
    etc. calls after a tail call and those just prevent the tailc pass
    to mark the musttail calls as [tail call].
    Normally, the sanopt pass (which comes after tailc) will optimize those
    away, the optimization is if there are no .ASAN_CHECK calls or normal
    function calls dominated by those .ASAN_MARK (POSION, ...) calls, the
    poison is not needed, because in the epilog sequence (the one dealt with
    in the patch posted earlier today) all the stack slots are unpoisoned anyway
    (or poisoned for use-after-return).
    Unlike __builtin_tsan_exit_function, .ASAN_MARK is not a real function
    and is always expanded inline, so can be never tail called successfully,
    so the patch just ignores those for the cfun->has_musttail && diag_musttail
    cases.  If there is a non-musttail call, it will fail worst case during
    expansion because there is the epilog asan sequence.
    
    2025-06-12  Jakub Jelinek  <ja...@redhat.com>
    
            PR middle-end/120608
            * tree-tailcall.cc (empty_eh_cleanup): Ignore .ASAN_MARK (POISON)
            internal calls for the cfun->has_musttail case and diag_musttail.
            (find_tail_calls): Likewise.
    
            * c-c++-common/asan/pr120608-1.c: New test.
            * c-c++-common/asan/pr120608-2.c: New test.
    
    (cherry picked from commit 35a26f2ec55d20d524464c33b68b23328a7f6bbe)

Diff:
---
 gcc/testsuite/c-c++-common/asan/pr120608-1.c | 43 ++++++++++++++++++++++++++++
 gcc/testsuite/c-c++-common/asan/pr120608-2.c | 39 +++++++++++++++++++++++++
 gcc/tree-tailcall.cc                         | 16 +++++++++++
 3 files changed, 98 insertions(+)

diff --git a/gcc/testsuite/c-c++-common/asan/pr120608-1.c 
b/gcc/testsuite/c-c++-common/asan/pr120608-1.c
new file mode 100644
index 000000000000..114c42db6f83
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asan/pr120608-1.c
@@ -0,0 +1,43 @@
+/* PR middle-end/120608 */
+/* { dg-do run { target musttail } } */
+/* { dg-options "-O2 -fsanitize=address" } */
+
+__attribute__((noipa)) void
+foo (int *x, int *y, int *z)
+{
+  ++x[0];
+  ++y[0];
+  ++z[0];
+}
+
+__attribute__((noipa)) void
+bar (int *x, int *y, int *z)
+{
+  if (x || y || z)
+    __builtin_abort ();
+}
+
+__attribute__((noipa)) void
+baz (int *x, int *y, int *z)
+{
+  (void) x; (void) y; (void) z;
+  int a = 42, b = -42, c = 0;
+  foo (&a, &b, &c);
+  [[gnu::musttail]] return bar (0, 0, 0);
+}
+
+__attribute__((noipa)) void
+qux (int *x, int *y, int *z)
+{
+  (void) x; (void) y; (void) z;
+  int a = 42, b = -42, c = 0;
+  foo (&a, &b, &c);
+  [[gnu::musttail]] return bar (0, 0, 0);
+}
+
+int
+main ()
+{
+  baz (0, 0, 0);
+  qux (0, 0, 0);
+}
diff --git a/gcc/testsuite/c-c++-common/asan/pr120608-2.c 
b/gcc/testsuite/c-c++-common/asan/pr120608-2.c
new file mode 100644
index 000000000000..251ff3a1a074
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asan/pr120608-2.c
@@ -0,0 +1,39 @@
+/* PR middle-end/120608 */
+/* { dg-do run { target musttail } } */
+/* { dg-options "-O2 -fsanitize=address" } */
+/* { dg-set-target-env-var ASAN_OPTIONS "detect_stack_use_after_return=1" } */
+/* { dg-shouldfail "asan" } */
+
+__attribute__((noipa)) void
+foo (int *x, int *y, int *z)
+{
+  ++x[0];
+  ++y[0];
+  ++z[0];
+}
+
+__attribute__((noipa)) void
+bar (int *x, int *y, int *z)
+{
+  volatile int a = x[0] + y[0] + z[0];
+}
+
+__attribute__((noipa)) void
+baz (int *x, int *y, int *z)
+{
+  (void) x; (void) y; (void) z;
+  int a = 42, b = -42, c = 0;
+  foo (&a, &b, &c);
+  [[gnu::musttail]] return bar (&a, &b, &c);   /* { dg-warning "address of 
automatic variable 'a' passed to 'musttail' call argument" } */
+}                                              /* { dg-warning "address of 
automatic variable 'b' passed to 'musttail' call argument" "" { target *-*-* } 
.-1 } */
+                                               /* { dg-warning "address of 
automatic variable 'c' passed to 'musttail' call argument" "" { target *-*-* } 
.-2 } */
+
+int
+main ()
+{
+  baz (0, 0, 0);
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-return on 
address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size .*" }
+// { dg-output ".*'a' \\(line 25\\) <== Memory access at offset \[0-9\]* is 
inside this variable.*" }
diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc
index f593363dae43..8ce8bcf0e20b 100644
--- a/gcc/tree-tailcall.cc
+++ b/gcc/tree-tailcall.cc
@@ -528,6 +528,10 @@ empty_eh_cleanup (basic_block bb, int 
*eh_has_tsan_func_exit, int cnt)
          *eh_has_tsan_func_exit = 1;
          continue;
        }
+      if (eh_has_tsan_func_exit
+         && sanitize_flags_p (SANITIZE_ADDRESS)
+         && asan_mark_p (g, ASAN_MARK_POISON))
+       continue;
       if (is_gimple_resx (g) && stmt_can_throw_external (cfun, g))
        return true;
       return false;
@@ -619,6 +623,12 @@ find_tail_calls (basic_block bb, struct tailcall **ret, 
bool only_musttail,
            continue;
        }
 
+      if (cfun->has_musttail
+         && sanitize_flags_p (SANITIZE_ADDRESS)
+         && asan_mark_p (stmt, ASAN_MARK_POISON)
+         && diag_musttail)
+       continue;
+
       if (!last_stmt)
        last_stmt = stmt;
       /* Check for a call.  */
@@ -996,6 +1006,12 @@ find_tail_calls (basic_block bb, struct tailcall **ret, 
bool only_musttail,
          continue;
        }
 
+      if (cfun->has_musttail
+         && sanitize_flags_p (SANITIZE_ADDRESS)
+         && asan_mark_p (stmt, ASAN_MARK_POISON)
+         && diag_musttail)
+       continue;
+
       if (gimple_code (stmt) != GIMPLE_ASSIGN)
        {
          maybe_error_musttail (call, _("unhandled code after call"),

Reply via email to