162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2018, Breno Leitao, IBM Corp.
462306a36Sopenharmony_ci * Licensed under GPLv2.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Sigfuz(tm): A PowerPC TM-aware signal fuzzer.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This is a new selftest that raises SIGUSR1 signals and handles it in a set
962306a36Sopenharmony_ci * of different ways, trying to create different scenario for testing
1062306a36Sopenharmony_ci * purpose.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * This test works raising a signal and calling sigreturn interleaved with
1362306a36Sopenharmony_ci * TM operations, as starting, suspending and terminating a transaction. The
1462306a36Sopenharmony_ci * test depends on random numbers, and, based on them, it sets different TM
1562306a36Sopenharmony_ci * states.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Other than that, the test fills out the user context struct that is passed
1862306a36Sopenharmony_ci * to the sigreturn system call with random data, in order to make sure that
1962306a36Sopenharmony_ci * the signal handler syscall can handle different and invalid states
2062306a36Sopenharmony_ci * properly.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * This selftest has command line parameters to control what kind of tests the
2362306a36Sopenharmony_ci * user wants to run, as for example, if a transaction should be started prior
2462306a36Sopenharmony_ci * to signal being raised, or, after the signal being raised and before the
2562306a36Sopenharmony_ci * sigreturn. If no parameter is given, the default is enabling all options.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * This test does not check if the user context is being read and set
2862306a36Sopenharmony_ci * properly by the kernel. Its purpose, at this time, is basically
2962306a36Sopenharmony_ci * guaranteeing that the kernel does not crash on invalid scenarios.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <stdio.h>
3362306a36Sopenharmony_ci#include <limits.h>
3462306a36Sopenharmony_ci#include <sys/wait.h>
3562306a36Sopenharmony_ci#include <unistd.h>
3662306a36Sopenharmony_ci#include <stdlib.h>
3762306a36Sopenharmony_ci#include <signal.h>
3862306a36Sopenharmony_ci#include <string.h>
3962306a36Sopenharmony_ci#include <ucontext.h>
4062306a36Sopenharmony_ci#include <sys/mman.h>
4162306a36Sopenharmony_ci#include <pthread.h>
4262306a36Sopenharmony_ci#include "utils.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Selftest defaults */
4562306a36Sopenharmony_ci#define COUNT_MAX	600		/* Number of interactions */
4662306a36Sopenharmony_ci#define THREADS		16		/* Number of threads */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Arguments options */
4962306a36Sopenharmony_ci#define ARG_MESS_WITH_TM_AT	0x1
5062306a36Sopenharmony_ci#define ARG_MESS_WITH_TM_BEFORE	0x2
5162306a36Sopenharmony_ci#define ARG_MESS_WITH_MSR_AT	0x4
5262306a36Sopenharmony_ci#define ARG_FOREVER		0x10
5362306a36Sopenharmony_ci#define ARG_COMPLETE		(ARG_MESS_WITH_TM_AT |		\
5462306a36Sopenharmony_ci				ARG_MESS_WITH_TM_BEFORE |	\
5562306a36Sopenharmony_ci				ARG_MESS_WITH_MSR_AT)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int args;
5862306a36Sopenharmony_cistatic int nthread = THREADS;
5962306a36Sopenharmony_cistatic int count_max = COUNT_MAX;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* checkpoint context */
6262306a36Sopenharmony_cistatic ucontext_t *tmp_uc;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Return true with 1/x probability */
6562306a36Sopenharmony_cistatic int one_in_chance(int x)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	return rand() % x == 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Change TM states */
7162306a36Sopenharmony_cistatic void mess_with_tm(void)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	/* Starts a transaction 33% of the time */
7462306a36Sopenharmony_ci	if (one_in_chance(3)) {
7562306a36Sopenharmony_ci		asm ("tbegin.	;"
7662306a36Sopenharmony_ci		     "beq 8	;");
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		/* And suspended half of them */
7962306a36Sopenharmony_ci		if (one_in_chance(2))
8062306a36Sopenharmony_ci			asm("tsuspend.	;");
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* Call 'tend' in 5% of the runs */
8462306a36Sopenharmony_ci	if (one_in_chance(20))
8562306a36Sopenharmony_ci		asm("tend.	;");
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Signal handler that will be invoked with raise() */
8962306a36Sopenharmony_cistatic void trap_signal_handler(int signo, siginfo_t *si, void *uc)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	ucontext_t *ucp = uc;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	ucp->uc_link = tmp_uc;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/*
9662306a36Sopenharmony_ci	 * Set uc_link in three possible ways:
9762306a36Sopenharmony_ci	 *  - Setting a single 'int' in the whole chunk
9862306a36Sopenharmony_ci	 *  - Cloning ucp into uc_link
9962306a36Sopenharmony_ci	 *  - Allocating a new memory chunk
10062306a36Sopenharmony_ci	 */
10162306a36Sopenharmony_ci	if (one_in_chance(3)) {
10262306a36Sopenharmony_ci		memset(ucp->uc_link, rand(), sizeof(ucontext_t));
10362306a36Sopenharmony_ci	} else if (one_in_chance(2)) {
10462306a36Sopenharmony_ci		memcpy(ucp->uc_link, uc, sizeof(ucontext_t));
10562306a36Sopenharmony_ci	} else if (one_in_chance(2)) {
10662306a36Sopenharmony_ci		if (tmp_uc) {
10762306a36Sopenharmony_ci			free(tmp_uc);
10862306a36Sopenharmony_ci			tmp_uc = NULL;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci		tmp_uc = malloc(sizeof(ucontext_t));
11162306a36Sopenharmony_ci		ucp->uc_link = tmp_uc;
11262306a36Sopenharmony_ci		/* Trying to cause a major page fault at Kernel level */
11362306a36Sopenharmony_ci		madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED);
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (args & ARG_MESS_WITH_MSR_AT) {
11762306a36Sopenharmony_ci		/* Changing the checkpointed registers */
11862306a36Sopenharmony_ci		if (one_in_chance(4)) {
11962306a36Sopenharmony_ci			ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
12062306a36Sopenharmony_ci		} else {
12162306a36Sopenharmony_ci			if (one_in_chance(2)) {
12262306a36Sopenharmony_ci				ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
12362306a36Sopenharmony_ci						 MSR_TS_T;
12462306a36Sopenharmony_ci			} else if (one_in_chance(2)) {
12562306a36Sopenharmony_ci				ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] |=
12662306a36Sopenharmony_ci						MSR_TS_T | MSR_TS_S;
12762306a36Sopenharmony_ci			}
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		/* Checking the current register context */
13162306a36Sopenharmony_ci		if (one_in_chance(2)) {
13262306a36Sopenharmony_ci			ucp->uc_mcontext.gp_regs[PT_MSR] |= MSR_TS_S;
13362306a36Sopenharmony_ci		} else if (one_in_chance(2)) {
13462306a36Sopenharmony_ci			if (one_in_chance(2))
13562306a36Sopenharmony_ci				ucp->uc_mcontext.gp_regs[PT_MSR] |=
13662306a36Sopenharmony_ci					MSR_TS_T;
13762306a36Sopenharmony_ci			else if (one_in_chance(2))
13862306a36Sopenharmony_ci				ucp->uc_mcontext.gp_regs[PT_MSR] |=
13962306a36Sopenharmony_ci					MSR_TS_T | MSR_TS_S;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (one_in_chance(20)) {
14462306a36Sopenharmony_ci		/* Nested transaction start */
14562306a36Sopenharmony_ci		if (one_in_chance(5))
14662306a36Sopenharmony_ci			mess_with_tm();
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		/* Return without changing any other context info */
14962306a36Sopenharmony_ci		return;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (one_in_chance(10))
15362306a36Sopenharmony_ci		ucp->uc_mcontext.gp_regs[PT_MSR] = random();
15462306a36Sopenharmony_ci	if (one_in_chance(10))
15562306a36Sopenharmony_ci		ucp->uc_mcontext.gp_regs[PT_NIP] = random();
15662306a36Sopenharmony_ci	if (one_in_chance(10))
15762306a36Sopenharmony_ci		ucp->uc_link->uc_mcontext.gp_regs[PT_MSR] = random();
15862306a36Sopenharmony_ci	if (one_in_chance(10))
15962306a36Sopenharmony_ci		ucp->uc_link->uc_mcontext.gp_regs[PT_NIP] = random();
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_TRAP] = random();
16262306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_DSISR] = random();
16362306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_DAR] = random();
16462306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
16562306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_XER] = random();
16662306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_RESULT] = random();
16762306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_SOFTE] = random();
16862306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_DSCR] = random();
16962306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_CTR] = random();
17062306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_LNK] = random();
17162306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_CCR] = random();
17262306a36Sopenharmony_ci	ucp->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_TRAP] = random();
17562306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_DSISR] = random();
17662306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_DAR] = random();
17762306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_ORIG_R3] = random();
17862306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_XER] = random();
17962306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_RESULT] = random();
18062306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_SOFTE] = random();
18162306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_DSCR] = random();
18262306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_CTR] = random();
18362306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_LNK] = random();
18462306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_CCR] = random();
18562306a36Sopenharmony_ci	ucp->uc_link->uc_mcontext.gp_regs[PT_REGS_COUNT] = random();
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (args & ARG_MESS_WITH_TM_BEFORE) {
18862306a36Sopenharmony_ci		if (one_in_chance(2))
18962306a36Sopenharmony_ci			mess_with_tm();
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void seg_signal_handler(int signo, siginfo_t *si, void *uc)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	/* Clear exit for process that segfaults */
19662306a36Sopenharmony_ci	exit(0);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void *sigfuz_test(void *thrid)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct sigaction trap_sa, seg_sa;
20262306a36Sopenharmony_ci	int ret, i = 0;
20362306a36Sopenharmony_ci	pid_t t;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	tmp_uc = malloc(sizeof(ucontext_t));
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* Main signal handler */
20862306a36Sopenharmony_ci	trap_sa.sa_flags = SA_SIGINFO;
20962306a36Sopenharmony_ci	trap_sa.sa_sigaction = trap_signal_handler;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* SIGSEGV signal handler */
21262306a36Sopenharmony_ci	seg_sa.sa_flags = SA_SIGINFO;
21362306a36Sopenharmony_ci	seg_sa.sa_sigaction = seg_signal_handler;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* The signal handler will enable MSR_TS */
21662306a36Sopenharmony_ci	sigaction(SIGUSR1, &trap_sa, NULL);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* If it does not crash, it will segfault, avoid it to retest */
21962306a36Sopenharmony_ci	sigaction(SIGSEGV, &seg_sa, NULL);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	while (i < count_max) {
22262306a36Sopenharmony_ci		t = fork();
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		if (t == 0) {
22562306a36Sopenharmony_ci			/* Once seed per process */
22662306a36Sopenharmony_ci			srand(time(NULL) + getpid());
22762306a36Sopenharmony_ci			if (args & ARG_MESS_WITH_TM_AT) {
22862306a36Sopenharmony_ci				if (one_in_chance(2))
22962306a36Sopenharmony_ci					mess_with_tm();
23062306a36Sopenharmony_ci			}
23162306a36Sopenharmony_ci			raise(SIGUSR1);
23262306a36Sopenharmony_ci			exit(0);
23362306a36Sopenharmony_ci		} else {
23462306a36Sopenharmony_ci			waitpid(t, &ret, 0);
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci		if (!(args & ARG_FOREVER))
23762306a36Sopenharmony_ci			i++;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* If not freed already, free now */
24162306a36Sopenharmony_ci	if (tmp_uc) {
24262306a36Sopenharmony_ci		free(tmp_uc);
24362306a36Sopenharmony_ci		tmp_uc = NULL;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return NULL;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int signal_fuzzer(void)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	int t, rc;
25262306a36Sopenharmony_ci	pthread_t *threads;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	threads = malloc(nthread * sizeof(pthread_t));
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	for (t = 0; t < nthread; t++) {
25762306a36Sopenharmony_ci		rc = pthread_create(&threads[t], NULL, sigfuz_test,
25862306a36Sopenharmony_ci				    (void *)&t);
25962306a36Sopenharmony_ci		if (rc)
26062306a36Sopenharmony_ci			perror("Thread creation error\n");
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	for (t = 0; t < nthread; t++) {
26462306a36Sopenharmony_ci		rc = pthread_join(threads[t], NULL);
26562306a36Sopenharmony_ci		if (rc)
26662306a36Sopenharmony_ci			perror("Thread join error\n");
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	free(threads);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return EXIT_SUCCESS;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void show_help(char *name)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	printf("%s: Sigfuzzer for powerpc\n", name);
27762306a36Sopenharmony_ci	printf("Usage:\n");
27862306a36Sopenharmony_ci	printf("\t-b\t Mess with TM before raising a SIGUSR1 signal\n");
27962306a36Sopenharmony_ci	printf("\t-a\t Mess with TM after raising a SIGUSR1 signal\n");
28062306a36Sopenharmony_ci	printf("\t-m\t Mess with MSR[TS] bits at mcontext\n");
28162306a36Sopenharmony_ci	printf("\t-x\t Mess with everything above\n");
28262306a36Sopenharmony_ci	printf("\t-f\t Run forever (Press ^C to Quit)\n");
28362306a36Sopenharmony_ci	printf("\t-i\t Amount of interactions.	(Default = %d)\n", COUNT_MAX);
28462306a36Sopenharmony_ci	printf("\t-t\t Amount of threads.	(Default = %d)\n", THREADS);
28562306a36Sopenharmony_ci	exit(-1);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ciint main(int argc, char **argv)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	int opt;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	while ((opt = getopt(argc, argv, "bamxt:fi:h")) != -1) {
29362306a36Sopenharmony_ci		if (opt == 'b') {
29462306a36Sopenharmony_ci			printf("Mess with TM before signal\n");
29562306a36Sopenharmony_ci			args |= ARG_MESS_WITH_TM_BEFORE;
29662306a36Sopenharmony_ci		} else if (opt == 'a') {
29762306a36Sopenharmony_ci			printf("Mess with TM at signal handler\n");
29862306a36Sopenharmony_ci			args |= ARG_MESS_WITH_TM_AT;
29962306a36Sopenharmony_ci		} else if (opt == 'm') {
30062306a36Sopenharmony_ci			printf("Mess with MSR[TS] bits in mcontext\n");
30162306a36Sopenharmony_ci			args |= ARG_MESS_WITH_MSR_AT;
30262306a36Sopenharmony_ci		} else if (opt == 'x') {
30362306a36Sopenharmony_ci			printf("Running with all options enabled\n");
30462306a36Sopenharmony_ci			args |= ARG_COMPLETE;
30562306a36Sopenharmony_ci		} else if (opt == 't') {
30662306a36Sopenharmony_ci			nthread = atoi(optarg);
30762306a36Sopenharmony_ci			printf("Threads = %d\n", nthread);
30862306a36Sopenharmony_ci		} else if (opt == 'f') {
30962306a36Sopenharmony_ci			args |= ARG_FOREVER;
31062306a36Sopenharmony_ci			printf("Press ^C to stop\n");
31162306a36Sopenharmony_ci			test_harness_set_timeout(-1);
31262306a36Sopenharmony_ci		} else if (opt == 'i') {
31362306a36Sopenharmony_ci			count_max = atoi(optarg);
31462306a36Sopenharmony_ci			printf("Running for %d interactions\n", count_max);
31562306a36Sopenharmony_ci		} else if (opt == 'h') {
31662306a36Sopenharmony_ci			show_help(argv[0]);
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Default test suite */
32162306a36Sopenharmony_ci	if (!args)
32262306a36Sopenharmony_ci		args = ARG_COMPLETE;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	test_harness(signal_fuzzer, "signal_fuzzer");
32562306a36Sopenharmony_ci}
326