Skip to content

Commit 05e6cff

Browse files
brooniectmarinas
authored andcommitted
kselftest/arm64: Add a GCS stress test
Add a stress test which runs one more process than we have CPUs spinning through a very recursive function with frequent syscalls immediately prior to return and signals being injected every 100ms. The goal is to flag up any scheduling related issues, for example failure to ensure that barriers are inserted when moving a GCS using task to another CPU. The test runs for a configurable amount of time, defaulting to 10 seconds. Reviewed-by: Thiago Jung Bauermann <[email protected]> Tested-by: Thiago Jung Bauermann <[email protected]> Signed-off-by: Mark Brown <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent 794b64c commit 05e6cff

File tree

5 files changed

+848
-1
lines changed

5 files changed

+848
-1
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
basic-gcs
22
libc-gcs
33
gcs-locking
4+
gcs-stress
5+
gcs-stress-thread

tools/testing/selftests/arm64/gcs/Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
# nolibc.
77
#
88

9-
TEST_GEN_PROGS := basic-gcs libc-gcs gcs-locking
9+
TEST_GEN_PROGS := basic-gcs libc-gcs gcs-locking gcs-stress
10+
TEST_GEN_PROGS_EXTENDED := gcs-stress-thread
1011

1112
LDLIBS+=-lpthread
1213

@@ -18,3 +19,6 @@ $(OUTPUT)/basic-gcs: basic-gcs.c
1819
-I../../../../../usr/include \
1920
-std=gnu99 -I../.. -g \
2021
-ffreestanding -Wall $^ -o $@ -lgcc
22+
23+
$(OUTPUT)/gcs-stress-thread: gcs-stress-thread.S
24+
$(CC) -nostdlib $^ -o $@

tools/testing/selftests/arm64/gcs/asm-offsets.h

