linux/tools/testing/selftests/powerpc/signal/sigfuz.c
<<
>>
Prefs
   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