]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
AArch64 pauth: Indicate unmasked addresses in backtrace
authorAlan Hayward <alan.hayward@arm.com>
Wed, 7 Aug 2019 08:47:57 +0000 (09:47 +0100)
committerAlan Hayward <alan.hayward@arm.com>
Wed, 7 Aug 2019 12:34:12 +0000 (13:34 +0100)
Armv8.3-a Pointer Authentication causes the function return address to be
obfuscated on entry to some functions. GDB must unmask the link register in
order to produce a backtrace.

The following patch adds markers of [PAC] to the bracktrace, to indicate
which addresses needed unmasking.  This includes the backtrace when using MI.

For example, consider the following backtrace:

(gdb) bt
0  0x0000000000400490 in puts@plt ()
1  0x00000000004005dc in foo ("hello") at cbreak-lib.c:6
2  0x0000000000400604 [PAC] in bar () at cbreak-lib.c:12
3  0x0000000000400620 [PAC] in main2 () at cbreak.c:17
4  0x00000000004005b4 in main () at cbreak-3.c:10

The functions in cbreak-lib use pointer auth, which masks the return address
to the previous function, causing the addresses of bar (in the library) and main2
(in the main binary) to require unmasking in order to unwind the backtrace.

An extra bool is added alongside the prev_pc in the frame structure.  At the
point at which the link register is unmasked, the AArch64 port calls into frame
to sets the bool.  This is the most efficient way of doing it.

The marker is also added to the python frame printer, which is always printed if
set.  The marker is not explicitly exposed to the python code.

I expect this will potentially cause issues with some tests in the testsuite
when Armv8.3 pointer authentication is used.  This should be fixed up in the
the future once real hardware is available for full testsuite testing.

gdb/ChangeLog:

        * NEWS: Expand the Pointer Authentication entry.
        * aarch64-tdep.c (aarch64_frame_unmask_address): Rename from this.
        (aarch64_frame_unmask_lr): ... to this.
        (aarch64_prologue_prev_register, aarch64_dwarf2_prev_register):
        Call aarch64_frame_unmask_lr.
        * frame.c (struct frame_info): Add "masked" variable.
        (frame_set_previous_pc_masked) (frame_get_pc_masked): New functions.
        (fprint_frame): Check for masked pc.
        * frame.h (frame_set_previous_pc_masked) (frame_get_pc_masked): New
        declarations.
* python/py-framefilter.c (py_print_frame): Check for masked pc.
        * stack.c (print_frame): Check for masked pc.

gdb/doc/ChangeLog:

        * gdb.texinfo (AArch64 Pointer Authentication): New subsection.

gdb/ChangeLog
gdb/NEWS
gdb/aarch64-tdep.c
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/frame.c
gdb/frame.h
gdb/python/py-framefilter.c
gdb/stack.c

index 82e0819c136dc42702e74c0714b399b5ab437436..bacf5d76368962712f9ca99b2da8e75ee539217c 100644 (file)
@@ -1,3 +1,18 @@
+2019-08-07  Alan Hayward  <alan.hayward@arm.com>
+
+       * NEWS: Expand the Pointer Authentication entry.
+       * aarch64-tdep.c (aarch64_frame_unmask_address): Rename from this.
+       (aarch64_frame_unmask_lr): ... to this.
+       (aarch64_prologue_prev_register, aarch64_dwarf2_prev_register):
+       Call aarch64_frame_unmask_lr.
+       * frame.c (struct frame_info): Add "masked" variable.
+       (frame_set_previous_pc_masked) (frame_get_pc_masked): New functions.
+       (fprint_frame): Check for masked pc.
+       * frame.h (frame_set_previous_pc_masked) (frame_get_pc_masked): New
+       declarations.
+       * python/py-framefilter.c (py_print_frame): Check for masked pc.
+       * stack.c (print_frame): Check for masked pc.
+
 2019-08-06  Tom Tromey  <tom@tromey.com>
 
        * stabsread.c (patch_block_stabs, read_one_struct_field)
index b4c59e4410d8247b14506c3622b540be21962da7..fa01adf6e894ac81c534ce40deeafab846a63e91 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -16,7 +16,9 @@
   architectures require kernel changes.  TLS is not yet supported for
   amd64 and i386 process core dumps.
 
