Skip to content

Commit 83e367f

Browse files
leitaompe
authored andcommitted
selftests/powerpc: Add a signal fuzzer selftest
This is a new selftest that raises SIGUSR1 signals and handles it in a set of different ways, trying to create different scenario for testing purpose. This test works raising a signal and calling sigreturn interleaved with TM operations, as starting, suspending and terminating a transaction. The test depends on random numbers, and, based on them, it sets different TM states. Other than that, the test fills out the user context struct that is passed to the sigreturn system call with random data, in order to make sure that the signal handler syscall can handle different and invalid states properly. This selftest has command line parameters to control what kind of tests the user wants to run, as for example, if a transaction should be started prior to signal being raised, or, after the signal being raised and before the sigreturn. If no parameter is given, the default is enabling all options. This test does not check if the user context is being read and set properly by the kernel. Its purpose, at this time, is basically guaranteeing that the kernel does not crash on invalid scenarios. Signed-off-by: Breno Leitao <[email protected]> Signed-off-by: Michael Ellerman <[email protected]>
1 parent 5266e58 commit 83e367f

File tree

5 files changed

+334
-3
lines changed

5 files changed

+334
-3
lines changed

tools/testing/selftests/powerpc/harness.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#define KILL_TIMEOUT 5
2323

24+
/* Setting timeout to -1 disables the alarm */
2425
static uint64_t timeout = 120;
2526

2627
int run_test(int (test_function)(void), char *name)
@@ -43,8 +44,9 @@ int run_test(int (test_function)(void), char *name)
4344

4445
setpgid(pid, pid);
4546

46-
/* Wake us up in timeout seconds */
47-
alarm(timeout);
47+
if (timeout != -1)
48+
/* Wake us up in timeout seconds */
49+
alarm(timeout);
4850
terminated = false;
4951

5052
wait:

tools/testing/selftests/powerpc/include/reg.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,13 @@
7979

8080
/* MSR register bits */
8181
#define MSR_TS_S_LG 33 /* Trans Mem state: Suspended */
82+
#define MSR_TS_T_LG 34 /* Trans Mem state: Active */
8283

8384
#define __MASK(X) (1UL<<(X))
8485

8586
/* macro to check TM MSR bits */
8687
#define MSR_TS_S __MASK(MSR_TS_S_LG) /* Transaction Suspended */
88+
#define MSR_TS_T __MASK(MSR_TS_T_LG) /* Transaction Transactional */
8789

8890
/* Vector Instructions */
8991
#define VSX_XX1(xs, ra, rb) (((xs) & 0x1f) << 21 | ((ra) << 16) | \
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
signal
22
signal_tm
3+
sigfuz

tools/testing/selftests/powerpc/signal/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# SPDX-License-Identifier: GPL-2.0
2-
TEST_GEN_PROGS := signal signal_tm
2+
TEST_GEN_PROGS := signal signal_tm sigfuz
33

44
CFLAGS += -maltivec
55
$(OUTPUT)/signal_tm: CFLAGS += -mhtm
6+
$(OUTPUT)/sigfuz: CFLAGS += -pthread -m64
67

