Skip to content

compiler-rt: Introduce runtime functions for emulated PAC. #133530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: users/pcc/spr/main.compiler-rt-introduce-runtime-functions-for-emulated-pac
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion compiler-rt/cmake/Modules/AddCompilerRT.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ function(add_compiler_rt_runtime name type)
cmake_parse_arguments(LIB
""
"PARENT_TARGET"
"OS;ARCHS;SOURCES;CFLAGS;LINK_FLAGS;DEFS;DEPS;LINK_LIBS;OBJECT_LIBS;ADDITIONAL_HEADERS;EXTENSIONS"
"OS;ARCHS;SOURCES;CFLAGS;LINK_FLAGS;DEFS;DEPS;LINK_LIBS;OBJECT_LIBS;ADDITIONAL_HEADERS;EXTENSIONS;C_STANDARD;CXX_STANDARD"
${ARGN})
set(libnames)
# Until we support this some other way, build compiler-rt runtime without LTO
Expand Down Expand Up @@ -360,6 +360,12 @@ function(add_compiler_rt_runtime name type)
set_target_link_flags(${libname} ${extra_link_flags_${libname}})
set_property(TARGET ${libname} APPEND PROPERTY
COMPILE_DEFINITIONS ${LIB_DEFS})
if(LIB_C_STANDARD)
set_property(TARGET ${libname} PROPERTY C_STANDARD ${LIB_C_STANDARD})
endif()
if(LIB_CXX_STANDARD)
set_property(TARGET ${libname} PROPERTY CXX_STANDARD ${LIB_CXX_STANDARD})
endif()
set_target_output_directories(${libname} ${output_dir_${libname}})
install(TARGETS ${libname}
ARCHIVE DESTINATION ${install_dir_${libname}}
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/cmake/builtin-config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ builtin_check_c_compiler_flag("-Xclang -mcode-object-version=none" COMPILER_RT_H
builtin_check_c_compiler_flag(-Wbuiltin-declaration-mismatch COMPILER_RT_HAS_WBUILTIN_DECLARATION_MISMATCH_FLAG)
builtin_check_c_compiler_flag(/Zl COMPILER_RT_HAS_ZL_FLAG)
builtin_check_c_compiler_flag(-fcf-protection=full COMPILER_RT_HAS_FCF_PROTECTION_FLAG)
builtin_check_c_compiler_flag(-nostdinc++ COMPILER_RT_HAS_NOSTDINCXX_FLAG)

builtin_check_c_compiler_source(COMPILER_RT_HAS_ATOMIC_KEYWORD
"
Expand Down
9 changes: 7 additions & 2 deletions compiler-rt/lib/builtins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
cmake_minimum_required(VERSION 3.20.0)

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
project(CompilerRTBuiltins C ASM)
project(CompilerRTBuiltins C CXX ASM)
set(COMPILER_RT_STANDALONE_BUILD TRUE)
set(COMPILER_RT_BUILTINS_STANDALONE_BUILD TRUE)

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

include_directories(../../../third-party/siphash/include)

# TODO: Need to add a mechanism for logging errors when builtin source files are
# added to a sub-directory and not this CMakeLists file.
set(GENERIC_SOURCES
Expand Down Expand Up @@ -570,6 +572,7 @@ set(aarch64_SOURCES
${GENERIC_TF_SOURCES}
${GENERIC_SOURCES}
cpu_model/aarch64.c
aarch64/emupac.cpp
aarch64/fp_mode.c
)

Expand Down Expand Up @@ -802,7 +805,7 @@ else ()
append_list_if(COMPILER_RT_ENABLE_CET -fcf-protection=full BUILTIN_CFLAGS)
endif()

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

# Don't embed directives for picking any specific CRT
Expand Down Expand Up @@ -919,6 +922,8 @@ else ()
SOURCES ${${arch}_SOURCES}
DEFS ${BUILTIN_DEFS}
CFLAGS ${BUILTIN_CFLAGS_${arch}}
C_STANDARD 11
CXX_STANDARD 17
PARENT_TARGET builtins)
cmake_pop_check_state()
endif ()
Expand Down
133 changes: 133 additions & 0 deletions compiler-rt/lib/builtins/aarch64/emupac.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//===--- emupac.cpp - Emulated PAC implementation -------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements Emulated PAC using SipHash_1_3 as the IMPDEF hashing
// scheme.
//
//===----------------------------------------------------------------------===//

#include <stdint.h>

#include "siphash/SipHash.h"

// EmuPAC implements runtime emulation of PAC instructions. If the current
// CPU supports PAC, EmuPAC uses real PAC instructions. Otherwise, it uses the
// emulation, which is effectively an implementation of PAC with an IMPDEF
// hashing scheme based on SipHash_1_3.
//
// The purpose of the emulation is to allow programs to be built to be portable
// to machines without PAC support, with some performance loss and increased
// probability of false positives (due to not being able to portably determine
// the VA size), while being functionally almost equivalent to running on a
// machine with PAC support. One example of a use case is if PAC is used in
// production as a security mitigation, but the testing environment is
// heterogeneous (i.e. some machines lack PAC support). In this case we would
// like the testing machines to be able to detect issues resulting
// from the use of PAC instructions that would affect production by running
// tests. This can be achieved by building test binaries with EmuPAC and
// production binaries with real PAC.
//
// The emulation assumes that the VA size is at most 48 bits. The architecture
// as of ARMv8.2, which was the last architecture version in which PAC was not
// mandatory, permitted VA size up to 52 bits via ARMv8.2-LVA, but we are
// unaware of an ARMv8.2 CPU that implemented ARMv8.2-LVA.

const uint64_t kMaxVASize = 48;
const uint64_t kPACMask = ((1ULL << 55) - 1) & ~((1ULL << kMaxVASize) - 1);
const uint64_t kTTBR1Mask = 1ULL << 55;

// Determine whether PAC is supported without accessing memory. This utilizes
// the XPACLRI instruction which will copy bit 55 of x30 into at least bit 54 if
// PAC is supported and acts as a NOP if PAC is not supported.
static bool pac_supported() {
register uintptr_t x30 __asm__("x30") = 1ULL << 55;
__asm__ __volatile__("xpaclri" : "+r"(x30));
return x30 & (1ULL << 54);
}

// This asm snippet is used to force the creation of a frame record when
// calling the EmuPAC functions. This is important because the EmuPAC functions
// may crash if an auth failure is detected and may be unwound past using a
// frame pointer based unwinder.
#ifdef __GCC_HAVE_DWARF2_CFI_ASM
#define CFI_INST(inst) inst
#else
#define CFI_INST(inst)
#endif

// clang-format off
#define FRAME_POINTER_WRAP(sym) \
"stp x29, x30, [sp, #-16]!\n" \
CFI_INST(".cfi_def_cfa_offset 16\n") \
"mov x29, sp\n" \
CFI_INST(".cfi_def_cfa w29, 16\n") \
CFI_INST(".cfi_offset w30, -8\n") \
CFI_INST(".cfi_offset w29, -16\n") \
"bl " #sym "\n" \
CFI_INST(".cfi_def_cfa wsp, 16\n") \
"ldp x29, x30, [sp], #16\n" \
CFI_INST(".cfi_def_cfa_offset 0\n") \
CFI_INST(".cfi_restore w30\n") \
CFI_INST(".cfi_restore w29\n") \
"ret"
// clang-format on

static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79,
0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4};

__attribute__((flatten)) extern "C" uint64_t
__emupac_pacda_impl(uint64_t ptr, uint64_t disc) {
if (pac_supported()) {
__asm__ __volatile__(".arch_extension pauth\npacda %0, %1"
: "+r"(ptr)
: "r"(disc));
return ptr;
}
if (ptr & kTTBR1Mask) {
if ((ptr & kPACMask) != kPACMask) {
return ptr | kPACMask;
}
} else {
if (ptr & kPACMask) {
return ptr & ~kPACMask;
}
}
uint64_t hash;
siphash<1, 3>(reinterpret_cast<uint8_t *>(&ptr), 8, K,
*reinterpret_cast<uint8_t(*)[8]>(&hash));
return (ptr & ~kPACMask) | (hash & kPACMask);
}

extern "C" __attribute__((naked)) uint64_t __emupac_pacda(uint64_t ptr,
uint64_t disc) {
__asm__(FRAME_POINTER_WRAP(__emupac_pacda_impl));
}

__attribute__((flatten)) extern "C" uint64_t
__emupac_autda_impl(uint64_t ptr, uint64_t disc) {
if (pac_supported()) {
__asm__ __volatile__(".arch_extension pauth\nautda %0, %1"
: "+r"(ptr)
: "r"(disc));
return ptr;
}
uint64_t ptr_without_pac =
(ptr & kTTBR1Mask) ? (ptr | kPACMask) : (ptr & ~kPACMask);
uint64_t hash;
siphash<1, 3>(reinterpret_cast<uint8_t *>(&ptr_without_pac), 8, K,
*reinterpret_cast<uint8_t(*)[8]>(&hash));
if (((ptr & ~kPACMask) | (hash & kPACMask)) != ptr) {
__builtin_trap();
}
return ptr_without_pac;
}

extern "C" __attribute__((naked)) uint64_t __emupac_autda(uint64_t ptr,
uint64_t disc) {
__asm__(FRAME_POINTER_WRAP(__emupac_autda_impl));
}
62 changes: 62 additions & 0 deletions compiler-rt/test/builtins/Unit/aarch64/emupac.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// REQUIRES: librt_has_emupac
// RUN: %clang_builtins %s %librt -o %t
// RUN: %run %t 1
// RUN: %run %t 2
// RUN: %expect_crash %run %t 3
// RUN: %expect_crash %run %t 4

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

uint64_t __emupac_pacda(uint64_t ptr, uint64_t disc);
uint64_t __emupac_autda(uint64_t ptr, uint64_t disc);

int main(int argc, char **argv) {
char stack_object1;
uint64_t ptr1 = (uint64_t)stack_object1;

char stack_object2;
uint64_t ptr2 = (uint64_t)stack_object2;

switch (atoi(argv[1])) {
case 1: {
// Normal case: test that a pointer authenticated with the same
// discriminator is equal to the original pointer.
uint64_t signed_ptr = __emupac_pacda(ptr1, ptr2);
uint64_t authed_ptr = __emupac_autda(signed_ptr, ptr2);
if (authed_ptr != ptr1) {
printf("0x%lx != 0x%lx\n", authed_ptr, ptr1);
return 1;
}
break;
}
case 2: {
// Test that negative addresses (addressses controlled by TTBR1,
// conventionally kernel addresses) can be signed and authenticated.
uint64_t unsigned_ptr = -1ULL;
uint64_t signed_ptr = __emupac_pacda(unsigned_ptr, ptr2);
uint64_t authed_ptr = __emupac_autda(signed_ptr, ptr2);
if (authed_ptr != unsigned_ptr) {
printf("0x%lx != 0x%lx\n", authed_ptr, unsigned_ptr);
return 1;
}
break;
}
case 3: {
// Test that a corrupted signature crashes the program.
uint64_t signed_ptr = __emupac_pacda(ptr1, ptr2);
__emupac_autda(signed_ptr + (1ULL << 48), ptr2);
break;
}
case 4: {
// Test that signing a pointer with signature bits already set produces a pointer
// that would fail auth.
uint64_t signed_ptr = __emupac_pacda(ptr1 + (1ULL << 48), ptr2);
__emupac_autda(signed_ptr, ptr2);
break;
}
}

return 0;
}
2 changes: 2 additions & 0 deletions llvm/utils/gn/secondary/compiler-rt/lib/builtins/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,14 @@ static_library("builtins") {
cflags += [ "-fomit-frame-pointer" ]
}
cflags_c = [ "-std=c11" ]
cflags_cc = [ "-nostdinc++" ]
}

defines = builtins_defines
sources = builtins_sources

deps = lse_targets
include_dirs = [ "//third-party/siphash/include" ]
}

# Currently unused but necessary to make sync_source_lists_from_cmake.py happy.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ if (current_cpu == "arm") {
if (current_cpu == "arm64") {
builtins_sources -= [ "fp_mode.c" ]
builtins_sources += [
"aarch64/emupac.cpp",
"aarch64/fp_mode.c",
"cpu_model/aarch64.c",
]
Expand Down
1 change: 1 addition & 0 deletions llvm/utils/gn/secondary/compiler-rt/test/builtins/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ if (current_toolchain != host_toolchain) {
"//compiler-rt/include($host_toolchain)",
"//compiler-rt/lib/builtins",
"//compiler-rt/test:lit_common_configured",
"//llvm/utils/not($host_toolchain)",
]
}
}
Expand Down
Loading