-* Support for Pointer Authentication on AArch64 Linux.
+* Support for Pointer Authentication (PAC) on AArch64 Linux.  Return
+  addresses that required unmasking are shown in the backtrace with the
+  postfix [PAC].
 
 * Two new convenience functions $_cimag and $_creal that extract the
   imaginary and real parts respectively from complex numbers.
index e23cc3f9567d08cf154962608ef414fb31c231fb..9b6324f0fcf05133f59b7bc4f38ccd383dfb1cf9 100644 (file)
@@ -250,13 +250,13 @@ class instruction_reader : public abstract_instruction_reader
 
 } // namespace
 
-/* If address signing is enabled, mask off the signature bits from ADDR, using
-   the register values in THIS_FRAME.  */
+/* If address signing is enabled, mask off the signature bits from the link
+   register, which is passed by value in ADDR, using the register values in
+   THIS_FRAME.  */
 
 static CORE_ADDR
-aarch64_frame_unmask_address (struct gdbarch_tdep *tdep,
-                             struct frame_info *this_frame,
-                             CORE_ADDR addr)
+aarch64_frame_unmask_lr (struct gdbarch_tdep *tdep,
+                        struct frame_info *this_frame, CORE_ADDR addr)
 {
   if (tdep->has_pauth ()
       && frame_unwind_register_unsigned (this_frame,
@@ -265,6 +265,9 @@ aarch64_frame_unmask_address (struct gdbarch_tdep *tdep,
       int cmask_num = AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base);
       CORE_ADDR cmask = frame_unwind_register_unsigned (this_frame, cmask_num);
       addr = addr & ~cmask;
+
+      /* Record in the frame that the link register required unmasking.  */
+      set_frame_previous_pc_masked (this_frame);
     }
 
   return addr;
@@ -952,7 +955,7 @@ aarch64_prologue_prev_register (struct frame_info *this_frame,
       if (tdep->has_pauth ()
          && trad_frame_value_p (cache->saved_regs,
                                 tdep->pauth_ra_state_regnum))
-       lr = aarch64_frame_unmask_address (tdep, this_frame, lr);
+       lr = aarch64_frame_unmask_lr (tdep, this_frame, lr);
 
       return frame_unwind_got_constant (this_frame, prev_regnum, lr);
     }
@@ -1119,7 +1122,7 @@ aarch64_dwarf2_prev_register (struct frame_info *this_frame,
     {
     case AARCH64_PC_REGNUM:
       lr = frame_unwind_register_unsigned (this_frame, AARCH64_LR_REGNUM);
-      lr = aarch64_frame_unmask_address (tdep, this_frame, lr);
+      lr = aarch64_frame_unmask_lr (tdep, this_frame, lr);
       return frame_unwind_got_constant (this_frame, regnum, lr);
 
     default:
index 6faf5f359d6a615a37aaf7a9b2114e33a94d4732..702bb7c7a0256d0088f64c0dc6e695cc36221be9 100644 (file)
@@ -1,3 +1,7 @@
+2019-08-07  Alan Hayward  <alan.hayward@arm.com>
+
+       * gdb.texinfo (AArch64 Pointer Authentication): New subsection.
+
 2019-08-05  Christian Biesinger  <cbiesinger@google.com>
 
        * python.texi (Blocks In Python): Document dictionary access on blocks.
index 89b1eda2c175d368122c3b19d84b0f113f93b3a6..7f8c0aff1cdb1451ab0d7338618c3545483e335a 100644 (file)
@@ -24390,6 +24390,14 @@ but the lengths of the @code{z} and @code{p} registers will not change.  This
 is a known limitation of @value{GDBN} and does not affect the execution of the
 target process.
 
+@subsubsection AArch64 Pointer Authentication.
+@cindex AArch64 Pointer Authentication.
+
+When @value{GDBN} is debugging the AArch64 architecture, and the program is
+using the v8.3-A feature Pointer Authentication (PAC), then whenever the link
+register @code{$lr} is pointing to an PAC function it's value will be masked.
+When GDB prints a backtrace, any addresses that required unmasking will be
+postfixed with the marker [PAC].
 
 @node i386
 @subsection x86 Architecture-specific Issues
index adac24f68c6cc56852178e35ac76e1d0ce62ea28..b62fd5cd852158226d952299581ab5af1f4c036c 100644 (file)
@@ -123,6 +123,8 @@ struct frame_info
   /* Cached copy of the previous frame's resume address.  */
   struct {
     enum cached_copy_status status;
+    /* Did VALUE require unmasking when being read.  */
+    bool masked;
     CORE_ADDR value;
   } prev_pc;
   
@@ -161,6 +163,25 @@ struct frame_info
   const char *stop_string;
 };
 
+/* See frame.h.  */
+
+void
+set_frame_previous_pc_masked (struct frame_info *frame)
+{
+  frame->prev_pc.masked = true;
+}
+
+/* See frame.h.  */
+
+bool
+get_frame_pc_masked (const struct frame_info *frame)
+{
+  gdb_assert (frame->next != nullptr);
+  gdb_assert (frame->next->prev_pc.status == CC_VALUE);
+
+  return frame->next->prev_pc.masked;
+}
+
 /* A frame stash used to speed up frame lookups.  Create a hash table
    to stash frames previously accessed from the frame cache for
    quicker subsequent retrieval.  The hash table is emptied whenever
@@ -429,8 +450,11 @@ fprint_frame (struct ui_file *file, struct frame_info *fi)
   if (fi->next == NULL || fi->next->prev_pc.status == CC_UNKNOWN)
     fprintf_unfiltered (file, "<unknown>");
   else if (fi->next->prev_pc.status == CC_VALUE)
-    fprintf_unfiltered (file, "%s",
-                       hex_string (fi->next->prev_pc.value));
+    {
+      fprintf_unfiltered (file, "%s", hex_string (fi->next->prev_pc.value));
+      if (fi->next->prev_pc.masked)
+       fprintf_unfiltered (file, "[PAC]");
+    }
   else if (fi->next->prev_pc.status == CC_NOT_SAVED)
     val_print_not_saved (file);
   else if (fi->next->prev_pc.status == CC_UNAVAILABLE)
index ccc285005aaf8e3b2f6fdb85a46b1056ec39fa8d..fdb401d84f1e219c6aaa21dd125c3f47a893f91d 100644 (file)
@@ -949,4 +949,13 @@ extern const gdb::option::option_def set_backtrace_option_defs[2];
 /* The values behind the global "set backtrace ..." settings.  */
 extern set_backtrace_options user_set_backtrace_options;
 
+/* Mark that the PC value is masked for the previous frame.  */
+
+extern void set_frame_previous_pc_masked (struct frame_info *frame);
+
+/* Get whether the PC value is masked for the given frame.  */
+
+extern bool get_frame_pc_masked (const struct frame_info *frame);
+
+
 #endif /* !defined (FRAME_H)  */
index a2a96ac0d39eb50b6cfa7abdaa9e01cf2b216e83..d805ec68f233bc257d7891a63b14e2dba58c5f03 100644 (file)
@@ -901,6 +901,8 @@ py_print_frame (PyObject *filter, frame_filter_flags flags,
            {
              annotate_frame_address ();
              out->field_core_addr ("addr", gdbarch, address);
+             if (get_frame_pc_masked (frame))
+               out->field_string ("pac", " [PAC]");
              annotate_frame_address_end ();
              out->text (" in ");
            }
index c68b3876d35428a81137034edb3f51bce04fd6f0..0859815baf452f037d8d86e9e2206f7f6fc9e8ab 100644 (file)
@@ -1298,7 +1298,11 @@ print_frame (const frame_print_options &fp_opts,
        {
          annotate_frame_address ();
          if (pc_p)
-           uiout->field_core_addr ("addr", gdbarch, pc);
+           {
+             uiout->field_core_addr ("addr", gdbarch, pc);
+             if (get_frame_pc_masked (frame))
+               uiout->field_string ("pac", " [PAC]");
+           }
          else
            uiout->field_string ("addr", "<unavailable>",
                                 ui_out_style_kind::ADDRESS);