Skip to content

Commit da8477b

Browse files
stratakisvstinner
andauthored
gh-139808: Add branch protections for aarch64 in asm_trampoline.S (#130864)
Apply protection against ROP/JOP attacks for aarch64 on asm_trampoline.S. The BTI flag must be applied in assembler sources for this class of attacks to be mitigated on newer aarch64 processors. See also: https://sourceware.org/annobin/annobin.html/Test-branch-protection.html and https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/enabling-pac-and-bti-on-aarch64 Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 0534774 commit da8477b

4 files changed

Lines changed: 75 additions & 0 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add branch protections for AArch64 (BTI/PAC) in assembly code used by
2+
:option:`-X perf_jit <-X>` (Linux perf profiler integration).

Python/asm_trampoline.S

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include "asm_trampoline_aarch64.h"
2+
13
.text
24
#if defined(__APPLE__)
35
.globl __Py_trampoline_func_start
@@ -29,10 +31,12 @@ _Py_trampoline_func_start:
2931
#if defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__)
3032
// ARM64 little endian, 64bit ABI
3133
// generate with aarch64-linux-gnu-gcc 12.1
34+
SIGN_LR
3235
stp x29, x30, [sp, -16]!
3336
mov x29, sp
3437
blr x3
3538
ldp x29, x30, [sp], 16
39+
VERIFY_LR
3640
ret
3741
#endif
3842
#ifdef __riscv

Python/asm_trampoline_aarch64.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#ifndef ASM_TRAMPOLINE_AARCH_64_H_
2+
#define ASM_TRAMPOLINE_AARCH_64_H_
3+
4+
/*
5+
* References:
6+
* - https://developer.arm.com/documentation/101028/0012/5--Feature-test-macros
7+
* - https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
8+
*/
9+
10+
#if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1
11+
#define BTI_J hint 36 /* bti j: for jumps, IE br instructions */
12+
#define BTI_C hint 34 /* bti c: for calls, IE bl instructions */
13+
#define GNU_PROPERTY_AARCH64_BTI 1 /* bit 0 GNU Notes is for BTI support */
14+
#else
15+
#define BTI_J
16+
#define BTI_C
17+
#define GNU_PROPERTY_AARCH64_BTI 0
18+
#endif
19+
20+
#if defined(__ARM_FEATURE_PAC_DEFAULT)
21+
#if __ARM_FEATURE_PAC_DEFAULT & 1
22+
#define SIGN_LR hint 25 /* paciasp: sign with the A key */
23+
#define VERIFY_LR hint 29 /* autiasp: verify with the A key */
24+
#elif __ARM_FEATURE_PAC_DEFAULT & 2
25+
#define SIGN_LR hint 27 /* pacibsp: sign with the b key */
26+
#define VERIFY_LR hint 31 /* autibsp: verify with the b key */
27+
#endif
28+
#define GNU_PROPERTY_AARCH64_POINTER_AUTH 2 /* bit 1 GNU Notes is for PAC support */
29+
#else
30+
#define SIGN_LR BTI_C
31+
#define VERIFY_LR
32+
#define GNU_PROPERTY_AARCH64_POINTER_AUTH 0
33+
#endif
34+
35+
#if defined(__ARM_FEATURE_GCS_DEFAULT) && __ARM_FEATURE_GCS_DEFAULT == 1
36+
#define GNU_PROPERTY_AARCH64_GCS 4 /* bit 2 GNU Notes is for GCS support */
37+
#else
38+
#define GNU_PROPERTY_AARCH64_GCS 0
39+
#endif
40+
41+
/* Add the BTI, PAC and GCS support to GNU Notes section */
42+
#if GNU_PROPERTY_AARCH64_BTI != 0 || GNU_PROPERTY_AARCH64_POINTER_AUTH != 0 || GNU_PROPERTY_AARCH64_GCS != 0
43+
.pushsection .note.gnu.property, "a"; /* Start a new allocatable section */
44+
.balign 8; /* align it on a byte boundry */
45+
.long 4; /* size of "GNU\0" */
46+
.long 0x10; /* size of descriptor */
47+
.long 0x5; /* NT_GNU_PROPERTY_TYPE_0 */
48+
.asciz "GNU";
49+
.long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */
50+
.long 4; /* Four bytes of data */
51+
.long (GNU_PROPERTY_AARCH64_BTI|GNU_PROPERTY_AARCH64_POINTER_AUTH|GNU_PROPERTY_AARCH64_GCS); /* BTI, PAC or GCS is enabled */
52+
.long 0; /* padding for 8 byte alignment */
53+
.popsection; /* end the section */
54+
#endif
55+
56+
#endif

Python/jit_unwind.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ enum {
6060
DWRF_CFA_offset_extended_sf = 0x11, // Extended signed offset
6161
DWRF_CFA_advance_loc = 0x40, // Advance location counter
6262
DWRF_CFA_offset = 0x80, // Simple offset instruction
63+
#if defined(__aarch64__)
64+
DWRF_CFA_AARCH64_negate_ra_state = 0x2d, // Toggle return address signing state
65+
#endif
6366
DWRF_CFA_restore = 0xc0 // Restore register
6467
};
6568

@@ -562,6 +565,13 @@ static void elf_init_ehframe_perf(ELFObjectContext* ctx) {
562565
DWRF_UV(8); // New offset: SP + 8
563566
#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__)
564567
/* AArch64 calling convention unwinding rules */
568+
#if defined(__ARM_FEATURE_PAC_DEFAULT) || \
569+
(defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1)
570+
DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance past SIGN_LR (4 bytes)
571+
#endif
572+
#if defined(__ARM_FEATURE_PAC_DEFAULT)
573+
DWRF_U8(DWRF_CFA_AARCH64_negate_ra_state); // Saved LR is PAC-signed from here
574+
#endif
565575
DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance by 1 instruction (4 bytes)
566576
DWRF_U8(DWRF_CFA_def_cfa_offset); // CFA = SP + 16
567577
DWRF_UV(16); // Stack pointer moved by 16 bytes
@@ -570,6 +580,9 @@ static void elf_init_ehframe_perf(ELFObjectContext* ctx) {
570580
DWRF_U8(DWRF_CFA_offset | DWRF_REG_RA); // x30 (link register) saved
571581
DWRF_UV(1); // At CFA-8 (1 * 8 = 8 bytes from CFA)
572582
DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance by 3 instructions (12 bytes)
583+
#if defined(__ARM_FEATURE_PAC_DEFAULT)
584+
DWRF_U8(DWRF_CFA_AARCH64_negate_ra_state); // LR is authenticated, no longer PAC-signed
585+
#endif
573586
DWRF_U8(DWRF_CFA_def_cfa_register); // CFA = FP (x29) + 16
574587
DWRF_UV(DWRF_REG_FP);
575588
DWRF_U8(DWRF_CFA_restore | DWRF_REG_RA); // Restore x30 - NO DWRF_UV() after this!

0 commit comments

Comments
 (0)