xref: /aosp_15_r20/trusty/kernel/app/pactest/pactest.c (revision 344aa361028b423587d4ef3fa52a23d194628137)
1 /*
2  * Copyright (c) 2022, Google Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 /*
25  * Tests for ARM64 FEAT_Puth (Pointer Authentication Codes)
26  * This is a CPU feature at ARM-A v8.3
27  * Since this is an ARM64 feature, this should not be built for other arches.
28  */
29 #ifndef ARCH_ARM64
30 #error PAC is an ARM64 feature
31 #endif
32 
33 #include "pactest.h"
34 #include <arch/arm64/sregs.h>
35 #include <arch/ops.h>
36 #include <err.h>
37 #include <lib/unittest/unittest.h>
38 #include <lk/init.h>
39 #include <platform/random.h>
40 #include <stdio.h>
41 
42 #define MASK(bits) ((1ull << (bits)) - 1)
43 
44 /*
45  * Test addresses for each translation table (TT).
46  * Address bit 55 is used to select between page translation tables to use.
47  * TT0 is used for user addresses, while TT1 is kernel addresses.
48  */
49 #define PACTEST_TT0_ADDRESS \
50     (0x1234567890abcdefu & MASK(MMU_USER_SIZE_SHIFT)) & ~(1ull << 55)
51 #define PACTEST_TT1_ADDRESS \
52     (0x1234567890abcdefu | (~0ull << MMU_KERNEL_SIZE_SHIFT)) | (1ull << 55)
53 #define PACTEST_MODIFIER 0xfedcba0987654321u
54 
55 /* Helper function for parameterized calling of specific PAC instructions */
pacxx(bool instr_not_data,bool key_a_not_b,uint64_t address,uint64_t modifier)56 static uint64_t pacxx(bool instr_not_data,
57                       bool key_a_not_b,
58                       uint64_t address,
59                       uint64_t modifier) {
60     if (key_a_not_b) {
61         if (instr_not_data) {
62             __asm__(".arch_extension pauth\n"
63                     "\tPACIA %0, %1"
64                     : "+r"(address)
65                     : "r"(modifier));
66         } else {
67             __asm__(".arch_extension pauth\n"
68                     "\tPACDA %0, %1"
69                     : "+r"(address)
70                     : "r"(modifier));
71         }
72     } else {
73         if (instr_not_data) {
74             __asm__(".arch_extension pauth\n"
75                     "\tPACIB %0, %1"
76                     : "+r"(address)
77                     : "r"(modifier));
78         } else {
79             __asm__(".arch_extension pauth\n"
80                     "\tPACDB %0, %1"
81                     : "+r"(address)
82                     : "r"(modifier));
83         }
84     }
85 
86     return address;
87 }
88 
89 /*
90  * Helper function for parameterized calling of specific PAC instructions.
91  * The instructions are implemented in assembly as they may generate exceptions
92  * (FEAT_FPAC) which need catching.
93  */
autxx(bool instr_not_data,bool key_a_not_b,uint64_t address,uint64_t modifier,uint64_t * result)94 static int autxx(bool instr_not_data,
95                  bool key_a_not_b,
96                  uint64_t address,
97                  uint64_t modifier,
98                  uint64_t* result) {
99     if (key_a_not_b) {
100         if (instr_not_data) {
101             return pactest_autia(address, modifier, result);
102         } else {
103             return pactest_autda(address, modifier, result);
104         }
105     } else {
106         if (instr_not_data) {
107             return pactest_autib(address, modifier, result);
108         } else {
109             return pactest_autdb(address, modifier, result);
110         }
111     }
112 }
113 
get_nibble(uint64_t reg,uint8_t shift)114 static uint8_t get_nibble(uint64_t reg, uint8_t shift) {
115     return (reg >> shift) & 0xf;
116 }
117 
get_pac_features(uint8_t nibble)118 static const char* get_pac_features(uint8_t nibble) {
119     switch (nibble) {
120     case 0b0001:
121         return "PAuth";
122     case 0b0010:
123         return "PAuth + EPAC";
124     case 0b0011:
125         return "PAuth + PAuth2";
126     case 0b0100:
127         return "Pauth + Pauth2 + FPAC";
128     case 0b0101:
129         return "Pauth + Pauth2 + FPAC + FPACCOMBINE";
130     }
131 
132     return "<unknown>";
133 }
134 
TEST(pactest,pauth_supported)135 TEST(pactest, pauth_supported) {
136     if (!arch_pac_address_supported()) {
137         trusty_unittest_printf("[   INFO   ] PAuth is not supported\n");
138         GTEST_SKIP();
139     }
140 
141     const uint64_t isar1 = ARM64_READ_SYSREG(id_aa64isar1_el1);
142     const uint64_t isar2 = ARM64_READ_SYSREG(id_aa64isar2_el1);
143     const uint8_t pacfrac = get_nibble(isar2, ID_AA64ISAR2_EL1_PAC_FRAC_SHIFT);
144     const uint8_t apa3 = get_nibble(isar2, ID_AA64ISAR2_EL1_APA3_SHIFT);
145     const uint8_t apa = get_nibble(isar1, ID_AA64ISAR1_EL1_APA_SHIFT);
146     const uint8_t api = get_nibble(isar1, ID_AA64ISAR1_EL1_API_SHIFT);
147     const char *algo = "none", *features = "", *cpf = "";
148 
149     if (apa3) {
150         algo = "QARMA3";
151         features = get_pac_features(apa3);
152         EXPECT_EQ(apa, 0);
153         EXPECT_EQ(api, 0);
154     } else if (apa) {
155         algo = "QARMA5";
156         features = get_pac_features(apa);
157         EXPECT_EQ(api, 0);
158     } else if (api) {
159         algo = "implementation defined";
160         features = get_pac_features(api);
161     }
162 
163     if (pacfrac == 1) {
164         cpf = ", CONSTPACFIELD";
165     } else {
166         EXPECT_EQ(pacfrac, 0);
167     }
168 
169     /* Log the support in case later trying to debug a test */
170     trusty_unittest_printf("[   INFO   ] algo: %s\n", algo);
171     trusty_unittest_printf("[   INFO   ] feat: %s%s\n", features, cpf);
172 test_abort:;
173 }
174 
TEST(pactest,fpac_supported)175 TEST(pactest, fpac_supported) {
176     uint64_t val;
177     int rc;
178 
179     if (!arch_pac_exception_supported()) {
180         trusty_unittest_printf("[   INFO   ] FPAC is not supported\n");
181         GTEST_SKIP();
182     }
183 
184     rc = pactest_autia(PACTEST_TT0_ADDRESS, PACTEST_MODIFIER, &val);
185     EXPECT_EQ(rc, ERR_FAULT);
186 test_abort:;
187 }
188 
TEST(pactest,enabled)189 TEST(pactest, enabled) {
190     const uint64_t sctlr_el1 = ARM64_READ_SYSREG(SCTLR_EL1);
191 
192     /* Check the expected keys are enabled */
193     EXPECT_EQ(sctlr_el1 & SCTLR_EL1_ENIA,
194               arch_pac_address_supported() ? SCTLR_EL1_ENIA : 0);
195     EXPECT_EQ(sctlr_el1 & SCTLR_EL1_ENIB, 0);
196     EXPECT_EQ(sctlr_el1 & SCTLR_EL1_ENDA, 0);
197     EXPECT_EQ(sctlr_el1 & SCTLR_EL1_ENDB, 0);
198 }
199 
TEST(pactest,keys)200 TEST(pactest, keys) {
201     if (!arch_pac_address_supported()) {
202         GTEST_SKIP();
203     }
204 
205     const struct packeys* thread_keys = &get_current_thread()->arch.packeys;
206     const uint64_t keyi_lo = ARM64_READ_SYSREG(APIAKeyLo_EL1);
207     const uint64_t keyi_hi = ARM64_READ_SYSREG(APIAKeyHi_EL1);
208 
209     EXPECT_EQ(thread_keys->apia[0], keyi_lo);
210     EXPECT_EQ(thread_keys->apia[1], keyi_hi);
211 
212     /*
213      * Check the keys are neither all 0's of all 1's.
214      * While these values are valid, it may indicate incorrect initialisation.
215      */
216     EXPECT_NE(UINT64_MAX, keyi_lo);
217     EXPECT_NE(UINT64_MAX, keyi_hi);
218     EXPECT_NE(0, keyi_lo);
219     EXPECT_NE(0, keyi_hi);
220 test_abort:;
221 }
222 
223 typedef struct {
224     bool translation_table;
225     bool instr_not_data;
226     bool key_a_not_b;
227     bool key_enabled;
228 } pactest_t;
229 
get_params(pactest_t * p)230 static void get_params(pactest_t* p) {
231     const bool* const* params = GetParam();
232     p->translation_table = *params[0];
233     /* Invert for more logical test ordering: AI, AD, BI, BD */
234     p->instr_not_data = !*params[1];
235     p->key_a_not_b = !*params[2];
236 }
237 
TEST_F_SETUP(pactest)238 TEST_F_SETUP(pactest) {
239     uint64_t key_enabled_bit;
240 
241     get_params(_state);
242 
243     if (_state->instr_not_data) {
244         key_enabled_bit = _state->key_a_not_b ? SCTLR_EL1_ENIA : SCTLR_EL1_ENIB;
245     } else {
246         key_enabled_bit = _state->key_a_not_b ? SCTLR_EL1_ENDA : SCTLR_EL1_ENDB;
247     }
248 
249     _state->key_enabled = ARM64_READ_SYSREG(SCTLR_EL1) & key_enabled_bit;
250 }
251 
TEST_F_TEARDOWN(pactest)252 TEST_F_TEARDOWN(pactest) {}
253 
user_param_to_string(const void * param,char * buf,size_t buf_size)254 static void user_param_to_string(const void* param,
255                                  char* buf,
256                                  size_t buf_size) {
257     pactest_t p;
258     get_params(&p);
259 
260     snprintf(buf, buf_size, "TT%u/%s%s", p.translation_table ? 1 : 0,
261              p.instr_not_data ? "PACI" : "PACD", p.key_a_not_b ? "A" : "B");
262 }
263 
264 INSTANTIATE_TEST_SUITE_P(pac,
265                          pactest,
266                          testing_Combine(testing_Bool(),
267                                          testing_Bool(),
268                                          testing_Bool()),
269                          user_param_to_string);
270 
TEST_P(pactest,instr)271 TEST_P(pactest, instr) {
272     if (!arch_pac_address_supported()) {
273         GTEST_SKIP();
274     }
275 
276     const uint64_t test_address = _state->translation_table
277                                           ? PACTEST_TT1_ADDRESS
278                                           : PACTEST_TT0_ADDRESS;
279     uint64_t address = test_address;
280     int rc;
281 
282     if (_state->key_enabled) {
283         /* Test instruction adds a PAC */
284         address = pacxx(_state->instr_not_data, _state->key_a_not_b, address,
285                         PACTEST_MODIFIER);
286 
287         /* Address should have been modified to include PAC */
288         EXPECT_NE(test_address, address);
289 
290         uint64_t pac_address = address;
291 
292         /* Check AUT returns the original pointer */
293         rc = autxx(_state->instr_not_data, _state->key_a_not_b, address,
294                    PACTEST_MODIFIER, &address);
295 
296         EXPECT_EQ(rc, 0)
297         EXPECT_EQ(test_address, address);
298 
299         /* Check the pointer is invalidated when the modifier is changed */
300         rc = autxx(_state->instr_not_data, _state->key_a_not_b, pac_address,
301                    ~PACTEST_MODIFIER, &address);
302         if (arch_pac_exception_supported()) {
303             EXPECT_EQ(rc, ERR_FAULT);
304         } else {
305             /* Address should have been invalidated */
306             EXPECT_EQ(rc, 0);
307             EXPECT_NE(address, test_address);
308         }
309     } else { /* Key disabled */
310 
311         address = pacxx(_state->instr_not_data, _state->key_a_not_b, address,
312                         PACTEST_MODIFIER);
313         EXPECT_EQ(test_address, address);
314 
315         rc = autxx(_state->instr_not_data, _state->key_a_not_b, address,
316                    PACTEST_MODIFIER, &address);
317         EXPECT_EQ(rc, 0)
318         EXPECT_EQ(test_address, address);
319     }
320 test_abort:;
321 }
322 
TEST_P(pactest,pac_length)323 TEST_P(pactest, pac_length) {
324     if (!arch_pac_address_supported()) {
325         GTEST_SKIP();
326     }
327 
328     uint8_t top = 0, bot = 64;
329 
330     /*
331      * Probe a number of times in order to ensure we find the top and bottom
332      * bits used.  Odds of missing correct bounds are about P=(1/2)^32.
333      */
334     for (uint16_t t = 0; t < 32; t++) {
335         uint64_t val, orig;
336 
337         /* Get 64-bit random value */
338         platform_random_get_bytes((void*)&orig, sizeof(orig));
339 
340         /*
341          * Select which of T0SZ or T1SZ we should probe.
342          * Address bit 55 is used to select between page translation tables
343          * to use (e.g. TTBRx and TxSZ, where x is 0 or 1).
344          */
345         val = orig & ~(1ull << 55);
346         if (_state->translation_table) {
347             val |= 1ull << 55;
348         }
349 
350         /* Call specific instruction variant */
351         val = pacxx(_state->instr_not_data, _state->key_a_not_b, val, 0);
352 
353         /* Remove un-changed bits and clear bit 55 */
354         val ^= orig;
355         val &= ~(1ull << 55);
356 
357         /* Find highest and lowest set bit positions */
358         if (val) {
359             top = MAX(top, 63 - __builtin_clzll(val));
360             bot = MIN(bot, __builtin_ctzll(val));
361         }
362     }
363 
364     if (_state->key_enabled) {
365         /* If this is not true, the PAC key not be functioning */
366         ASSERT_GT(top, bot);
367 
368         /* Count bit range, except bit 55 if it is in the range */
369         int bits = (top + 1) - bot;
370         if (bot < 55 && top > 55) {
371             bits--;
372         }
373 
374         trusty_unittest_printf("[   INFO   ] PAC bits %" PRIu8 ":%" PRIu8
375                                " = %d effective bits\n",
376                                top, bot, bits);
377     } else {
378         trusty_unittest_printf("[   INFO   ] PAC key disabled\n");
379 
380         ASSERT_EQ(top, 0);
381         ASSERT_EQ(bot, 64);
382     }
383 
384 test_abort:;
385 }
386 
387 PORT_TEST(pactest, "com.android.kernel.pactest");
388