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