xref: /openbmc/linux/tools/testing/selftests/powerpc/signal/sigfuz.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
183e367f9SBreno Leitao // SPDX-License-Identifier: GPL-2.0
283e367f9SBreno Leitao /*
383e367f9SBreno Leitao  * Copyright 2018, Breno Leitao, IBM Corp.
483e367f9SBreno Leitao  * Licensed under GPLv2.
583e367f9SBreno Leitao  *
683e367f9SBreno Leitao  * Sigfuz(tm): A PowerPC TM-aware signal fuzzer.
783e367f9SBreno Leitao  *
883e367f9SBreno Leitao  * This is a new selftest that raises SIGUSR1 signals and handles it in a set
983e367f9SBreno Leitao  * of different ways, trying to create different scenario for testing
1083e367f9SBreno Leitao  * purpose.
1183e367f9SBreno Leitao  *
1283e367f9SBreno Leitao  * This test works raising a signal and calling sigreturn interleaved with
1383e367f9SBreno Leitao  * TM operations, as starting, suspending and terminating a transaction. The
1483e367f9SBreno Leitao  * test depends on random numbers, and, based on them, it sets different TM
1583e367f9SBreno Leitao  * states.
1683e367f9SBreno Leitao  *
1783e367f9SBreno Leitao  * Other than that, the test fills out the user context struct that is passed
1883e367f9SBreno Leitao  * to the sigreturn system call with random data, in order to make sure that
1983e367f9SBreno Leitao  * the signal handler syscall can handle different and invalid states
2083e367f9SBreno Leitao  * properly.
2183e367f9SBreno Leitao  *
2283e367f9SBreno Leitao  * This selftest has command line parameters to control what kind of tests the
2383e367f9SBreno Leitao  * user wants to run, as for example, if a transaction should be started prior
2483e367f9SBreno Leitao  * to signal being raised, or, after the signal being raised and before the
2583e367f9SBreno Leitao  * sigreturn. If no parameter is given, the default is enabling all options.
2683e367f9SBreno Leitao  *
2783e367f9SBreno Leitao  * This test does not check if the user context is being read and set
2883e367f9SBreno Leitao  * properly by the kernel. Its purpose, at this time, is basically
2983e367f9SBreno Leitao  * guaranteeing that the kernel does not crash on invalid scenarios.
3083e367f9SBreno Leitao  */
3183e367f9SBreno Leitao 
3283e367f9SBreno Leitao #include <stdio.h>
3383e367f9SBreno Leitao #include <limits.h>
3483e367f9SBreno Leitao #include <sys/wait.h>
3583e367f9SBreno Leitao #include <unistd.h>
3683e367f9SBreno Leitao #include <stdlib.h>
3783e367f9SBreno Leitao #include <signal.h>
3883e367f9SBreno Leitao #include <string.h>
3983e367f9SBreno Leitao #include <ucontext.h>
4083e367f9SBreno Leitao #include <sys/mman.h>
4183e367f9SBreno Leitao #include <pthread.h>
4283e367f9SBreno Leitao #include "utils.h"
4383e367f9SBreno Leitao 
4483e367f9SBreno Leitao /* Selftest defaults */
45*4f5c5b76SMichael Ellerman #define COUNT_MAX	600		/* Number of interactions */
4683e367f9SBreno Leitao #define THREADS		16		/* Number of threads */
4783e367f9SBreno Leitao 
4883e367f9SBreno Leitao /* Arguments options */
4983e367f9SBreno Leitao #define ARG_MESS_WITH_TM_AT	0x1
5083e367f9SBreno Leitao #define ARG_MESS_WITH_TM_BEFORE	0x2
5183e367f9SBreno Leitao #define ARG_MESS_WITH_MSR_AT	0x4
5283e367f9SBreno Leitao #define ARG_FOREVER		0x10
5383e367f9SBreno Leitao #define ARG_COMPLETE		(ARG_MESS_WITH_TM_AT |		\
5483e367f9SBreno Leitao 				ARG_MESS_WITH_TM_BEFORE |	\
5583e367f9SBreno Leitao 				ARG_MESS_WITH_MSR_AT)
5683e367f9SBreno Leitao 
5783e367f9SBreno Leitao static int args;
5883e367f9SBreno Leitao static int nthread = THREADS;
5983e367f9SBreno Leitao static int count_max = COUNT_MAX;
6083e367f9SBreno Leitao 
6183e367f9SBreno Leitao /* checkpoint context */
6283e367f9SBreno Leitao static ucontext_t *tmp_uc;
6383e367f9SBreno Leitao 
6483e367f9SBreno Leitao /* Return true with 1/x probability */
one_in_chance(int x)6583e367f9SBreno Leitao static int one_in_chance(int x)
6683e367f9SBreno Leitao {
6783e367f9SBreno Leitao 	return rand() % x == 0;
6883e367f9SBreno Leitao }
6983e367f9SBreno Leitao 
7083e367f9SBreno Leitao /* Change TM states */
mess_with_tm(void)7183e367f9SBreno Leitao static void mess_with_tm(void)
7283e367f9SBreno Leitao {
7383e367f9SBreno Leitao 	/* Starts a transaction 33% of the time */
7483e367f9SBreno Leitao 	if (one_in_chance(3)) {
7583e367f9SBreno Leitao 		asm ("tbegin.	;"
7683e367f9SBreno Leitao 		     "beq 8	;");
7783e367f9SBreno Leitao 
7883e367f9SBreno Leitao 		/* And suspended half of them */
7983e367f9SBreno Leitao 		if (one_in_chance(2))
8083e367f9SBreno Leitao 			asm("tsuspend.	;");
8183e367f9SBreno Leitao 	}
8283e367f9SBreno Leitao 
8383e367f9SBreno Leitao 	/* Call 'tend' in 5% of the runs */
8483e367f9SBreno Leitao 	if (one_in_chance(20))
8583e367f9SBreno Leitao 		asm("tend.	;");
8683e367f9SBreno Leitao }
8783e367f9SBreno Leitao 
8883e367f9SBreno Leitao /* Signal handler that will be invoked with raise() */
trap_signal_handler(int signo,siginfo_t * si,void * uc)8983e367f9SBreno Leitao static void trap_signal_handler(int signo, siginfo_t *si, void *uc)
9083e367f9SBreno Leitao {
9183e367f9SBreno Leitao 	ucontext_t *ucp = uc;
9283e367f9SBreno Leitao 
9383e367f9SBreno Leitao 	ucp->uc_link = tmp_uc;
9483e367f9SBreno Leitao 
9583e367f9SBreno Leitao 	/*
9683e367f9SBreno Leitao 	 * Set uc_link in three possible ways:
9783e367f9SBreno Leitao 	 *  - Setting a single 'int' in the whole chunk
9883e367f9SBreno Leitao 	 *  - Cloning ucp into uc_link
9983e367f9SBreno Leitao 	 *  - Allocating a new memory chunk
10083e367f9SBreno Leitao 	 */
10183e367f9SBreno Leitao 	if (one_in_chance(3)) {
10283e367f9SBreno Leitao 		memset(ucp->uc_link, rand(), sizeof(ucontext_t));
10383e367f9SBreno Leitao 	} else if (one_in_chance(2)) {
10483e367f9SBreno Leitao 		memcpy(ucp->uc_link, uc, sizeof(ucontext_t));
10583e367f9SBreno Leitao 	} else if (one_in_chance(2)) {
10683e367f9SBreno Leitao 		if (tmp_uc) {
10783e367f9SBreno Leitao 			free(tmp_uc);
10883e367f9SBreno Leitao 			tmp_uc = NULL;
10983e367f9SBreno Leitao 		}
11083e367f9SBreno Leitao 		tmp_uc = malloc(sizeof(ucontext_t));
11183e367f9SBreno Leitao 		ucp->uc_link = tmp_uc;
11283e367f9SBreno Leitao 		/* Trying to cause a major page fault at Kernel level */
11383e367f9SBreno Leitao 		madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED);
11483e367f9SBreno Leitao 	}
11583e367f9SBreno Leitao 
11683e367f9SBreno Leitao 	if (args & ARG_MESS_WITH_MSR_AT) {
11783e367f9SBreno Leitao 		/* Changing the checkpointed registers */
11883e367f9SBreno Leitao 		if (one_in_chance(4)) {
11983e367f9SBreno Leitao 			ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
12083e367f9SBreno Leitao 		} else {
12183e367f9SBreno Leitao 			if (one_in_chance(2)) {
12283e367f9SBreno Leitao 				ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
12383e367f9SBreno Leitao 						 MSR_TS_T;
12483e367f9SBreno Leitao 			} else if (one_in_chance(2)) {
12583e367f9SBreno Leitao 				ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
12683e367f9SBreno Leitao 						MSR_TS_T | MSR_TS_S;
12783e367f9SBreno Leitao 			}
12883e367f9SBreno Leitao 		}
12983e367f9SBreno Leitao 
13083e367f9SBreno Leitao 		/* Checking the current register context */
13183e367f9SBreno Leitao 		if (one_in_chance(2)) {
13283e367f9SBreno Leitao 			ucp->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
13383e367f9SBreno Leitao 		} else if (one_in_chance(2)) {
13483e367f9SBreno Leitao 			if (one_in_chance(2))
13583e367f9SBreno Leitao 				ucp->uc_mcontext.gp_regs[PT_MSR] |=
13683e367f9SBreno Leitao 					MSR_TS_T;
13783e367f9SBreno Leitao 			else if (one_in_chance(2))
13883e367f9SBreno Leitao 				ucp->uc_mcontext.gp_regs[PT_MSR] |=
13983e367f9SBreno Leitao 					MSR_TS_T | MSR_TS_S;
14083e367f9SBreno Leitao 		}
14183e367f9SBreno Leitao 	}
14283e367f9SBreno Leitao 
14383e367f9SBreno Leitao 	if (one_in_chance(20)) {
14483e367f9SBreno Leitao 		/* Nested transaction start */
14583e367f9SBreno Leitao 		if (one_in_chance(5))
14683e367f9SBreno Leitao 			mess_with_tm();
14783e367f9SBreno Leitao 
14883e367f9SBreno Leitao 		/* Return without changing any other context info */
14983e367f9SBreno Leitao 		return;
15083e367f9SBreno Leitao 	}
15183e367f9SBreno Leitao 
15283e367f9SBreno Leitao 	if (one_in_chance(10))
15383e367f9SBreno Leitao 		ucp->uc_mcontext.gp_regs[PT_MSR] = random();
15483e367f9SBreno Leitao 	if (one_in_chance(10))
15583e367f9SBreno Leitao 		ucp->uc_mcontext.gp_regs[PT_NIP] = random();
15683e367f9SBreno Leitao 	if (one_in_chance(10))
15783e367f9SBreno Leitao 		ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] = random();
15883e367f9SBreno Leitao 	if (one_in_chance(10))
15983e367f9SBreno Leitao 		ucp->uc_link->uc_mcontext.gp_regs[PT_NIP] = random();
16083e367f9SBreno Leitao 
16183e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_TRAP] = random();
16283e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_DSISR] = random();
16383e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_DAR] = random();
16483e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
16583e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_XER] = random();
16683e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_RESULT] = random();
16783e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_SOFTE] = random();
16883e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_DSCR] = random();
16983e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_CTR] = random();
17083e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_LNK] = random();
17183e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_CCR] = random();
17283e367f9SBreno Leitao 	ucp->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
17383e367f9SBreno Leitao 
17483e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_TRAP] = random();
17583e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_DSISR] = random();
17683e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_DAR] = random();
17783e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
17883e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_XER] = random();
17983e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_RESULT] = random();
18083e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_SOFTE] = random();
18183e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_DSCR] = random();
18283e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_CTR] = random();
18383e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_LNK] = random();
18483e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_CCR] = random();
18583e367f9SBreno Leitao 	ucp->uc_link->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
18683e367f9SBreno Leitao 
18783e367f9SBreno Leitao 	if (args & ARG_MESS_WITH_TM_BEFORE) {
18883e367f9SBreno Leitao 		if (one_in_chance(2))
18983e367f9SBreno Leitao 			mess_with_tm();
19083e367f9SBreno Leitao 	}
19183e367f9SBreno Leitao }
19283e367f9SBreno Leitao 
seg_signal_handler(int signo,siginfo_t * si,void * uc)19383e367f9SBreno Leitao static void seg_signal_handler(int signo, siginfo_t *si, void *uc)
19483e367f9SBreno Leitao {
19583e367f9SBreno Leitao 	/* Clear exit for process that segfaults */
19683e367f9SBreno Leitao 	exit(0);
19783e367f9SBreno Leitao }
19883e367f9SBreno Leitao 
sigfuz_test(void * thrid)19983e367f9SBreno Leitao static void *sigfuz_test(void *thrid)
20083e367f9SBreno Leitao {
20183e367f9SBreno Leitao 	struct sigaction trap_sa, seg_sa;
20283e367f9SBreno Leitao 	int ret, i = 0;
20383e367f9SBreno Leitao 	pid_t t;
20483e367f9SBreno Leitao 
20583e367f9SBreno Leitao 	tmp_uc = malloc(sizeof(ucontext_t));
20683e367f9SBreno Leitao 
20783e367f9SBreno Leitao 	/* Main signal handler */
20883e367f9SBreno Leitao 	trap_sa.sa_flags = SA_SIGINFO;
20983e367f9SBreno Leitao 	trap_sa.sa_sigaction = trap_signal_handler;
21083e367f9SBreno Leitao 
21183e367f9SBreno Leitao 	/* SIGSEGV signal handler */
21283e367f9SBreno Leitao 	seg_sa.sa_flags = SA_SIGINFO;
21383e367f9SBreno Leitao 	seg_sa.sa_sigaction = seg_signal_handler;
21483e367f9SBreno Leitao 
21583e367f9SBreno Leitao 	/* The signal handler will enable MSR_TS */
21683e367f9SBreno Leitao 	sigaction(SIGUSR1, &trap_sa, NULL);
21783e367f9SBreno Leitao 
21883e367f9SBreno Leitao 	/* If it does not crash, it will segfault, avoid it to retest */
21983e367f9SBreno Leitao 	sigaction(SIGSEGV, &seg_sa, NULL);
22083e367f9SBreno Leitao 
22183e367f9SBreno Leitao 	while (i < count_max) {
22283e367f9SBreno Leitao 		t = fork();
22383e367f9SBreno Leitao 
22483e367f9SBreno Leitao 		if (t == 0) {
22583e367f9SBreno Leitao 			/* Once seed per process */
22683e367f9SBreno Leitao 			srand(time(NULL) + getpid());
22783e367f9SBreno Leitao 			if (args & ARG_MESS_WITH_TM_AT) {
22883e367f9SBreno Leitao 				if (one_in_chance(2))
22983e367f9SBreno Leitao 					mess_with_tm();
23083e367f9SBreno Leitao 			}
23183e367f9SBreno Leitao 			raise(SIGUSR1);
23283e367f9SBreno Leitao 			exit(0);
23383e367f9SBreno Leitao 		} else {
23483e367f9SBreno Leitao 			waitpid(t, &ret, 0);
23583e367f9SBreno Leitao 		}
23683e367f9SBreno Leitao 		if (!(args & ARG_FOREVER))
23783e367f9SBreno Leitao 			i++;
23883e367f9SBreno Leitao 	}
23983e367f9SBreno Leitao 
24083e367f9SBreno Leitao 	/* If not freed already, free now */
24183e367f9SBreno Leitao 	if (tmp_uc) {
24283e367f9SBreno Leitao 		free(tmp_uc);
24383e367f9SBreno Leitao 		tmp_uc = NULL;
24483e367f9SBreno Leitao 	}
24583e367f9SBreno Leitao 
24683e367f9SBreno Leitao 	return NULL;
24783e367f9SBreno Leitao }
24883e367f9SBreno Leitao 
signal_fuzzer(void)24983e367f9SBreno Leitao static int signal_fuzzer(void)
25083e367f9SBreno Leitao {
25183e367f9SBreno Leitao 	int t, rc;
25283e367f9SBreno Leitao 	pthread_t *threads;
25383e367f9SBreno Leitao 
25483e367f9SBreno Leitao 	threads = malloc(nthread * sizeof(pthread_t));
25583e367f9SBreno Leitao 
25683e367f9SBreno Leitao 	for (t = 0; t < nthread; t++) {
25783e367f9SBreno Leitao 		rc = pthread_create(&threads[t], NULL, sigfuz_test,
25883e367f9SBreno Leitao 				    (void *)&t);
25983e367f9SBreno Leitao 		if (rc)
26083e367f9SBreno Leitao 			perror("Thread creation error\n");
26183e367f9SBreno Leitao 	}
26283e367f9SBreno Leitao 
26383e367f9SBreno Leitao 	for (t = 0; t < nthread; t++) {
26483e367f9SBreno Leitao 		rc = pthread_join(threads[t], NULL);
26583e367f9SBreno Leitao 		if (rc)
26683e367f9SBreno Leitao 			perror("Thread join error\n");
26783e367f9SBreno Leitao 	}
26883e367f9SBreno Leitao 
26983e367f9SBreno Leitao 	free(threads);
27083e367f9SBreno Leitao 
27183e367f9SBreno Leitao 	return EXIT_SUCCESS;
27283e367f9SBreno Leitao }
27383e367f9SBreno Leitao 
show_help(char * name)27483e367f9SBreno Leitao static void show_help(char *name)
27583e367f9SBreno Leitao {
27683e367f9SBreno Leitao 	printf("%s: Sigfuzzer for powerpc\n", name);
27783e367f9SBreno Leitao 	printf("Usage:\n");
27883e367f9SBreno Leitao 	printf("\t-b\t Mess with TM before raising a SIGUSR1 signal\n");
27983e367f9SBreno Leitao 	printf("\t-a\t Mess with TM after raising a SIGUSR1 signal\n");
28083e367f9SBreno Leitao 	printf("\t-m\t Mess with MSR[TS] bits at mcontext\n");
28183e367f9SBreno Leitao 	printf("\t-x\t Mess with everything above\n");
28283e367f9SBreno Leitao 	printf("\t-f\t Run forever (Press ^C to Quit)\n");
28383e367f9SBreno Leitao 	printf("\t-i\t Amount of interactions.	(Default = %d)\n", COUNT_MAX);
28483e367f9SBreno Leitao 	printf("\t-t\t Amount of threads.	(Default = %d)\n", THREADS);
28583e367f9SBreno Leitao 	exit(-1);
28683e367f9SBreno Leitao }
28783e367f9SBreno Leitao 
main(int argc,char ** argv)28883e367f9SBreno Leitao int main(int argc, char **argv)
28983e367f9SBreno Leitao {
29083e367f9SBreno Leitao 	int opt;
29183e367f9SBreno Leitao 
29283e367f9SBreno Leitao 	while ((opt = getopt(argc, argv, "bamxt:fi:h")) != -1) {
29383e367f9SBreno Leitao 		if (opt == 'b') {
29483e367f9SBreno Leitao 			printf("Mess with TM before signal\n");
29583e367f9SBreno Leitao 			args |= ARG_MESS_WITH_TM_BEFORE;
29683e367f9SBreno Leitao 		} else if (opt == 'a') {
29783e367f9SBreno Leitao 			printf("Mess with TM at signal handler\n");
29883e367f9SBreno Leitao 			args |= ARG_MESS_WITH_TM_AT;
29983e367f9SBreno Leitao 		} else if (opt == 'm') {
30083e367f9SBreno Leitao 			printf("Mess with MSR[TS] bits in mcontext\n");
30183e367f9SBreno Leitao 			args |= ARG_MESS_WITH_MSR_AT;
30283e367f9SBreno Leitao 		} else if (opt == 'x') {
30383e367f9SBreno Leitao 			printf("Running with all options enabled\n");
30483e367f9SBreno Leitao 			args |= ARG_COMPLETE;
30583e367f9SBreno Leitao 		} else if (opt == 't') {
30683e367f9SBreno Leitao 			nthread = atoi(optarg);
30783e367f9SBreno Leitao 			printf("Threads = %d\n", nthread);
30883e367f9SBreno Leitao 		} else if (opt == 'f') {
30983e367f9SBreno Leitao 			args |= ARG_FOREVER;
31083e367f9SBreno Leitao 			printf("Press ^C to stop\n");
31183e367f9SBreno Leitao 			test_harness_set_timeout(-1);
31283e367f9SBreno Leitao 		} else if (opt == 'i') {
31383e367f9SBreno Leitao 			count_max = atoi(optarg);
31483e367f9SBreno Leitao 			printf("Running for %d interactions\n", count_max);
31583e367f9SBreno Leitao 		} else if (opt == 'h') {
31683e367f9SBreno Leitao 			show_help(argv[0]);
31783e367f9SBreno Leitao 		}
31883e367f9SBreno Leitao 	}
31983e367f9SBreno Leitao 
32083e367f9SBreno Leitao 	/* Default test suite */
32183e367f9SBreno Leitao 	if (!args)
32283e367f9SBreno Leitao 		args = ARG_COMPLETE;
32383e367f9SBreno Leitao 
32483e367f9SBreno Leitao 	test_harness(signal_fuzzer, "signal_fuzzer");
32583e367f9SBreno Leitao }
326