Skip to content

Commit d8f654b

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. Pull Request: llvm#133530
1 parent 13cb97d commit d8f654b

File tree

5 files changed

+150
-3
lines changed

5 files changed

+150
-3
lines changed

compiler-rt/cmake/Modules/AddCompilerRT.cmake

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ function(add_compiler_rt_runtime name type)
172172
cmake_parse_arguments(LIB
173173
""
174174
"PARENT_TARGET"
175-
"OS;ARCHS;SOURCES;CFLAGS;LINK_FLAGS;DEFS;DEPS;LINK_LIBS;OBJECT_LIBS;ADDITIONAL_HEADERS;EXTENSIONS"
175+
"OS;ARCHS;SOURCES;CFLAGS;LINK_FLAGS;DEFS;DEPS;LINK_LIBS;OBJECT_LIBS;ADDITIONAL_HEADERS;EXTENSIONS;C_STANDARD;CXX_STANDARD"
176176
${ARGN})
177177
set(libnames)
178178
# Until we support this some other way, build compiler-rt runtime without LTO
@@ -360,6 +360,12 @@ function(add_compiler_rt_runtime name type)
360360
set_target_link_flags(${libname} ${extra_link_flags_${libname}})
361361
set_property(TARGET ${libname} APPEND PROPERTY
362362
COMPILE_DEFINITIONS ${LIB_DEFS})
363+
if(LIB_C_STANDARD)
364+
set_property(TARGET ${libname} PROPERTY C_STANDARD ${LIB_C_STANDARD})
365+
endif()
366+
if(LIB_CXX_STANDARD)
367+
set_property(TARGET ${libname} PROPERTY CXX_STANDARD ${LIB_CXX_STANDARD})
368+
endif()
363369
set_target_output_directories(${libname} ${output_dir_${libname}})
364370
install(TARGETS ${libname}
365371
ARCHIVE DESTINATION ${install_dir_${libname}}

compiler-rt/cmake/builtin-config-ix.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ builtin_check_c_compiler_flag("-Xclang -mcode-object-version=none" COMPILER_RT_H
2626
builtin_check_c_compiler_flag(-Wbuiltin-declaration-mismatch COMPILER_RT_HAS_WBUILTIN_DECLARATION_MISMATCH_FLAG)
2727
builtin_check_c_compiler_flag(/Zl COMPILER_RT_HAS_ZL_FLAG)
2828
builtin_check_c_compiler_flag(-fcf-protection=full COMPILER_RT_HAS_FCF_PROTECTION_FLAG)
29+
builtin_check_c_compiler_flag(-nostdinc++ COMPILER_RT_HAS_NOSTDINCXX_FLAG)
2930

3031
builtin_check_c_compiler_source(COMPILER_RT_HAS_ATOMIC_KEYWORD
3132
"

compiler-rt/lib/builtins/CMakeLists.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
66
cmake_minimum_required(VERSION 3.20.0)
77

88
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
9-
project(CompilerRTBuiltins C ASM)
9+
project(CompilerRTBuiltins C CXX ASM)
1010
set(COMPILER_RT_STANDALONE_BUILD TRUE)
1111
set(COMPILER_RT_BUILTINS_STANDALONE_BUILD TRUE)
1212

@@ -64,6 +64,8 @@ include(CMakePushCheckState)
6464
option(COMPILER_RT_BUILTINS_HIDE_SYMBOLS
6565
"Do not export any symbols from the static library." ON)
6666

67+
include_directories(../../../third-party/siphash/include)
68+
6769
# TODO: Need to add a mechanism for logging errors when builtin source files are
6870
# added to a sub-directory and not this CMakeLists file.
6971
set(GENERIC_SOURCES
@@ -570,6 +572,7 @@ set(aarch64_SOURCES
570572
${GENERIC_TF_SOURCES}
571573
${GENERIC_SOURCES}
572574
cpu_model/aarch64.c
575+
aarch64/emupac.cpp
573576
aarch64/fp_mode.c
574577
)
575578

@@ -802,7 +805,7 @@ else ()
802805
append_list_if(COMPILER_RT_ENABLE_CET -fcf-protection=full BUILTIN_CFLAGS)
803806
endif()
804807

805-
append_list_if(COMPILER_RT_HAS_STD_C11_FLAG -std=c11 BUILTIN_CFLAGS)
808+
append_list_if(COMPILER_RT_HAS_NOSTDINCXX_FLAG -nostdinc++ BUILTIN_CFLAGS)
806809
append_list_if(COMPILER_RT_HAS_WBUILTIN_DECLARATION_MISMATCH_FLAG -Werror=builtin-declaration-mismatch BUILTIN_CFLAGS)
807810

808811
# Don't embed directives for picking any specific CRT
@@ -917,6 +920,8 @@ else ()
917920
SOURCES ${${arch}_SOURCES}
918921
DEFS ${BUILTIN_DEFS}
919922
CFLAGS ${BUILTIN_CFLAGS_${arch}}
923+
C_STANDARD 11
924+
CXX_STANDARD 17
920925
PARENT_TARGET builtins)
921926
cmake_pop_check_state()
922927
endif ()
Lines changed: 132 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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)