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(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() */ |
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(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 | |
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(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 | |
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(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 | |