78
top_srcdir = ../../../../..
89
include ../../lib.mk
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright 2018, Breno Leitao, IBM Corp.
4+
* Licensed under GPLv2.
5+
*
6+
* Sigfuz(tm): A PowerPC TM-aware signal fuzzer.
7+
*
8+
* This is a new selftest that raises SIGUSR1 signals and handles it in a set
9+
* of different ways, trying to create different scenario for testing
10+
* purpose.
11+
*
12+
* This test works raising a signal and calling sigreturn interleaved with
13+
* TM operations, as starting, suspending and terminating a transaction. The
14+
* test depends on random numbers, and, based on them, it sets different TM
15+
* states.
16+
*
17+
* Other than that, the test fills out the user context struct that is passed
18+
* to the sigreturn system call with random data, in order to make sure that
19+
* the signal handler syscall can handle different and invalid states
20+
* properly.
21+
*
22+
* This selftest has command line parameters to control what kind of tests the
23+
* user wants to run, as for example, if a transaction should be started prior
24+
* to signal being raised, or, after the signal being raised and before the
25+
* sigreturn. If no parameter is given, the default is enabling all options.
26+
*
27+
* This test does not check if the user context is being read and set
28+
* properly by the kernel. Its purpose, at this time, is basically
29+
* guaranteeing that the kernel does not crash on invalid scenarios.
30+
*/
31+
32+
#include <stdio.h>
33+
#include <limits.h>
34+
#include <sys/wait.h>
35+
#include <unistd.h>
36+
#include <stdlib.h>
37+
#include <signal.h>
38+
#include <string.h>
39+
#include <ucontext.h>
40+
#include <sys/mman.h>
41+
#include <pthread.h>
42+
#include "utils.h"
43+
44+
/* Selftest defaults */
45+
#define COUNT_MAX 4000 /* Number of interactions */
46+
#define THREADS 16 /* Number of threads */
47+
48+
/* Arguments options */
49+
#define ARG_MESS_WITH_TM_AT 0x1
50+
#define ARG_MESS_WITH_TM_BEFORE 0x2
51+
#define ARG_MESS_WITH_MSR_AT 0x4
52+
#define ARG_FOREVER 0x10
53+
#define ARG_COMPLETE (ARG_MESS_WITH_TM_AT | \
54+
ARG_MESS_WITH_TM_BEFORE | \
55+
ARG_MESS_WITH_MSR_AT)
56+
57+
static int args;
58+
static int nthread = THREADS;
59+
static int count_max = COUNT_MAX;
60+
61+
/* checkpoint context */
62+
static ucontext_t *tmp_uc;
63+
64+
/* Return true with 1/x probability */
65+
static int one_in_chance(int x)
66+
{
67+
return rand() % x == 0;
68+
}
69+
70+
/* Change TM states */
71+
static void mess_with_tm(void)
72+
{
73+
/* Starts a transaction 33% of the time */
74+
if (one_in_chance(3)) {
75+
asm ("tbegin. ;"
76+
"beq 8 ;");
77+
78+
/* And suspended half of them */
79+
if (one_in_chance(2))
80+
asm("tsuspend. ;");
81+
}
82+
83+
/* Call 'tend' in 5% of the runs */
84+
if (one_in_chance(20))
85+
asm("tend. ;");
86+
}
87+
88+
/* Signal handler that will be invoked with raise() */
89+
static void trap_signal_handler(int signo, siginfo_t *si, void *uc)
90+
{
91+
ucontext_t *ucp = uc;
92+
93+
ucp->uc_link = tmp_uc;
94+
95+
/*
96+
* Set uc_link in three possible ways:
97+
* - Setting a single 'int' in the whole chunk
98+
* - Cloning ucp into uc_link
99+
* - Allocating a new memory chunk
100+
*/
101+
if (one_in_chance(3)) {
102+
memset(ucp->uc_link, rand(), sizeof(ucontext_t));
103+
} else if (one_in_chance(2)) {
104+
memcpy(ucp->uc_link, uc, sizeof(ucontext_t));
105+
} else if (one_in_chance(2)) {
106+
if (tmp_uc) {
107+
free(tmp_uc);
108+
tmp_uc = NULL;
109+
}
110+
tmp_uc = malloc(sizeof(ucontext_t));
111+
ucp->uc_link = tmp_uc;
112+
/* Trying to cause a major page fault at Kernel level */
113+
madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED);
114+
}
115+
116+
if (args & ARG_MESS_WITH_MSR_AT) {
117+
/* Changing the checkpointed registers */
118+
if (one_in_chance(4)) {
119+
ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
120+
} else {
121+
if (one_in_chance(2)) {
122+
ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
123+
MSR_TS_T;
124+
} else if (one_in_chance(2)) {
125+
ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
126+
MSR_TS_T | MSR_TS_S;
127+
}
128+
}
129+
130+
/* Checking the current register context */
131+
if (one_in_chance(2)) {
132+
ucp->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
133+
} else if (one_in_chance(2)) {
134+
if (one_in_chance(2))
135+
ucp->uc_mcontext.gp_regs[PT_MSR] |=
136+
MSR_TS_T;
137+
else if (one_in_chance(2))
138+
ucp->uc_mcontext.gp_regs[PT_MSR] |=
139+
MSR_TS_T | MSR_TS_S;
140+
}
141+
}
142+
143+
if (one_in_chance(20)) {
144+
/* Nested transaction start */
145+
if (one_in_chance(5))
146+
mess_with_tm();
147+
148+
/* Return without changing any other context info */
149+
return;
150+
}
151+
152+
if (one_in_chance(10))
153+
ucp->uc_mcontext.gp_regs[PT_MSR] = random();
154+
if (one_in_chance(10))
155+
ucp->uc_mcontext.gp_regs[PT_NIP] = random();
156+
if (one_in_chance(10))
157+
ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] = random();
158+
if (one_in_chance(10))
159+
ucp->uc_link->uc_mcontext.gp_regs[PT_NIP] = random();
160+
161+
ucp->uc_mcontext.gp_regs[PT_TRAP] = random();
162+
ucp->uc_mcontext.gp_regs[PT_DSISR] = random();
163+
ucp->uc_mcontext.gp_regs[PT_DAR] = random();
164+
ucp->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
165+
ucp->uc_mcontext.gp_regs[PT_XER] = random();
166+
ucp->uc_mcontext.gp_regs[PT_RESULT] = random();
167+
ucp->uc_mcontext.gp_regs[PT_SOFTE] = random();
168+
ucp->uc_mcontext.gp_regs[PT_DSCR] = random();
169+
ucp->uc_mcontext.gp_regs[PT_CTR] = random();
170+
ucp->uc_mcontext.gp_regs[PT_LNK] = random();
171+
ucp->uc_mcontext.gp_regs[PT_CCR] = random();
172+
ucp->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
173+
174+
ucp->uc_link->uc_mcontext.gp_regs[PT_TRAP] = random();
175+
ucp->uc_link->uc_mcontext.gp_regs[PT_DSISR] = random();
176+
ucp->uc_link->uc_mcontext.gp_regs[PT_DAR] = random();
177+
ucp->uc_link->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
178+
ucp->uc_link->uc_mcontext.gp_regs[PT_XER] = random();
179+
ucp->uc_link->uc_mcontext.gp_regs[PT_RESULT] = random();
180+
ucp->uc_link->uc_mcontext.gp_regs[PT_SOFTE] = random();
181+
ucp->uc_link->uc_mcontext.gp_regs[PT_DSCR] = random();
182+
ucp->uc_link->uc_mcontext.gp_regs[PT_CTR] = random();
183+
ucp->uc_link->uc_mcontext.gp_regs[PT_LNK] = random();
184+
ucp->uc_link->uc_mcontext.gp_regs[PT_CCR] = random();
185+
ucp->uc_link->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
186+
187+
if (args & ARG_MESS_WITH_TM_BEFORE) {
188+
if (one_in_chance(2))
189+
mess_with_tm();
190+
}
191+
}
192+
193+
static void seg_signal_handler(int signo, siginfo_t *si, void *uc)
194+
{
195+
/* Clear exit for process that segfaults */
196+
exit(0);
197+
}
198+
199+
static void *sigfuz_test(void *thrid)
200+
{
201+
struct sigaction trap_sa, seg_sa;
202+
int ret, i = 0;
203+
pid_t t;
204+
205+
tmp_uc = malloc(sizeof(ucontext_t));
206+
207+
/* Main signal handler */
208+
trap_sa.sa_flags = SA_SIGINFO;
209+
trap_sa.sa_sigaction = trap_signal_handler;
210+
211+
/* SIGSEGV signal handler */
212+
seg_sa.sa_flags = SA_SIGINFO;
213+
seg_sa.sa_sigaction = seg_signal_handler;
214+
215+
/* The signal handler will enable MSR_TS */
216+
sigaction(SIGUSR1, &trap_sa, NULL);
217+
218+
/* If it does not crash, it will segfault, avoid it to retest */
219+
sigaction(SIGSEGV, &seg_sa, NULL);
220+
221+
while (i < count_max) {
222+
t = fork();
223+
224+
if (t == 0) {
225+
/* Once seed per process */
226+
srand(time(NULL) + getpid());
227+
if (args & ARG_MESS_WITH_TM_AT) {
228+
if (one_in_chance(2))
229+
mess_with_tm();
230+
}
231+
raise(SIGUSR1);
232+
exit(0);
233+
} else {
234+
waitpid(t, &ret, 0);
235+
}
236+
if (!(args & ARG_FOREVER))
237+
i++;
238+
}
239+
240+
/* If not freed already, free now */
241+
if (tmp_uc) {
242+
free(tmp_uc);
243+
tmp_uc = NULL;
244+
}
245+
246+
return NULL;
247+
}
248+
249+
static int signal_fuzzer(void)
250+
{
251+
int t, rc;
252+
pthread_t *threads;
253+
254+
threads = malloc(nthread * sizeof(pthread_t));
255+
256+
for (t = 0; t < nthread; t++) {
257+
rc = pthread_create(&threads[t], NULL, sigfuz_test,
258+
(void *)&t);
259+
if (rc)
260+
perror("Thread creation error\n");
261+
}
262+
263+
for (t = 0; t < nthread; t++) {
264+
rc = pthread_join(threads[t], NULL);
265+
if (rc)
266+
perror("Thread join error\n");
267+
}
268+
269+
free(threads);
270+
271+
return EXIT_SUCCESS;
272+
}
273+
274+
static void show_help(char *name)
275+
{
276+
printf("%s: Sigfuzzer for powerpc\n", name);
277+
printf("Usage:\n");
278+
printf("\t-b\t Mess with TM before raising a SIGUSR1 signal\n");
279+
printf("\t-a\t Mess with TM after raising a SIGUSR1 signal\n");
280+
printf("\t-m\t Mess with MSR[TS] bits at mcontext\n");
281+
printf("\t-x\t Mess with everything above\n");
282+
printf("\t-f\t Run forever (Press ^C to Quit)\n");
283+
printf("\t-i\t Amount of interactions. (Default = %d)\n", COUNT_MAX);
284+
printf("\t-t\t Amount of threads. (Default = %d)\n", THREADS);
285+
exit(-1);
286+
}
287+
288+
int main(int argc, char **argv)
289+
{
290+
int opt;
291+
292+
while ((opt = getopt(argc, argv, "bamxt:fi:h")) != -1) {
293+
if (opt == 'b') {
294+
printf("Mess with TM before signal\n");
295+
args |= ARG_MESS_WITH_TM_BEFORE;
296+
} else if (opt == 'a') {
297+
printf("Mess with TM at signal handler\n");
298+
args |= ARG_MESS_WITH_TM_AT;
299+
} else if (opt == 'm') {
300+
printf("Mess with MSR[TS] bits in mcontext\n");
301+
args |= ARG_MESS_WITH_MSR_AT;
302+
} else if (opt == 'x') {
303+
printf("Running with all options enabled\n");
304+
args |= ARG_COMPLETE;
305+
} else if (opt == 't') {
306+
nthread = atoi(optarg);
307+
printf("Threads = %d\n", nthread);
308+
} else if (opt == 'f') {
309+
args |= ARG_FOREVER;
310+
printf("Press ^C to stop\n");
311+
test_harness_set_timeout(-1);
312+
} else if (opt == 'i') {
313+
count_max = atoi(optarg);
314+
printf("Running for %d interactions\n", count_max);
315+
} else if (opt == 'h') {
316+
show_help(argv[0]);
317+
}
318+
}
319+
320+
/* Default test suite */
321+
if (!args)
322+
args = ARG_COMPLETE;
323+
324+
test_harness(signal_fuzzer, "signal_fuzzer");
325+
}

0 commit comments

Comments
 (0)