Skip to content

Commit

Permalink
aarch64: enable PAC/BTI
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
billatarm authored and charles-lunarg committed Jul 23, 2024
1 parent c5c24ad commit be0e1c3
Showing 1 changed file with 55 additions and 7 deletions.
62 changes: 55 additions & 7 deletions loader/unknown_ext_chain_gas_aarch.S
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,31 @@

.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
Expand All @@ -35,12 +60,18 @@
#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
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit be0e1c3

Please sign in to comment.