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 4000 /* 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