]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
tailc: Allow musttail tail calls with -fsanitize=address [PR120608]
authorJakub Jelinek <jakub@redhat.com>
Mon, 23 Jun 2025 14:08:34 +0000 (16:08 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Mon, 23 Jun 2025 14:08:34 +0000 (16:08 +0200)
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  <jakub@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.

gcc/testsuite/c-c++-common/asan/pr120608-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/asan/pr120608-2.c [new file with mode: 0644]
gcc/tree-tailcall.cc

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 (file)
index 0000000..114c42d
--- /dev/null
@@ -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 (file)
index 0000000..251ff3a
--- /dev/null
@@ -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.*" }
index 10e88d9c8292b18f2d9d8820ade4cfdbf4059503..d6d2830221130d0b30ca9dface2008b6c834710a 100644 (file)
@@ -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.  */
@@ -995,6 +1005,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"),