Whitespace-only changes.
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
// Program that loops for ever doing lots of recursions and system calls,
2+
// intended to be used as part of a stress test for GCS context switching.
3+
//
4+
// Copyright 2015-2023 Arm Ltd
5+
6+
#include <asm/unistd.h>
7+
8+
#define sa_sz 32
9+
#define sa_flags 8
10+
#define sa_handler 0
11+
#define sa_mask_sz 8
12+
13+
#define si_code 8
14+
15+
#define SIGINT 2
16+
#define SIGABRT 6
17+
#define SIGUSR1 10
18+
#define SIGSEGV 11
19+
#define SIGUSR2 12
20+
#define SIGTERM 15
21+
#define SEGV_CPERR 10
22+
23+
#define SA_NODEFER 1073741824
24+
#define SA_SIGINFO 4
25+
#define ucontext_regs 184
26+
27+
#define PR_SET_SHADOW_STACK_STATUS 75
28+
# define PR_SHADOW_STACK_ENABLE (1UL << 0)
29+
30+
#define GCSPR_EL0 S3_3_C2_C5_1
31+
32+
.macro function name
33+
.macro endfunction
34+
.type \name, @function
35+
.purgem endfunction
36+
.endm
37+
\name:
38+
.endm
39+
40+
// Print a single character x0 to stdout
41+
// Clobbers x0-x2,x8
42+
function putc
43+
str x0, [sp, #-16]!
44+
45+
mov x0, #1 // STDOUT_FILENO
46+
mov x1, sp
47+
mov x2, #1
48+
mov x8, #__NR_write
49+
svc #0
50+
51+
add sp, sp, #16
52+
ret
53+
endfunction
54+
.globl putc
55+
56+
// Print a NUL-terminated string starting at address x0 to stdout
57+
// Clobbers x0-x3,x8
58+
function puts
59+
mov x1, x0
60+
61+
mov x2, #0
62+
0: ldrb w3, [x0], #1
63+
cbz w3, 1f
64+
add x2, x2, #1
65+
b 0b
66+
67+
1: mov w0, #1 // STDOUT_FILENO
68+
mov x8, #__NR_write
69+
svc #0
70+
71+
ret
72+
endfunction
73+
.globl puts
74+
75+
// Utility macro to print a literal string
76+
// Clobbers x0-x4,x8
77+
.macro puts string
78+
.pushsection .rodata.str1.1, "aMS", @progbits, 1
79+
.L__puts_literal\@: .string "\string"
80+
.popsection
81+
82+
ldr x0, =.L__puts_literal\@
83+
bl puts
84+
.endm
85+
86+
// Print an unsigned decimal number x0 to stdout
87+
// Clobbers x0-x4,x8
88+
function putdec
89+
mov x1, sp
90+
str x30, [sp, #-32]! // Result can't be > 20 digits
91+
92+
mov x2, #0
93+
strb w2, [x1, #-1]! // Write the NUL terminator
94+
95+
mov x2, #10
96+
0: udiv x3, x0, x2 // div-mod loop to generate the digits
97+
msub x0, x3, x2, x0
98+
add w0, w0, #'0'
99+
strb w0, [x1, #-1]!
100+
mov x0, x3
101+
cbnz x3, 0b
102+
103+
ldrb w0, [x1]
104+
cbnz w0, 1f
105+
mov w0, #'0' // Print "0" for 0, not ""
106+
strb w0, [x1, #-1]!
107+
108+
1: mov x0, x1
109+
bl puts
110+
111+
ldr x30, [sp], #32
112+
ret
113+
endfunction
114+
.globl putdec
115+
116+
// Print an unsigned decimal number x0 to stdout, followed by a newline
117+
// Clobbers x0-x5,x8
118+
function putdecn
119+
mov x5, x30
120+
121+
bl putdec
122+
mov x0, #'\n'
123+
bl putc
124+
125+
ret x5
126+
endfunction
127+
.globl putdecn
128+
129+
// Fill x1 bytes starting at x0 with 0.
130+
// Clobbers x1, x2.
131+
function memclr
132+
mov w2, #0
133+
endfunction
134+
.globl memclr
135+
// fall through to memfill
136+
137+
// Trivial memory fill: fill x1 bytes starting at address x0 with byte w2
138+
// Clobbers x1
139+
function memfill
140+
cmp x1, #0
141+
b.eq 1f
142+
143+
0: strb w2, [x0], #1
144+
subs x1, x1, #1
145+
b.ne 0b
146+
147+
1: ret
148+
endfunction
149+
.globl memfill
150+
151+
// w0: signal number
152+
// x1: sa_action
153+
// w2: sa_flags
154+
// Clobbers x0-x6,x8
155+
function setsignal
156+
str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
157+
158+
mov w4, w0
159+
mov x5, x1
160+
mov w6, w2
161+
162+
add x0, sp, #16
163+
mov x1, #sa_sz
164+
bl memclr
165+
166+
mov w0, w4
167+
add x1, sp, #16
168+
str w6, [x1, #sa_flags]
169+
str x5, [x1, #sa_handler]
170+
mov x2, #0
171+
mov x3, #sa_mask_sz
172+
mov x8, #__NR_rt_sigaction
173+
svc #0
174+
175+
cbz w0, 1f
176+
177+
puts "sigaction failure\n"
178+
b abort
179+
180+
1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
181+
ret
182+
endfunction
183+
184+
185+
function tickle_handler
186+
// Perhaps collect GCSPR_EL0 here in future?
187+
ret
188+
endfunction
189+
190+
function terminate_handler
191+
mov w21, w0
192+
mov x20, x2
193+
194+
puts "Terminated by signal "
195+
mov w0, w21
196+
bl putdec
197+
puts ", no error\n"
198+
199+
mov x0, #0
200+
mov x8, #__NR_exit
201+
svc #0
202+
endfunction
203+
204+
function segv_handler
205+
// stash the siginfo_t *
206+
mov x20, x1
207+
208+
// Disable GCS, we don't want additional faults logging things
209+
mov x0, PR_SET_SHADOW_STACK_STATUS
210+
mov x1, xzr
211+
mov x2, xzr
212+
mov x3, xzr
213+
mov x4, xzr
214+
mov x5, xzr
215+
mov x8, #__NR_prctl
216+
svc #0
217+
218+
puts "Got SIGSEGV code "
219+
220+
ldr x21, [x20, #si_code]
221+
mov x0, x21
222+
bl putdec
223+
224+
// GCS faults should have si_code SEGV_CPERR
225+
cmp x21, #SEGV_CPERR
226+
bne 1f
227+
228+
puts " (GCS violation)"
229+
1:
230+
mov x0, '\n'
231+
bl putc
232+
b abort
233+
endfunction
234+
235+
// Recurse x20 times
236+
.macro recurse id
237+
function recurse\id
238+
stp x29, x30, [sp, #-16]!
239+
mov x29, sp
240+
241+
cmp x20, 0
242+
beq 1f
243+
sub x20, x20, 1
244+
bl recurse\id
245+
246+
1:
247+
ldp x29, x30, [sp], #16
248+
249+
// Do a syscall immediately prior to returning to try to provoke
250+
// scheduling and migration at a point where coherency issues
251+
// might trigger.
252+
mov x8, #__NR_getpid
253+
svc #0
254+
255+
ret
256+
endfunction
257+
.endm
258+
259+
// Generate and use two copies so we're changing the GCS contents
260+
recurse 1
261+
recurse 2
262+
263+
.globl _start
264+
function _start
265+
// Run with GCS
266+
mov x0, PR_SET_SHADOW_STACK_STATUS
267+
mov x1, PR_SHADOW_STACK_ENABLE
268+
mov x2, xzr
269+
mov x3, xzr
270+
mov x4, xzr
271+
mov x5, xzr
272+
mov x8, #__NR_prctl
273+
svc #0
274+
cbz x0, 1f
275+
puts "Failed to enable GCS\n"
276+
b abort
277+
1:
278+
279+
mov w0, #SIGTERM
280+
adr x1, terminate_handler
281+
mov w2, #SA_SIGINFO
282+
bl setsignal
283+
284+
mov w0, #SIGUSR1
285+
adr x1, tickle_handler
286+
mov w2, #SA_SIGINFO
287+
orr w2, w2, #SA_NODEFER
288+
bl setsignal
289+
290+
mov w0, #SIGSEGV
291+
adr x1, segv_handler
292+
mov w2, #SA_SIGINFO
293+
orr w2, w2, #SA_NODEFER
294+
bl setsignal
295+
296+
puts "Running\n"
297+
298+
loop:
299+
// Small recursion depth so we're frequently flipping between
300+
// the two recursors and changing what's on the stack
301+
mov x20, #5
302+
bl recurse1
303+
mov x20, #5
304+
bl recurse2
305+
b loop
306+
endfunction
307+
308+
abort:
309+
mov x0, #255
310+
mov x8, #__NR_exit
311+
svc #0

0 commit comments

Comments
 (0)