backtrace: Strip PAC field when PAUTH is enabled
authorLouis Mayencourt <[email protected]>
Tue, 9 Jul 2019 10:40:55 +0000 (11:40 +0100)
committerLouis Mayencourt <[email protected]>
Wed, 17 Jul 2019 09:43:48 +0000 (10:43 +0100)
When pointer authentication is enabled, the LR value saved on the stack
contains a Pointer Authentication Code (PAC). It must be stripped to
retrieve the return address.

The PAC field is stored on the high bits of the address and defined as:
- PAC field = Xn[54:bottom_PAC_bit], when address tagging is used.
- PAC field = Xn[63:56, 54:bottom_PAC_bit], without address tagging.

With bottom_PAC_bit = 64 - TCR_ELx.TnSZ

Change-Id: I21d804e58200dfeca1da4c2554690bed5d191936
Signed-off-by: Louis Mayencourt <[email protected]>
common/backtrace/backtrace.c

index ecc65c92e1859e78c9c0a054be2b29cdf331c13c..53f8b0719d24e1eb545b90c61cfc482c86bcf57a 100644 (file)
@@ -37,6 +37,47 @@ struct frame_record {
        uintptr_t return_addr;
 };
 
+/*
+ * Strip the Pointer Authentication Code (PAC) from the address to retrieve the
+ * original one.
+ *
+ * The PAC field is stored on the high bits of the address and defined as:
+ * - PAC field = Xn[54:bottom_PAC_bit], when address tagging is used.
+ * - PAC field = Xn[63:56, 54:bottom_PAC_bit], without address tagging.
+ *
+ * With bottom_PAC_bit = 64 - TCR_ELx.TnSZ
+ */
+#if ENABLE_PAUTH
+static uintptr_t demangle_address(uintptr_t addr)
+{
+       unsigned int el, t0sz, bottom_pac_bit;
+       uint64_t tcr, pac_mask;
+
+       /*
+        * Different virtual address space size can be defined for each EL.
+        * Ensure that we use the proper one by reading the corresponding
+        * TCR_ELx register.
+        */
+       el = get_current_el();
+
+       if (el == 3U) {
+               tcr = read_tcr_el3();
+       } else if (el == 2U) {
+               tcr = read_tcr_el2();
+       } else {
+               tcr = read_tcr_el1();
+       }
+
+       /* T0SZ = TCR_ELx[5:0] */
+       t0sz = tcr & 0x1f;
+       bottom_pac_bit = 64 - t0sz;
+       pac_mask = (1ULL << bottom_pac_bit) - 1;
+
+       /* demangle the address with the computed mask */
+       return (addr & pac_mask);
+}
+#endif /* ENABLE_PAUTH */
+
 static const char *get_el_str(unsigned int el)
 {
        if (el == 3U) {
@@ -57,6 +98,15 @@ static bool is_address_readable(uintptr_t addr)
 {
        unsigned int el = get_current_el();
 
+#if ENABLE_PAUTH
+       /*
+        * When pointer authentication is enabled, the LR value saved on the
+        * stack contains a PAC. It must be stripped to retrieve the return
+        * address.
+        */
+       addr = demangle_address(addr);
+#endif
+
        if (el == 3U) {
                ats1e3r(addr);
        } else if (el == 2U) {
@@ -201,6 +251,15 @@ static void unwind_stack(struct frame_record *fr, uintptr_t current_pc,
                 */
                call_site = fr->return_addr - 4U;
 
+#if ENABLE_PAUTH
+               /*
+                * When pointer authentication is enabled, the LR value saved on
+                * the stack contains a PAC. It must be stripped to retrieve the
+                * return address.
+                */
+               call_site = demangle_address(call_site);
+#endif
+
                /*
                 * If the address is invalid it means that the frame record is
                 * probably corrupted.