- 
                Notifications
    You must be signed in to change notification settings 
- Fork 15k
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
          
     Merged
      
      
            pcc
  merged 21 commits into
  main
from
users/pcc/spr/compiler-rt-introduce-runtime-functions-for-emulated-pac
  
      
      
   
  Jul 9, 2025 
      
    
  
     Merged
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            21 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      270fbad
              
                [𝘀𝗽𝗿] changes to main this commit is based on
              
              
                pcc 2397a45
              
                [𝘀𝗽𝗿] initial version
              
              
                pcc f2e0ad9
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc 84a0613
              
                Switch to SipHash
              
              
                pcc b37a44f
              
                Fix CMake build
              
              
                pcc 55b1cd0
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc 66b7ed2
              
                Add test
              
              
                pcc df9ceb5
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc 48031d4
              
                Rebase
              
              
                pcc 571b814
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc b4a4147
              
                Rebase
              
              
                pcc d54bb1f
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc f3d5547
              
                Rebase
              
              
                pcc 9f9519e
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc 7915d67
              
                Rebase
              
              
                pcc 9fe55fe
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc e59848b
              
                Address comments
              
              
                pcc 6881552
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc baf191f
              
                Rebase
              
              
                pcc 22e9f15
              
                [𝘀𝗽𝗿] changes introduced through rebase
              
              
                pcc 96acb2f
              
                Address review comment
              
              
                pcc File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| //===--- 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. | ||
| // | ||
| // EmuPAC should not be used in production and is only intended for testing use | ||
| // cases. This is not only because of the performance costs, which will exist | ||
| // even on PAC-supporting machines because of the function call overhead for | ||
| // each sign/auth operation, but because it provides weaker security compared to | ||
| // real PAC: the key is constant and public, which means that we do not mix a | ||
| // global secret. | ||
| // | ||
| // 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 max_va_size = 48; | ||
| const uint64_t pac_mask = ((1ULL << 55) - 1) & ~((1ULL << max_va_size) - 1); | ||
| const uint64_t ttbr1_mask = 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() { | ||
|         
                  atrosinenko marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| 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) \ | ||
| CFI_INST(".cfi_startproc\n") \ | ||
| "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\n" \ | ||
| CFI_INST(".cfi_endproc\n") | ||
| // clang-format on | ||
|  | ||
| // Emulated DA key value. | ||
| static const uint8_t emu_da_key[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, | ||
| 0x4a, 0x79, 0x6f, 0xec, 0x8b, 0x1b, | ||
| 0x42, 0x87, 0x81, 0xd4}; | ||
|  | ||
| extern "C" [[gnu::flatten]] 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 & ttbr1_mask) { | ||
| if ((ptr & pac_mask) != pac_mask) { | ||
| return ptr | pac_mask; | ||
| } | ||
| } else { | ||
| if (ptr & pac_mask) { | ||
| return ptr & ~pac_mask; | ||
| } | ||
| } | ||
| uint64_t hash; | ||
| siphash<1, 3>(reinterpret_cast<uint8_t *>(&ptr), 8, emu_da_key, | ||
| *reinterpret_cast<uint8_t(*)[8]>(&hash)); | ||
| return (ptr & ~pac_mask) | (hash & pac_mask); | ||
| } | ||
|  | ||
| __asm__(".globl __emupac_pacda\n" | ||
| "__emupac_pacda:\n" FRAME_POINTER_WRAP(__emupac_pacda_impl)); | ||
|  | ||
| extern "C" [[gnu::flatten]] 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 = | ||
|         
                  atrosinenko marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| (ptr & ttbr1_mask) ? (ptr | pac_mask) : (ptr & ~pac_mask); | ||
| uint64_t hash; | ||
| siphash<1, 3>(reinterpret_cast<uint8_t *>(&ptr_without_pac), 8, emu_da_key, | ||
| *reinterpret_cast<uint8_t(*)[8]>(&hash)); | ||
| if (((ptr & ~pac_mask) | (hash & pac_mask)) != ptr) { | ||
| __builtin_trap(); | ||
| } | ||
| return ptr_without_pac; | ||
| } | ||
|  | ||
| __asm__(".globl __emupac_autda\n" | ||
| "__emupac_autda:\n" FRAME_POINTER_WRAP(__emupac_autda_impl)); | ||
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | 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 (addresses 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; | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.