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