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(x: 3)) {
75 asm ("tbegin. ;"
76 "beq 8 ;");
77
78 /* And suspended half of them */
79 if (one_in_chance(x: 2))
80 asm("tsuspend. ;");
81 }
82
83 /* Call 'tend' in 5% of the runs */
84 if (one_in_chance(x: 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(x: 3)) {
102 memset(ucp->uc_link, rand(), sizeof(ucontext_t));
103 } else if (one_in_chance(x: 2)) {
104 memcpy(ucp->uc_link, uc, sizeof(ucontext_t));
105 } else if (one_in_chance(x: 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(x: 4)) {
119 ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
120 } else {
121 if (one_in_chance(x: 2)) {
122 ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
123 MSR_TS_T;
124 } else if (one_in_chance(x: 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(x: 2)) {
132 ucp->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
133 } else if (one_in_chance(x: 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(x: 20)) {
144 /* Nested transaction start */
145 if (one_in_chance(x: 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(x: 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(x: 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(name: 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

source code of linux/tools/testing/selftests/powerpc/signal/sigfuz.c