Skip to content

Commit fdcf6f4

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.6-beta.1
2 parents 64046e9 + 9928f6e commit fdcf6f4

23 files changed

+7966
-231
lines changed

compiler-rt/lib/builtins/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ set(aarch64_SOURCES
570570
${GENERIC_TF_SOURCES}
571571
${GENERIC_SOURCES}
572572
cpu_model/aarch64.c
573+
aarch64/emupac.c
573574
aarch64/fp_mode.c
574575
)
575576

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#include <stdint.h>
2+
3+
#define XXH_INLINE_ALL
4+
#define XXH_NO_STDLIB
5+
#define XXH_memcpy __builtin_memcpy
6+
#define XXH_memset __builtin_memset
7+
#define XXH_memcmp __builtin_memcmp
8+
#include "../xxhash.h"
9+
10+
// EmuPAC implements runtime emulation of PAC instructions. If the current
11+
// CPU supports PAC, EmuPAC uses real PAC instructions. Otherwise, it uses the
12+
// emulation, which is effectively an implementation of PAC with an IMPDEF
13+
// hashing scheme based on XXH128.
14+
//
15+
// The purpose of the emulation is to allow programs to be built to be portable
16+
// to machines without PAC support, with some performance loss and increased
17+
// probability of false positives (due to not being able to portably determine
18+
// the VA size), while being functionally almost equivalent to running on a
19+
// machine with PAC support. One example of a use case is if PAC is used in
20+
// production as a security mitigation, but the testing environment is
21+
// heterogeneous (i.e. some machines lack PAC support). In this case we would
22+
// like the testing machines to be able to detect issues resulting
23+
// from the use of PAC instructions that would affect production by running
24+
// tests. This can be achieved by building test binaries with EmuPAC and
25+
// production binaries with real PAC.
26+
//
27+
// The emulation assumes that the VA size is at most 48 bits. The architecture
28+
// as of ARMv8.2, which was the last architecture version in which PAC was not
29+
// mandatory, permitted VA size up to 52 bits via ARMv8.2-LVA, but we are
30+
// unaware of an ARMv8.2 CPU that implemented ARMv8.2-LVA.
31+
32+
const uint64_t kMaxVASize = 48;
33+
const uint64_t kPACMask = ((1ULL << 55) - 1) & ~((1ULL << kMaxVASize) - 1);
34+
const uint64_t kTTBR1Mask = 1ULL << 55;
35+
36+
// Determine whether PAC is supported without accessing memory. This utilizes
37+
// the XPACLRI instruction which will copy bit 55 of x30 into at least bit 54 if
38+
// PAC is supported and acts as a NOP if PAC is not supported.
39+
static _Bool pac_supported() {
40+
register uintptr_t x30 __asm__("x30") = 1ULL << 55;
41+
__asm__ __volatile__("xpaclri" : "+r"(x30));
42+
return x30 & (1ULL << 54);
43+
}
44+
45+
// This asm snippet is used to force the creation of a frame record when
46+
// calling the EmuPAC functions. This is important because the EmuPAC functions
47+
// may crash if an auth failure is detected and may be unwound past using a
48+
// frame pointer based unwinder.
49+
#ifdef __GCC_HAVE_DWARF2_CFI_ASM
50+
#define frame_pointer_wrap(sym) \
51+
"stp x29, x30, [sp, #-16]!\n" \
52+
".cfi_def_cfa_offset 16\n" \
53+
"mov x29, sp\n" \
54+
".cfi_def_cfa w29, 16\n" \
55+
".cfi_offset w30, -8\n" \
56+
".cfi_offset w29, -16\n" \
57+
"bl " #sym "\n" \
58+
".cfi_def_cfa wsp, 16\n" \
59+
"ldp x29, x30, [sp], #16\n" \
60+
".cfi_def_cfa_offset 0\n" \
61+
".cfi_restore w30\n" \
62+
".cfi_restore w29\n" \
63+
"ret"
64+
#else
65+
#define frame_pointer_wrap(sym) \
66+
"stp x29, x30, [sp, #-16]!\n" \
67+
"mov x29, sp\n" \
68+
"bl " #sym "\n" \
69+
"ldp x29, x30, [sp], #16\n" \
70+
"ret"
71+
#endif
72+
73+
uint64_t __emupac_pacda_impl(uint64_t ptr, uint64_t disc) {
74+
if (pac_supported()) {
75+
__asm__ __volatile__(".arch_extension pauth\npacda %0, %1"
76+
: "+r"(ptr)
77+
: "r"(disc));
78+
return ptr;
79+
}
80+
if (ptr & kTTBR1Mask) {
81+
if ((ptr & kPACMask) != kPACMask) {
82+
return ptr | kPACMask;
83+
}
84+
} else {
85+
if (ptr & kPACMask) {
86+
return ptr & ~kPACMask;
87+
}
88+
}
89+
uint64_t hash = XXH3_64bits_withSeed(&ptr, 8, disc);
90+
return (ptr & ~kPACMask) | (hash & kPACMask);
91+
}
92+
93+
__attribute__((naked)) uint64_t __emupac_pacda(uint64_t ptr, uint64_t disc) {
94+
__asm__(frame_pointer_wrap(__emupac_pacda_impl));
95+
}
96+
97+
uint64_t __emupac_autda_impl(uint64_t ptr, uint64_t disc) {
98+
if (pac_supported()) {
99+
__asm__ __volatile__(".arch_extension pauth\nautda %0, %1"
100+
: "+r"(ptr)
101+
: "r"(disc));
102+
return ptr;
103+
}
104+
uint64_t ptr_without_pac =
105+
(ptr & kTTBR1Mask) ? (ptr | kPACMask) : (ptr & ~kPACMask);
106+
uint64_t hash = XXH3_64bits_withSeed(&ptr_without_pac, 8, disc);
107+
if (((ptr & ~kPACMask) | (hash & kPACMask)) != ptr) {
108+
__builtin_trap();
109+
}
110+
return ptr_without_pac;
111+
}
112+
113+
__attribute__((naked)) uint64_t __emupac_autda(uint64_t ptr, uint64_t disc) {
114+
__asm__(frame_pointer_wrap(__emupac_autda_impl));
115+
}

0 commit comments

Comments
 (0)