Path: blob/main/contrib/llvm-project/compiler-rt/lib/builtins/aarch64/emupac.cpp
213799 views
//===--- emupac.cpp - Emulated PAC implementation -------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements Emulated PAC using SipHash_1_3 as the IMPDEF hashing9// scheme.10//11//===----------------------------------------------------------------------===//1213#include <stdint.h>1415#include "siphash/SipHash.h"1617// EmuPAC implements runtime emulation of PAC instructions. If the current18// CPU supports PAC, EmuPAC uses real PAC instructions. Otherwise, it uses the19// emulation, which is effectively an implementation of PAC with an IMPDEF20// hashing scheme based on SipHash_1_3.21//22// The purpose of the emulation is to allow programs to be built to be portable23// to machines without PAC support, with some performance loss and increased24// probability of false positives (due to not being able to portably determine25// the VA size), while being functionally almost equivalent to running on a26// machine with PAC support. One example of a use case is if PAC is used in27// production as a security mitigation, but the testing environment is28// heterogeneous (i.e. some machines lack PAC support). In this case we would29// like the testing machines to be able to detect issues resulting30// from the use of PAC instructions that would affect production by running31// tests. This can be achieved by building test binaries with EmuPAC and32// production binaries with real PAC.33//34// EmuPAC should not be used in production and is only intended for testing use35// cases. This is not only because of the performance costs, which will exist36// even on PAC-supporting machines because of the function call overhead for37// each sign/auth operation, but because it provides weaker security compared to38// real PAC: the key is constant and public, which means that we do not mix a39// global secret.40//41// The emulation assumes that the VA size is at most 48 bits. The architecture42// as of ARMv8.2, which was the last architecture version in which PAC was not43// mandatory, permitted VA size up to 52 bits via ARMv8.2-LVA, but we are44// unaware of an ARMv8.2 CPU that implemented ARMv8.2-LVA.4546static const uint64_t max_va_size = 48;47static const uint64_t pac_mask =48((1ULL << 55) - 1) & ~((1ULL << max_va_size) - 1);49static const uint64_t ttbr1_mask = 1ULL << 55;5051// Determine whether PAC is supported without accessing memory. This utilizes52// the XPACLRI instruction which will copy bit 55 of x30 into at least bit 54 if53// PAC is supported and acts as a NOP if PAC is not supported.54static bool pac_supported() {55register uintptr_t x30 __asm__("x30") = 1ULL << 55;56__asm__ __volatile__("xpaclri" : "+r"(x30));57return x30 & (1ULL << 54);58}5960#ifdef __GCC_HAVE_DWARF2_CFI_ASM61#define CFI_INST(inst) inst62#else63#define CFI_INST(inst)64#endif6566#ifdef __APPLE__67#define ASM_SYMBOL(symbol) "_" #symbol68#else69#define ASM_SYMBOL(symbol) #symbol70#endif7172// This asm snippet is used to force the creation of a frame record when73// calling the EmuPAC functions. This is important because the EmuPAC functions74// may crash if an auth failure is detected and may be unwound past using a75// frame pointer based unwinder.76// clang-format off77#define FRAME_POINTER_WRAP(sym) \78CFI_INST(".cfi_startproc\n") \79"stp x29, x30, [sp, #-16]!\n" \80CFI_INST(".cfi_def_cfa_offset 16\n") \81"mov x29, sp\n" \82CFI_INST(".cfi_def_cfa w29, 16\n") \83CFI_INST(".cfi_offset w30, -8\n") \84CFI_INST(".cfi_offset w29, -16\n") \85"bl " ASM_SYMBOL(sym) "\n" \86CFI_INST(".cfi_def_cfa wsp, 16\n") \87"ldp x29, x30, [sp], #16\n" \88CFI_INST(".cfi_def_cfa_offset 0\n") \89CFI_INST(".cfi_restore w30\n") \90CFI_INST(".cfi_restore w29\n") \91"ret\n" \92CFI_INST(".cfi_endproc\n")93// clang-format on9495// Emulated DA key value.96static const uint8_t emu_da_key[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10,970x4a, 0x79, 0x6f, 0xec, 0x8b, 0x1b,980x42, 0x87, 0x81, 0xd4};99100extern "C" [[gnu::flatten]] uint64_t __emupac_pacda_impl(uint64_t ptr,101uint64_t disc) {102if (pac_supported()) {103__asm__ __volatile__(".arch_extension pauth\npacda %0, %1"104: "+r"(ptr)105: "r"(disc));106return ptr;107}108if (ptr & ttbr1_mask) {109if ((ptr & pac_mask) != pac_mask) {110return ptr | pac_mask;111}112} else {113if (ptr & pac_mask) {114return ptr & ~pac_mask;115}116}117uint64_t hash;118siphash<1, 3>(reinterpret_cast<uint8_t *>(&ptr), 8, emu_da_key,119*reinterpret_cast<uint8_t (*)[8]>(&hash));120return (ptr & ~pac_mask) | (hash & pac_mask);121}122123// clang-format off124__asm__(125".globl " ASM_SYMBOL(__emupac_pacda) "\n"126ASM_SYMBOL(__emupac_pacda) ":\n"127FRAME_POINTER_WRAP(__emupac_pacda_impl)128);129// clang-format on130131extern "C" [[gnu::flatten]] uint64_t __emupac_autda_impl(uint64_t ptr,132uint64_t disc) {133if (pac_supported()) {134__asm__ __volatile__(".arch_extension pauth\nautda %0, %1"135: "+r"(ptr)136: "r"(disc));137return ptr;138}139uint64_t ptr_without_pac =140(ptr & ttbr1_mask) ? (ptr | pac_mask) : (ptr & ~pac_mask);141uint64_t hash;142siphash<1, 3>(reinterpret_cast<uint8_t *>(&ptr_without_pac), 8, emu_da_key,143*reinterpret_cast<uint8_t (*)[8]>(&hash));144if (((ptr & ~pac_mask) | (hash & pac_mask)) != ptr) {145__builtin_trap();146}147return ptr_without_pac;148}149150// clang-format off151__asm__(152".globl " ASM_SYMBOL(__emupac_autda) "\n"153ASM_SYMBOL(__emupac_autda) ":\n"154FRAME_POINTER_WRAP(__emupac_autda_impl)155);156// clang-format on157158159