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	600		/* 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
57static int args;
58static int nthread = THREADS;
59static int count_max = COUNT_MAX;
60
61/* checkpoint context */
62static ucontext_t *tmp_uc;
63
64/* Return true with 1/x probability */
65static int one_in_chance(int x)
66{
67	return rand() % x == 0;
68}
69
70/* Change TM states */
71static 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() */
89static 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
193static void seg_signal_handler(int signo, siginfo_t *si, void *uc)
194{
195	/* Clear exit for process that segfaults */
196	exit(0);
197}
198
199static 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
249static 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
274static 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
288int 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}
326