brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.8 KiB · 48f21a5 Raw
158 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9//  This file implements Emulated PAC using SipHash_1_3 as the IMPDEF hashing10//  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 current19// CPU supports PAC, EmuPAC uses real PAC instructions. Otherwise, it uses the20// emulation, which is effectively an implementation of PAC with an IMPDEF21// hashing scheme based on SipHash_1_3.22//23// The purpose of the emulation is to allow programs to be built to be portable24// to machines without PAC support, with some performance loss and increased25// probability of false positives (due to not being able to portably determine26// the VA size), while being functionally almost equivalent to running on a27// machine with PAC support. One example of a use case is if PAC is used in28// production as a security mitigation, but the testing environment is29// heterogeneous (i.e. some machines lack PAC support). In this case we would30// like the testing machines to be able to detect issues resulting31// from the use of PAC instructions that would affect production by running32// tests. This can be achieved by building test binaries with EmuPAC and33// production binaries with real PAC.34//35// EmuPAC should not be used in production and is only intended for testing use36// cases. This is not only because of the performance costs, which will exist37// even on PAC-supporting machines because of the function call overhead for38// each sign/auth operation, but because it provides weaker security compared to39// real PAC: the key is constant and public, which means that we do not mix a40// global secret.41//42// The emulation assumes that the VA size is at most 48 bits. The architecture43// as of ARMv8.2, which was the last architecture version in which PAC was not44// mandatory, permitted VA size up to 52 bits via ARMv8.2-LVA, but we are45// unaware of an ARMv8.2 CPU that implemented ARMv8.2-LVA.46 47static const uint64_t max_va_size = 48;48static const uint64_t pac_mask =49    ((1ULL << 55) - 1) & ~((1ULL << max_va_size) - 1);50static const uint64_t ttbr1_mask = 1ULL << 55;51 52// Determine whether PAC is supported without accessing memory. This utilizes53// the XPACLRI instruction which will copy bit 55 of x30 into at least bit 54 if54// PAC is supported and acts as a NOP if PAC is not supported.55static bool pac_supported() {56  register uintptr_t x30 __asm__("x30") = 1ULL << 55;57  __asm__ __volatile__("xpaclri" : "+r"(x30));58  return x30 & (1ULL << 54);59}60 61#ifdef __GCC_HAVE_DWARF2_CFI_ASM62#define CFI_INST(inst) inst63#else64#define CFI_INST(inst)65#endif66 67#ifdef __APPLE__68#define ASM_SYMBOL(symbol) "_" #symbol69#else70#define ASM_SYMBOL(symbol) #symbol71#endif72 73// This asm snippet is used to force the creation of a frame record when74// calling the EmuPAC functions. This is important because the EmuPAC functions75// may crash if an auth failure is detected and may be unwound past using a76// frame pointer based unwinder.77// clang-format off78#define FRAME_POINTER_WRAP(sym) \79  CFI_INST(".cfi_startproc\n") \80  "stp x29, x30, [sp, #-16]!\n" \81  CFI_INST(".cfi_def_cfa_offset 16\n") \82  "mov x29, sp\n" \83  CFI_INST(".cfi_def_cfa w29, 16\n") \84  CFI_INST(".cfi_offset w30, -8\n") \85  CFI_INST(".cfi_offset w29, -16\n") \86  "bl " ASM_SYMBOL(sym) "\n" \87  CFI_INST(".cfi_def_cfa wsp, 16\n") \88  "ldp x29, x30, [sp], #16\n" \89  CFI_INST(".cfi_def_cfa_offset 0\n") \90  CFI_INST(".cfi_restore w30\n") \91  CFI_INST(".cfi_restore w29\n") \92  "ret\n" \93  CFI_INST(".cfi_endproc\n")94// clang-format on95 96// Emulated DA key value.97static const uint8_t emu_da_key[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10,98                                       0x4a, 0x79, 0x6f, 0xec, 0x8b, 0x1b,99                                       0x42, 0x87, 0x81, 0xd4};100 101extern "C" [[gnu::flatten]] uint64_t __emupac_pacda_impl(uint64_t ptr,102                                                         uint64_t disc) {103  if (pac_supported()) {104    __asm__ __volatile__(".arch_extension pauth\npacda %0, %1"105                         : "+r"(ptr)106                         : "r"(disc));107    return ptr;108  }109  if (ptr & ttbr1_mask) {110    if ((ptr & pac_mask) != pac_mask) {111      return ptr | pac_mask;112    }113  } else {114    if (ptr & pac_mask) {115      return ptr & ~pac_mask;116    }117  }118  uint64_t hash;119  siphash<1, 3>(reinterpret_cast<uint8_t *>(&ptr), 8, emu_da_key,120                *reinterpret_cast<uint8_t (*)[8]>(&hash));121  return (ptr & ~pac_mask) | (hash & pac_mask);122}123 124// clang-format off125__asm__(126  ".globl " ASM_SYMBOL(__emupac_pacda) "\n"127  ASM_SYMBOL(__emupac_pacda) ":\n"128  FRAME_POINTER_WRAP(__emupac_pacda_impl)129);130// clang-format on131 132extern "C" [[gnu::flatten]] uint64_t __emupac_autda_impl(uint64_t ptr,133                                                         uint64_t disc) {134  if (pac_supported()) {135    __asm__ __volatile__(".arch_extension pauth\nautda %0, %1"136                         : "+r"(ptr)137                         : "r"(disc));138    return ptr;139  }140  uint64_t ptr_without_pac =141      (ptr & ttbr1_mask) ? (ptr | pac_mask) : (ptr & ~pac_mask);142  uint64_t hash;143  siphash<1, 3>(reinterpret_cast<uint8_t *>(&ptr_without_pac), 8, emu_da_key,144                *reinterpret_cast<uint8_t (*)[8]>(&hash));145  if (((ptr & ~pac_mask) | (hash & pac_mask)) != ptr) {146    __builtin_trap();147  }148  return ptr_without_pac;149}150 151// clang-format off152__asm__(153  ".globl " ASM_SYMBOL(__emupac_autda) "\n"154  ASM_SYMBOL(__emupac_autda) ":\n"155  FRAME_POINTER_WRAP(__emupac_autda_impl)156);157// clang-format on158