1// REQUIRES: librt_has_emupac
2// RUN: %clang_builtins %s %librt -o %t
3// RUN: %run %t 1
4// RUN: %run %t 2
5// RUN: %expect_crash %run %t 3
6// RUN: %expect_crash %run %t 4
7
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 theory
24 // 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, or
30// - The CPU supports FEAT_PAuth but not FEAT_FPAC.
31// Therefore, they call this function, which will crash the test process if one
32// of these cases is detected so that %expect_crash detects the crash and causes
33// the test to pass.
34//
35// We detect the former case by attempting to sign a pointer. If the signed
36// 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 reading
40// the system registers (as Linux does) or crash the process. In the
41// trap/emulate case we check the APA, API and APA3 fields for FEAT_FPAC support
42// and crash if it is not available. In the crash case we will crash when
43// reading the register leading to a passing test. This means that operating
44// 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(ap: apa) || fpac_supported(ap: 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(ap: 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(nptr: argv[1])) {
80 case 1: {
81 // Normal case: test that a pointer authenticated with the same
82 // discriminator is equal to the original pointer.
83 uint64_t signed_ptr = __emupac_pacda(ptr: ptr1, disc: ptr2);
84 uint64_t authed_ptr = __emupac_autda(ptr: signed_ptr, disc: ptr2);
85 if (authed_ptr != ptr1) {
86 printf(format: "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(ptr: unsigned_ptr, disc: ptr2);
96 uint64_t authed_ptr = __emupac_autda(ptr: signed_ptr, disc: ptr2);
97 if (authed_ptr != unsigned_ptr) {
98 printf(format: "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(ptr: ptr1, disc: ptr2);
107 __emupac_autda(ptr: signed_ptr + (1ULL << 48), disc: 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 pointer
113 // that would fail auth.
114 uint64_t signed_ptr = __emupac_pacda(ptr: ptr1 + (1ULL << 48), disc: ptr2);
115 __emupac_autda(ptr: signed_ptr, disc: ptr2);
116 break;
117 }
118 }
119
120 return 0;
121}
122

source code of compiler-rt/test/builtins/Unit/aarch64/emupac.c