cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sigfuz.c (8900B)


      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}