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 
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 }
326