On aarch64 -mbranch-protection=pac-ret reuses the dwarf
opcode for window_save to mean "toggle the return address
mangle state", but in the dwarf2cfi internal logic the
state was not properly recorded so the remember/restore
logic was broken.

This can cause the unwinder not to authenticate return
addresses that were signed (or vice versa) which means
a runtime crash on a pauth enabled system.

Currently only aarch64 pac-ret uses REG_CFA_TOGGLE_RA_MANGLE.

gcc/ChangeLog:

2020-04-XX  Szabolcs Nagy  <szabolcs.n...@arm.com>

        PR target/94515
        * dwarf2cfi.c (dwarf2out_frame_debug): Record RA
        mangle state in cur_row->window_save.

gcc/testsuite/ChangeLog:

2020-04-XX  Szabolcs Nagy  <szabolcs.n...@arm.com>

        PR target/94515
        * g++.target/aarch64/pr94515.C: New test.
---
 gcc/dwarf2cfi.c                            |  3 ++
 gcc/testsuite/g++.target/aarch64/pr94515.C | 43 ++++++++++++++++++++++
 2 files changed, 46 insertions(+)
 create mode 100644 gcc/testsuite/g++.target/aarch64/pr94515.C

diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c
index 229fbfacc30..22989a6c2fb 100644
--- a/gcc/dwarf2cfi.c
+++ b/gcc/dwarf2cfi.c
@@ -2145,6 +2145,9 @@ dwarf2out_frame_debug (rtx_insn *insn)
       case REG_CFA_TOGGLE_RA_MANGLE:
        /* This uses the same DWARF opcode as the next operation.  */
        dwarf2out_frame_debug_cfa_window_save (true);
+       /* Keep track of RA mangle state by toggling the window_save bit.
+          This is needed so .cfi_window_save is emitted correctly.  */
+       cur_row->window_save = !cur_row->window_save;
        handled_one = true;
        break;
 
diff --git a/gcc/testsuite/g++.target/aarch64/pr94515.C 
b/gcc/testsuite/g++.target/aarch64/pr94515.C
new file mode 100644
index 00000000000..7707c773a72
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/pr94515.C
@@ -0,0 +1,43 @@
+/* PR target/94515. Check .cfi_window_save with multiple return paths.  */
+/* { dg-do run } */
+/* { dg-additional-options "-O2 -mbranch-protection=pac-ret --save-temps" } */
+
+volatile int zero = 0;
+
+__attribute__((noinline, target("branch-protection=none")))
+void unwind (void)
+{
+  if (zero == 0)
+    throw 42;
+}
+
+__attribute__((noinline, noipa, target("branch-protection=pac-ret")))
+int test (int z)
+{
+  if (z) {
+    asm volatile ("":::"x20","x21");
+    unwind ();
+    return 1;
+  } else {
+    unwind ();
+    return 2;
+  }
+}
+
+__attribute__((target("branch-protection=none")))
+int main ()
+{
+  try {
+    test (zero);
+    __builtin_abort ();
+  } catch (...) {
+    return 0;
+  }
+  __builtin_abort ();
+}
+
+/* This check only works if there are two return paths in test and
+   cfi_window_save is used for both instead of cfi_remember_state
+   plus cfi_restore_state.  This is currently the case with -O2.  */
+
+/* { dg-final { scan-assembler-times {\t\.cfi_window_save\n} 4 } } */
-- 
2.17.1

Reply via email to