11// SPDX-License-Identifier: GPL-2.0
22// Copyright (C) 2020 ARM Limited
33
4+ #define _GNU_SOURCE
5+
46#include <sys/auxv.h>
57#include <sys/types.h>
68#include <sys/wait.h>
79#include <signal.h>
810#include <setjmp.h>
11+ #include <sched.h>
912
1013#include "../../kselftest_harness.h"
1114#include "helper.h"
1720 * The VA space size is 48 bits. Bigger is opt-in.
1821 */
1922#define PAC_MASK (~0xff80ffffffffffff)
23+ #define ARBITRARY_VALUE (0x1234)
2024#define ASSERT_PAUTH_ENABLED () \
2125do { \
2226 unsigned long hwcaps = getauxval(AT_HWCAP); \
@@ -61,13 +65,37 @@ int n_same(struct signatures *old, struct signatures *new, int nkeys)
6165 return res ;
6266}
6367
68+ int n_same_single_set (struct signatures * sign , int nkeys )
69+ {
70+ size_t vals [nkeys ];
71+ int same = 0 ;
72+
73+ vals [0 ] = sign -> keyia & PAC_MASK ;
74+ vals [1 ] = sign -> keyib & PAC_MASK ;
75+ vals [2 ] = sign -> keyda & PAC_MASK ;
76+ vals [3 ] = sign -> keydb & PAC_MASK ;
77+
78+ if (nkeys >= 4 )
79+ vals [4 ] = sign -> keyg & PAC_MASK ;
80+
81+ for (int i = 0 ; i < nkeys - 1 ; i ++ ) {
82+ for (int j = i + 1 ; j < nkeys ; j ++ ) {
83+ if (vals [i ] == vals [j ])
84+ same += 1 ;
85+ }
86+ }
87+ return same ;
88+ }
89+
6490int exec_sign_all (struct signatures * signed_vals , size_t val )
6591{
6692 int new_stdin [2 ];
6793 int new_stdout [2 ];
6894 int status ;
95+ int i ;
6996 ssize_t ret ;
7097 pid_t pid ;
98+ cpu_set_t mask ;
7199
72100 ret = pipe (new_stdin );
73101 if (ret == -1 ) {
@@ -81,6 +109,20 @@ int exec_sign_all(struct signatures *signed_vals, size_t val)
81109 return -1 ;
82110 }
83111
112+ /*
113+ * pin this process and all its children to a single CPU, so it can also
114+ * guarantee a context switch with its child
115+ */
116+ sched_getaffinity (0 , sizeof (mask ), & mask );
117+
118+ for (i = 0 ; i < sizeof (cpu_set_t ); i ++ )
119+ if (CPU_ISSET (i , & mask ))
120+ break ;
121+
122+ CPU_ZERO (& mask );
123+ CPU_SET (i , & mask );
124+ sched_setaffinity (0 , sizeof (mask ), & mask );
125+
84126 pid = fork ();
85127 // child
86128 if (pid == 0 ) {
@@ -205,6 +247,44 @@ TEST(pac_instructions_not_nop_generic)
205247 ASSERT_NE (0 , keyg ) TH_LOG ("keyg instructions did nothing" );
206248}
207249
250+ TEST (single_thread_different_keys )
251+ {
252+ int same = 10 ;
253+ int nkeys = NKEYS ;
254+ int tmp ;
255+ struct signatures signed_vals ;
256+ unsigned long hwcaps = getauxval (AT_HWCAP );
257+
258+ /* generic and data key instructions are not in NOP space. This prevents a SIGILL */
259+ ASSERT_NE (0 , hwcaps & HWCAP_PACA ) TH_LOG ("PAUTH not enabled" );
260+ if (!(hwcaps & HWCAP_PACG )) {
261+ TH_LOG ("WARNING: Generic PAUTH not enabled. Skipping generic key checks" );
262+ nkeys = NKEYS - 1 ;
263+ }
264+
265+ /*
266+ * In Linux the PAC field can be up to 7 bits wide. Even if keys are
267+ * different, there is about 5% chance for PACs to collide with
268+ * different addresses. This chance rapidly increases with fewer bits
269+ * allocated for the PAC (e.g. wider address). A comparison of the keys
270+ * directly will be more reliable.
271+ * All signed values need to be different at least once out of n
272+ * attempts to be certain that the keys are different
273+ */
274+ for (int i = 0 ; i < PAC_COLLISION_ATTEMPTS ; i ++ ) {
275+ if (nkeys == NKEYS )
276+ sign_all (& signed_vals , i );
277+ else
278+ sign_specific (& signed_vals , i );
279+
280+ tmp = n_same_single_set (& signed_vals , nkeys );
281+ if (tmp < same )
282+ same = tmp ;
283+ }
284+
285+ ASSERT_EQ (0 , same ) TH_LOG ("%d keys clashed every time" , same );
286+ }
287+
208288/*
209289 * fork() does not change keys. Only exec() does so call a worker program.
210290 * Its only job is to sign a value and report back the resutls
@@ -242,4 +322,47 @@ TEST(exec_changed_keys)
242322 ASSERT_EQ (0 , same ) TH_LOG ("exec() did not change %d keys" , same );
243323}
244324
325+ TEST (context_switch_keep_keys )
326+ {
327+ int ret ;
328+ struct signatures trash ;
329+ struct signatures before ;
330+ struct signatures after ;
331+
332+ ASSERT_PAUTH_ENABLED ();
333+
334+ sign_specific (& before , ARBITRARY_VALUE );
335+
336+ /* will context switch with a process with different keys at least once */
337+ ret = exec_sign_all (& trash , ARBITRARY_VALUE );
338+ ASSERT_EQ (0 , ret ) TH_LOG ("failed to run worker" );
339+
340+ sign_specific (& after , ARBITRARY_VALUE );
341+
342+ ASSERT_EQ (before .keyia , after .keyia ) TH_LOG ("keyia changed after context switching" );
343+ ASSERT_EQ (before .keyib , after .keyib ) TH_LOG ("keyib changed after context switching" );
344+ ASSERT_EQ (before .keyda , after .keyda ) TH_LOG ("keyda changed after context switching" );
345+ ASSERT_EQ (before .keydb , after .keydb ) TH_LOG ("keydb changed after context switching" );
346+ }
347+
348+ TEST (context_switch_keep_keys_generic )
349+ {
350+ int ret ;
351+ struct signatures trash ;
352+ size_t before ;
353+ size_t after ;
354+
355+ ASSERT_GENERIC_PAUTH_ENABLED ();
356+
357+ before = keyg_sign (ARBITRARY_VALUE );
358+
359+ /* will context switch with a process with different keys at least once */
360+ ret = exec_sign_all (& trash , ARBITRARY_VALUE );
361+ ASSERT_EQ (0 , ret ) TH_LOG ("failed to run worker" );
362+
363+ after = keyg_sign (ARBITRARY_VALUE );
364+
365+ ASSERT_EQ (before , after ) TH_LOG ("keyg changed after context switching" );
366+ }
367+
245368TEST_HARNESS_MAIN
0 commit comments