Skip to content

Commit 86933af

Browse files
committed
compiler-rt: Introduce runtime functions for emulated PAC.
The emulated PAC runtime functions emulate the ARMv8.3a pointer authentication instructions and are intended for use in heterogeneous testing environments. For more information, see the associated RFC: https://discourse.llvm.org/t/rfc-emulated-pac/85557 TODO: - Add tests. - Figure out how to get emupac.cpp to build with CMake. For some reason any .cpp files added to the builtins library are ignored. So for now only the gn build works. Pull Request: llvm#133530
1 parent 13cb97d commit 86933af

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
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.cpp
573574
aarch64/fp_mode.c
574575
)
575576

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

llvm/utils/gn/secondary/compiler-rt/lib/builtins/BUILD.gn

+3
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ static_library("builtins") {
8787
cflags += [ "-fomit-frame-pointer" ]
8888
}
8989
cflags_c = [ "-std=c11" ]
90+
cflags_cc = [ "-nostdinc++" ]
9091
}
9192

9293
sources = [
@@ -500,6 +501,7 @@ static_library("builtins") {
500501
if (current_cpu == "arm64") {
501502
sources -= [ "fp_mode.c" ]
502503
sources += [
504+
"aarch64/emupac.cpp",
503505
"aarch64/fp_mode.c",
504506
"cpu_model/aarch64.c",
505507
]
@@ -601,6 +603,7 @@ static_library("builtins") {
601603
}
602604

603605
deps = lse_targets
606+
include_dirs = [ "//third-party/siphash/include" ]
604607
}
605608

606609
# Currently unused but necessary to make sync_source_lists_from_cmake.py happy.

0 commit comments

Comments
 (0)