Skip to content

Commit 806a15b

Browse files
loosper-armwilldeacon
authored andcommitted
kselftests/arm64: add PAuth test for whether exec() changes keys
Kernel documentation states that it will change PAuth keys on exec() calls. Verify that all keys are correctly switched to new ones. Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com> Reviewed-by: Vincenzo Frascino <Vincenzo.Frascino@arm.com> Reviewed-by: Amit Daniel Kachhap <amit.kachhap@arm.com> Acked-by: Shuah Khan <skhan@linuxfoundation.org> Cc: Shuah Khan <shuah@kernel.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20200918104715.182310-4-boian4o1@gmail.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent 766d95b commit 806a15b

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed

tools/testing/selftests/arm64/pauth/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pauth_cc_support := $(shell if ($(CC) $(CFLAGS) -march=armv8.3-a -E -x c /dev/nu
1313
ifeq ($(pauth_cc_support),1)
1414
TEST_GEN_PROGS := pac
1515
TEST_GEN_FILES := pac_corruptor.o helper.o
16+
TEST_GEN_PROGS_EXTENDED := exec_target
1617
endif
1718

1819
include ../../lib.mk
@@ -30,6 +31,9 @@ $(OUTPUT)/helper.o: helper.c
3031
# greater, gcc emits pac* instructions which are not in HINT NOP space,
3132
# preventing the tests from occurring at all. Compile for ARMv8.2 so tests can
3233
# run on earlier targets and print a meaningful error messages
34+
$(OUTPUT)/exec_target: exec_target.c $(OUTPUT)/helper.o
35+
$(CC) $^ -o $@ $(CFLAGS) -march=armv8.2-a
36+
3337
$(OUTPUT)/pac: pac.c $(OUTPUT)/pac_corruptor.o $(OUTPUT)/helper.o
3438
$(CC) $^ -o $@ $(CFLAGS) -march=armv8.2-a
3539
endif
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (C) 2020 ARM Limited
3+
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <sys/auxv.h>
7+
8+
#include "helper.h"
9+
10+
int main(void)
11+
{
12+
struct signatures signed_vals;
13+
unsigned long hwcaps;
14+
size_t val;
15+
16+
fread(&val, sizeof(size_t), 1, stdin);
17+
18+
/* don't try to execute illegal (unimplemented) instructions) caller
19+
* should have checked this and keep worker simple
20+
*/
21+
hwcaps = getauxval(AT_HWCAP);
22+
23+
if (hwcaps & HWCAP_PACA) {
24+
signed_vals.keyia = keyia_sign(val);
25+
signed_vals.keyib = keyib_sign(val);
26+
signed_vals.keyda = keyda_sign(val);
27+
signed_vals.keydb = keydb_sign(val);
28+
}
29+
signed_vals.keyg = (hwcaps & HWCAP_PACG) ? keyg_sign(val) : 0;
30+
31+
fwrite(&signed_vals, sizeof(struct signatures), 1, stdout);
32+
33+
return 0;
34+
}

tools/testing/selftests/arm64/pauth/helper.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66

77
#include <stdlib.h>
88

9+
#define NKEYS 5
10+
11+
struct signatures {
12+
size_t keyia;
13+
size_t keyib;
14+
size_t keyda;
15+
size_t keydb;
16+
size_t keyg;
17+
};
18+
919
void pac_corruptor(void);
1020

1121
/* PAuth sign a value with key ia and modifier value 0 */

tools/testing/selftests/arm64/pauth/pac.c

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Copyright (C) 2020 ARM Limited
33

44
#include <sys/auxv.h>
5+
#include <sys/types.h>
6+
#include <sys/wait.h>
57
#include <signal.h>
68
#include <setjmp.h>
79

@@ -28,6 +30,117 @@ do { \
2830
ASSERT_NE(0, hwcaps & HWCAP_PACG) TH_LOG("Generic PAUTH not enabled"); \
2931
} while (0)
3032

33+
void sign_specific(struct signatures *sign, size_t val)
34+
{
35+
sign->keyia = keyia_sign(val);
36+
sign->keyib = keyib_sign(val);
37+
sign->keyda = keyda_sign(val);
38+
sign->keydb = keydb_sign(val);
39+
}
40+
41+
void sign_all(struct signatures *sign, size_t val)
42+
{
43+
sign->keyia = keyia_sign(val);
44+
sign->keyib = keyib_sign(val);
45+
sign->keyda = keyda_sign(val);
46+
sign->keydb = keydb_sign(val);
47+
sign->keyg = keyg_sign(val);
48+
}
49+
50+
int n_same(struct signatures *old, struct signatures *new, int nkeys)
51+
{
52+
int res = 0;
53+
54+
res += old->keyia == new->keyia;
55+
res += old->keyib == new->keyib;
56+
res += old->keyda == new->keyda;
57+
res += old->keydb == new->keydb;
58+
if (nkeys == NKEYS)
59+
res += old->keyg == new->keyg;
60+
61+
return res;
62+
}
63+
64+
int exec_sign_all(struct signatures *signed_vals, size_t val)
65+
{
66+
int new_stdin[2];
67+
int new_stdout[2];
68+
int status;
69+
ssize_t ret;
70+
pid_t pid;
71+
72+
ret = pipe(new_stdin);
73+
if (ret == -1) {
74+
perror("pipe returned error");
75+
return -1;
76+
}
77+
78+
ret = pipe(new_stdout);
79+
if (ret == -1) {
80+
perror("pipe returned error");
81+
return -1;
82+
}
83+
84+
pid = fork();
85+
// child
86+
if (pid == 0) {
87+
dup2(new_stdin[0], STDIN_FILENO);
88+
if (ret == -1) {
89+
perror("dup2 returned error");
90+
exit(1);
91+
}
92+
93+
dup2(new_stdout[1], STDOUT_FILENO);
94+
if (ret == -1) {
95+
perror("dup2 returned error");
96+
exit(1);
97+
}
98+
99+
close(new_stdin[0]);
100+
close(new_stdin[1]);
101+
close(new_stdout[0]);
102+
close(new_stdout[1]);
103+
104+
ret = execl("exec_target", "exec_target", (char *)NULL);
105+
if (ret == -1) {
106+
perror("exec returned error");
107+
exit(1);
108+
}
109+
}
110+
111+
close(new_stdin[0]);
112+
close(new_stdout[1]);
113+
114+
ret = write(new_stdin[1], &val, sizeof(size_t));
115+
if (ret == -1) {
116+
perror("write returned error");
117+
return -1;
118+
}
119+
120+
/*
121+
* wait for the worker to finish, so that read() reads all data
122+
* will also context switch with worker so that this function can be used
123+
* for context switch tests
124+
*/
125+
waitpid(pid, &status, 0);
126+
if (WIFEXITED(status) == 0) {
127+
fprintf(stderr, "worker exited unexpectedly\n");
128+
return -1;
129+
}
130+
if (WEXITSTATUS(status) != 0) {
131+
fprintf(stderr, "worker exited with error\n");
132+
return -1;
133+
}
134+
135+
ret = read(new_stdout[0], signed_vals, sizeof(struct signatures));
136+
if (ret == -1) {
137+
perror("read returned error");
138+
return -1;
139+
}
140+
141+
return 0;
142+
}
143+
31144
sigjmp_buf jmpbuf;
32145
void pac_signal_handler(int signum, siginfo_t *si, void *uc)
33146
{
@@ -92,4 +205,41 @@ TEST(pac_instructions_not_nop_generic)
92205
ASSERT_NE(0, keyg) TH_LOG("keyg instructions did nothing");
93206
}
94207

208+
/*
209+
* fork() does not change keys. Only exec() does so call a worker program.
210+
* Its only job is to sign a value and report back the resutls
211+
*/
212+
TEST(exec_changed_keys)
213+
{
214+
struct signatures new_keys;
215+
struct signatures old_keys;
216+
int ret;
217+
int same = 10;
218+
int nkeys = NKEYS;
219+
unsigned long hwcaps = getauxval(AT_HWCAP);
220+
221+
/* generic and data key instructions are not in NOP space. This prevents a SIGILL */
222+
ASSERT_NE(0, hwcaps & HWCAP_PACA) TH_LOG("PAUTH not enabled");
223+
if (!(hwcaps & HWCAP_PACG)) {
224+
TH_LOG("WARNING: Generic PAUTH not enabled. Skipping generic key checks");
225+
nkeys = NKEYS - 1;
226+
}
227+
228+
for (int i = 0; i < PAC_COLLISION_ATTEMPTS; i++) {
229+
ret = exec_sign_all(&new_keys, i);
230+
ASSERT_EQ(0, ret) TH_LOG("failed to run worker");
231+
232+
if (nkeys == NKEYS)
233+
sign_all(&old_keys, i);
234+
else
235+
sign_specific(&old_keys, i);
236+
237+
ret = n_same(&old_keys, &new_keys, nkeys);
238+
if (ret < same)
239+
same = ret;
240+
}
241+
242+
ASSERT_EQ(0, same) TH_LOG("exec() did not change %d keys", same);
243+
}
244+
95245
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)