diff --git a/compiler-rt/cmake/Modules/AddCompilerRT.cmake b/compiler-rt/cmake/Modules/AddCompilerRT.cmake index d346b0ec01b03..b7e105812a98f 100644 --- a/compiler-rt/cmake/Modules/AddCompilerRT.cmake +++ b/compiler-rt/cmake/Modules/AddCompilerRT.cmake @@ -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 @@ -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}} diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake index cbb43a5958d2f..e129cc80c885d 100644 --- a/compiler-rt/cmake/builtin-config-ix.cmake +++ b/compiler-rt/cmake/builtin-config-ix.cmake @@ -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 " diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt index 5efc4ab0e85bc..0de5c33f7119a 100644 --- a/compiler-rt/lib/builtins/CMakeLists.txt +++ b/compiler-rt/lib/builtins/CMakeLists.txt @@ -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) @@ -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 @@ -589,6 +591,7 @@ set(aarch64_SOURCES ${GENERIC_TF_SOURCES} ${GENERIC_SOURCES} cpu_model/aarch64.c + aarch64/emupac.cpp aarch64/fp_mode.c ) @@ -826,7 +829,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 @@ -944,6 +947,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 () diff --git a/compiler-rt/lib/builtins/aarch64/emupac.cpp b/compiler-rt/lib/builtins/aarch64/emupac.cpp new file mode 100644 index 0000000000000..8343a8d34cce6 --- /dev/null +++ b/compiler-rt/lib/builtins/aarch64/emupac.cpp @@ -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 + +#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(&ptr), 8, K, + *reinterpret_cast(&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(&ptr_without_pac), 8, K, + *reinterpret_cast(&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)); +} diff --git a/compiler-rt/test/builtins/Unit/aarch64/emupac.c b/compiler-rt/test/builtins/Unit/aarch64/emupac.c new file mode 100644 index 0000000000000..d4f3aecc8eca7 --- /dev/null +++ b/compiler-rt/test/builtins/Unit/aarch64/emupac.c @@ -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 +#include +#include + +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; +} diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/BUILD.gn b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/BUILD.gn index 44f5fdc20837c..2e7aa45f38e3e 100644 --- a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/BUILD.gn +++ b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/BUILD.gn @@ -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. diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni index ba151075c0f9d..bba2a4e891aa6 100644 --- a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni +++ b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni @@ -429,6 +429,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", ] diff --git a/llvm/utils/gn/secondary/compiler-rt/test/builtins/BUILD.gn b/llvm/utils/gn/secondary/compiler-rt/test/builtins/BUILD.gn index 87848075a804e..97e4fdf61ec2d 100644 --- a/llvm/utils/gn/secondary/compiler-rt/test/builtins/BUILD.gn +++ b/llvm/utils/gn/secondary/compiler-rt/test/builtins/BUILD.gn @@ -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)", ] } }