aarch64: enable PAC/BTI
authorBill Roberts <bill.roberts@arm.com>
Wed, 10 Jul 2024 17:49:03 +0000 (12:49 -0500)
committerCharles Giessen <46324611+charles-lunarg@users.noreply.github.com>
Tue, 23 Jul 2024 20:33:26 +0000 (15:33 -0500)
Enable Pointer Authentication Codes (PAC) and Branch Target
Identification (BTI) support for ARM 64 targets.

PAC works by signing the LR with either an A key or B key and verifying
the return address. Since the assembly code does not push and pop the
link register to the stack, and it remains in the register file, their
is no need to sign the LR, so PAC is essentially just adding it to the
GNU notes section for auditing purposes.

BTI works by marking all call and jump positions with bti c and bti
j instructions. If execution control transfers via an indirect branch
or call to an instruction other than a BTI instruction, the execution
is killed via SIGILL.

For BTI to work, all object files linked for a unit of execution,
whether an executable or a library must have the GNU Notes section of
the ELF file marked to indicate BTI support. This is so loader/linkers
can apply the proper permission bits (PROT_BRI) on the memory region.

PAC can also be annotated in the GNU ELF notes section, but it's not
required for enablement, as interleaved PAC and non-pac code works as
expected since it's the callee that performs all the checking.

Testing was done under the following CFLAGS and CXXFLAGS for all
combinations:
1. -mbranch-protection=none
2. -mbranch-protection=standard
3. -mbranch-protection=pac-ret
4. -mbranch-protection=pac-ret+b-key
5. -mbranch-protection=bti

Signed-off-by: Bill Roberts <bill.roberts@arm.com>
loader/unknown_ext_chain_gas_aarch.S

index 048f17a275aa358f0d3ddf05d7582b52ea0e970c..1176be30fb85d54c920066df6e7013e7aa81a15c 100644 (file)
 
 .include "gen_defines.asm"
 
+/*
+ * References:
+ *  - https://developer.arm.com/documentation/101028/0012/5--Feature-test-macros
+ *  - https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
+ */
+#if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1
+  #define BTI_J  hint 36 /* bti j: for jumps, IE br instructions */
+  #define BTI_C  hint 34 /* bti c: for calls, IE bl instructions */
+  #define GNU_PROPERTY_AARCH64_BTI 1 /* bit 0 GNU Notes is for BTI support */
+#else
+  #define BTI_J
+  #define BTI_C
+  #define GNU_PROPERTY_AARCH64_BTI 0
+#endif
+
+/*
+ * We just need PAC added to GNU Notes for auditing features, the assembly itself does
+ * not need pac augmentation at this time because it doesn't make use of the SP aka x30.
+ */
+#if defined(__ARM_FEATURE_PAC_DEFAULT)
+  #define GNU_PROPERTY_AARCH64_POINTER_AUTH 2 /* bit 1 GNU Notes is for PAC support */
+#else
+  #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0
+#endif
+
 .if AARCH_64
 
 .macro PhysDevExtTramp num
 #endif
 .balign 4
 
+/*
+ * NOTE: x16 is used for the br register so the pstate.btype is 01 and can
+ * land on 'bti c' that would be inserted by the compiler in C/C++ functions.
+ * See: https://developer.arm.com/documentation/102433/0200/Jump-oriented-programming
+ */
 vkPhysDevExtTramp\num:
+    BTI_C
     ldr     x9, [x0]                                                 // Load the loader_instance_dispatch_table* into x9
     ldr     x0, [x0, PHYS_DEV_OFFSET_PHYS_DEV_TRAMP]                 // Load the unwrapped VkPhysicalDevice into x0
     mov     x10, (PHYS_DEV_OFFSET_INST_DISPATCH + (PTR_SIZE * \num)) // Put the offset of the entry in the dispatch table for the function
-    ldr     x11, [x9, x10]                                           // Load the address to branch to out of the dispatch table
-    br      x11                                                      // Branch to the next member of the dispatch chain
+    ldr     x16, [x9, x10]                                           // Load the address to branch to out of the dispatch table
+    br      x16                                                      // Branch to the next member of the dispatch chain
 .endm
 
 .macro PhysDevExtTermin num
@@ -50,13 +81,15 @@ vkPhysDevExtTramp\num:
 #endif
 .balign 4
 vkPhysDevExtTermin\num:
+    BTI_C
     ldr     x9, [x0, ICD_TERM_OFFSET_PHYS_DEV_TERM]             // Load the loader_icd_term* in x9
     mov     x11, (DISPATCH_OFFSET_ICD_TERM + (PTR_SIZE * \num)) // Put the offset into the dispatch table in x11
-    ldr     x10, [x9, x11]                                      // Load the address of the next function in the dispatch chain
-    cbz     x10, terminError\num                                // Go to the error section if the next function in the chain is NULL
+    ldr     x16, [x9, x11]                                      // Load the address of the next function in the dispatch chain
+    cbz     x16, terminError\num                                // Go to the error section if the next function in the chain is NULL
     ldr     x0, [x0, PHYS_DEV_OFFSET_PHYS_DEV_TERM]             // Unwrap the VkPhysicalDevice in x0
-    br      x10                                                 // Jump to the next function in the chain
+    br      x16                                           // Jump to the next function in the chain
 terminError\num:
+    BTI_J
     mov     x10, (FUNCTION_OFFSET_INSTANCE + (CHAR_PTR_SIZE * \num)) // Offset of the function name string in the instance
     ldr     x11, [x9, INSTANCE_OFFSET_ICD_TERM]   // Load the instance pointer
     mov     x0, x11                               // Vulkan instance pointer (first arg)
@@ -75,10 +108,11 @@ terminError\num:
 #endif
 .balign 4
 vkdev_ext\num:
+    BTI_C
     ldr     x9, [x0]                                              // Load the loader_instance_dispatch_table* into x9
     mov     x10, (EXT_OFFSET_DEVICE_DISPATCH + (PTR_SIZE * \num)) // Offset of the desired function in the dispatch table
-    ldr     x11, [x9, x10]                                        // Load the function address
-    br      x11
+    ldr     x16, [x9, x10]                                        // Load the function address
+    br      x16
 .endm
 
 .else // AARCH_32
@@ -146,6 +180,20 @@ vkdev_ext\num:
 
 #if defined(__ELF__)
 .section .note.GNU-stack,"",%progbits
+/* Add the PAC and BTI support to GNU Notes section for ELF object files */
+#if GNU_PROPERTY_AARCH64_BTI != 0 || GNU_PROPERTY_AARCH64_POINTER_AUTH != 0
+    .pushsection .note.gnu.property, "a"; /* Start a new allocatable section */
+    .balign 8; /* align it on a byte boundry */
+    .long 4; /* size of "GNU\0" */
+    .long 0x10; /* size of descriptor */
+    .long 0x5; /* NT_GNU_PROPERTY_TYPE_0 */
+    .asciz "GNU";
+    .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */
+    .long 4; /* Four bytes of data */
+    .long (GNU_PROPERTY_AARCH64_BTI|GNU_PROPERTY_AARCH64_POINTER_AUTH); /* BTI or PAC is enabled */
+    .long 0; /* padding for 8 byte alignment */
+    .popsection; /* end the section */
+#endif
 #endif
 
 .data