122 lines · c
1// REQUIRES: librt_has_emupac2// RUN: %clang_builtins %s %librt -o %t3// RUN: %run %t 14// RUN: %run %t 25// RUN: %expect_crash %run %t 36// RUN: %expect_crash %run %t 47 8#include <stdbool.h>9#include <stdint.h>10#include <stdio.h>11#include <stdlib.h>12 13uint64_t __emupac_pacda(uint64_t ptr, uint64_t disc);14uint64_t __emupac_autda(uint64_t ptr, uint64_t disc);15 16static bool pac_supported() {17 register uintptr_t x30 __asm__("x30") = 1ULL << 55;18 __asm__ __volatile__("xpaclri" : "+r"(x30));19 return x30 & (1ULL << 54);20}21 22static bool fpac_supported(uint64_t ap) {23 // The meaning of values larger than 6 is reserved as of July 2025; in theory24 // larger values could mean that FEAT_FPAC is not implemented.25 return ap == 4 || ap == 5 || ap == 6;26}27 28// The crash tests would fail to crash (causing the test to fail) if:29// - The operating system did not enable the DA key, or30// - The CPU supports FEAT_PAuth but not FEAT_FPAC.31// Therefore, they call this function, which will crash the test process if one32// of these cases is detected so that %expect_crash detects the crash and causes33// the test to pass.34//35// We detect the former case by attempting to sign a pointer. If the signed36// pointer is equal to the unsigned pointer, DA is likely disabled, so we crash.37//38// We detect the latter case by reading ID_AA64ISAR1_EL1 and ID_AA64ISAR2_EL1.39// It is expected that the operating system will either trap and emulate reading40// the system registers (as Linux does) or crash the process. In the41// trap/emulate case we check the APA, API and APA3 fields for FEAT_FPAC support42// and crash if it is not available. In the crash case we will crash when43// reading the register leading to a passing test. This means that operating44// systems with the crashing behavior do not support the crash tests.45static void crash_if_crash_tests_unsupported() {46 if (!pac_supported())47 return;48 49 uint64_t ptr = 0;50 __asm__ __volatile__(".arch_extension pauth\npacda %0, %1"51 : "+r"(ptr)52 : "r"(0ul));53 if (ptr == 0)54 __builtin_trap();55 56 uint64_t aa64isar1;57 __asm__ __volatile__("mrs %0, id_aa64isar1_el1" : "=r"(aa64isar1));58 uint64_t apa = (aa64isar1 >> 4) & 0xf;59 uint64_t api = (aa64isar1 >> 8) & 0xf;60 if (fpac_supported(apa) || fpac_supported(api))61 return;62 63 uint64_t aa64isar2;64 __asm__ __volatile__("mrs %0, id_aa64isar2_el1" : "=r"(aa64isar2));65 uint64_t apa3 = (aa64isar2 >> 12) & 0xf;66 if (fpac_supported(apa3))67 return;68 69 __builtin_trap();70}71 72int main(int argc, char **argv) {73 char stack_object1;74 uint64_t ptr1 = (uint64_t)&stack_object1;75 76 char stack_object2;77 uint64_t ptr2 = (uint64_t)&stack_object2;78 79 switch (atoi(argv[1])) {80 case 1: {81 // Normal case: test that a pointer authenticated with the same82 // discriminator is equal to the original pointer.83 uint64_t signed_ptr = __emupac_pacda(ptr1, ptr2);84 uint64_t authed_ptr = __emupac_autda(signed_ptr, ptr2);85 if (authed_ptr != ptr1) {86 printf("0x%lx != 0x%lx\n", authed_ptr, ptr1);87 return 1;88 }89 break;90 }91 case 2: {92 // Test that negative addresses (addresses controlled by TTBR1,93 // conventionally kernel addresses) can be signed and authenticated.94 uint64_t unsigned_ptr = -1ULL;95 uint64_t signed_ptr = __emupac_pacda(unsigned_ptr, ptr2);96 uint64_t authed_ptr = __emupac_autda(signed_ptr, ptr2);97 if (authed_ptr != unsigned_ptr) {98 printf("0x%lx != 0x%lx\n", authed_ptr, unsigned_ptr);99 return 1;100 }101 break;102 }103 case 3: {104 crash_if_crash_tests_unsupported();105 // Test that a corrupted signature crashes the program.106 uint64_t signed_ptr = __emupac_pacda(ptr1, ptr2);107 __emupac_autda(signed_ptr + (1ULL << 48), ptr2);108 break;109 }110 case 4: {111 crash_if_crash_tests_unsupported();112 // Test that signing a pointer with signature bits already set produces a pointer113 // that would fail auth.114 uint64_t signed_ptr = __emupac_pacda(ptr1 + (1ULL << 48), ptr2);115 __emupac_autda(signed_ptr, ptr2);116 break;117 }118 }119 120 return 0;121}122