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

test_signals_utils.c (9124B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Copyright (C) 2019 ARM Limited */
      3
      4#include <stdio.h>
      5#include <stdlib.h>
      6#include <signal.h>
      7#include <string.h>
      8#include <unistd.h>
      9#include <assert.h>
     10#include <sys/auxv.h>
     11#include <linux/auxvec.h>
     12#include <ucontext.h>
     13
     14#include <asm/unistd.h>
     15
     16#include <kselftest.h>
     17
     18#include "test_signals.h"
     19#include "test_signals_utils.h"
     20#include "testcases/testcases.h"
     21
     22
     23extern struct tdescr *current;
     24
     25static int sig_copyctx = SIGTRAP;
     26
     27static char const *const feats_names[FMAX_END] = {
     28	" SSBS ",
     29	" SVE ",
     30	" SME ",
     31	" FA64 ",
     32};
     33
     34#define MAX_FEATS_SZ	128
     35static char feats_string[MAX_FEATS_SZ];
     36
     37static inline char *feats_to_string(unsigned long feats)
     38{
     39	size_t flen = MAX_FEATS_SZ - 1;
     40
     41	feats_string[0] = '\0';
     42
     43	for (int i = 0; i < FMAX_END; i++) {
     44		if (feats & (1UL << i)) {
     45			size_t tlen = strlen(feats_names[i]);
     46
     47			assert(flen > tlen);
     48			flen -= tlen;
     49			strncat(feats_string, feats_names[i], flen);
     50		}
     51	}
     52
     53	return feats_string;
     54}
     55
     56static void unblock_signal(int signum)
     57{
     58	sigset_t sset;
     59
     60	sigemptyset(&sset);
     61	sigaddset(&sset, signum);
     62	sigprocmask(SIG_UNBLOCK, &sset, NULL);
     63}
     64
     65static void default_result(struct tdescr *td, bool force_exit)
     66{
     67	if (td->result == KSFT_SKIP) {
     68		fprintf(stderr, "==>> completed. SKIP.\n");
     69	} else if (td->pass) {
     70		fprintf(stderr, "==>> completed. PASS(1)\n");
     71		td->result = KSFT_PASS;
     72	} else {
     73		fprintf(stdout, "==>> completed. FAIL(0)\n");
     74		td->result = KSFT_FAIL;
     75	}
     76
     77	if (force_exit)
     78		exit(td->result);
     79}
     80
     81/*
     82 * The following handle_signal_* helpers are used by main default_handler
     83 * and are meant to return true when signal is handled successfully:
     84 * when false is returned instead, it means that the signal was somehow
     85 * unexpected in that context and it was NOT handled; default_handler will
     86 * take care of such unexpected situations.
     87 */
     88
     89static bool handle_signal_unsupported(struct tdescr *td,
     90				      siginfo_t *si, void *uc)
     91{
     92	if (feats_ok(td))
     93		return false;
     94
     95	/* Mangling PC to avoid loops on original SIGILL */
     96	((ucontext_t *)uc)->uc_mcontext.pc += 4;
     97
     98	if (!td->initialized) {
     99		fprintf(stderr,
    100			"Got SIG_UNSUPP @test_init. Ignore.\n");
    101	} else {
    102		fprintf(stderr,
    103			"-- RX SIG_UNSUPP on unsupported feat...OK\n");
    104		td->pass = 1;
    105		default_result(current, 1);
    106	}
    107
    108	return true;
    109}
    110
    111static bool handle_signal_trigger(struct tdescr *td,
    112				  siginfo_t *si, void *uc)
    113{
    114	td->triggered = 1;
    115	/* ->run was asserted NON-NULL in test_setup() already */
    116	td->run(td, si, uc);
    117
    118	return true;
    119}
    120
    121static bool handle_signal_ok(struct tdescr *td,
    122			     siginfo_t *si, void *uc)
    123{
    124	/*
    125	 * it's a bug in the test code when this assert fail:
    126	 * if sig_trig was defined, it must have been used before getting here.
    127	 */
    128	assert(!td->sig_trig || td->triggered);
    129	fprintf(stderr,
    130		"SIG_OK -- SP:0x%llX  si_addr@:%p  si_code:%d  token@:%p  offset:%ld\n",
    131		((ucontext_t *)uc)->uc_mcontext.sp,
    132		si->si_addr, si->si_code, td->token, td->token - si->si_addr);
    133	/*
    134	 * fake_sigreturn tests, which have sanity_enabled=1, set, at the very
    135	 * last time, the token field to the SP address used to place the fake
    136	 * sigframe: so token==0 means we never made it to the end,
    137	 * segfaulting well-before, and the test is possibly broken.
    138	 */
    139	if (!td->sanity_disabled && !td->token) {
    140		fprintf(stdout,
    141			"current->token ZEROED...test is probably broken!\n");
    142		abort();
    143	}
    144	/*
    145	 * Trying to narrow down the SEGV to the ones generated by Kernel itself
    146	 * via arm64_notify_segfault(). This is a best-effort check anyway, and
    147	 * the si_code check may need to change if this aspect of the kernel
    148	 * ABI changes.
    149	 */
    150	if (td->sig_ok == SIGSEGV && si->si_code != SEGV_ACCERR) {
    151		fprintf(stdout,
    152			"si_code != SEGV_ACCERR...test is probably broken!\n");
    153		abort();
    154	}
    155	td->pass = 1;
    156	/*
    157	 * Some tests can lead to SEGV loops: in such a case we want to
    158	 * terminate immediately exiting straight away; some others are not
    159	 * supposed to outlive the signal handler code, due to the content of
    160	 * the fake sigframe which caused the signal itself.
    161	 */
    162	default_result(current, 1);
    163
    164	return true;
    165}
    166
    167static bool handle_signal_copyctx(struct tdescr *td,
    168				  siginfo_t *si, void *uc)
    169{
    170	/* Mangling PC to avoid loops on original BRK instr */
    171	((ucontext_t *)uc)->uc_mcontext.pc += 4;
    172	memcpy(td->live_uc, uc, td->live_sz);
    173	ASSERT_GOOD_CONTEXT(td->live_uc);
    174	td->live_uc_valid = 1;
    175	fprintf(stderr,
    176		"GOOD CONTEXT grabbed from sig_copyctx handler\n");
    177
    178	return true;
    179}
    180
    181static void default_handler(int signum, siginfo_t *si, void *uc)
    182{
    183	if (current->sig_unsupp && signum == current->sig_unsupp &&
    184	    handle_signal_unsupported(current, si, uc)) {
    185		fprintf(stderr, "Handled SIG_UNSUPP\n");
    186	} else if (current->sig_trig && signum == current->sig_trig &&
    187		   handle_signal_trigger(current, si, uc)) {
    188		fprintf(stderr, "Handled SIG_TRIG\n");
    189	} else if (current->sig_ok && signum == current->sig_ok &&
    190		   handle_signal_ok(current, si, uc)) {
    191		fprintf(stderr, "Handled SIG_OK\n");
    192	} else if (signum == sig_copyctx && current->live_uc &&
    193		   handle_signal_copyctx(current, si, uc)) {
    194		fprintf(stderr, "Handled SIG_COPYCTX\n");
    195	} else {
    196		if (signum == SIGALRM && current->timeout) {
    197			fprintf(stderr, "-- Timeout !\n");
    198		} else {
    199			fprintf(stderr,
    200				"-- RX UNEXPECTED SIGNAL: %d\n", signum);
    201		}
    202		default_result(current, 1);
    203	}
    204}
    205
    206static int default_setup(struct tdescr *td)
    207{
    208	struct sigaction sa;
    209
    210	sa.sa_sigaction = default_handler;
    211	sa.sa_flags = SA_SIGINFO | SA_RESTART;
    212	sa.sa_flags |= td->sa_flags;
    213	sigemptyset(&sa.sa_mask);
    214	/* uncatchable signals naturally skipped ... */
    215	for (int sig = 1; sig < 32; sig++)
    216		sigaction(sig, &sa, NULL);
    217	/*
    218	 * RT Signals default disposition is Term but they cannot be
    219	 * generated by the Kernel in response to our tests; so just catch
    220	 * them all and report them as UNEXPECTED signals.
    221	 */
    222	for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
    223		sigaction(sig, &sa, NULL);
    224
    225	/* just in case...unblock explicitly all we need */
    226	if (td->sig_trig)
    227		unblock_signal(td->sig_trig);
    228	if (td->sig_ok)
    229		unblock_signal(td->sig_ok);
    230	if (td->sig_unsupp)
    231		unblock_signal(td->sig_unsupp);
    232
    233	if (td->timeout) {
    234		unblock_signal(SIGALRM);
    235		alarm(td->timeout);
    236	}
    237	fprintf(stderr, "Registered handlers for all signals.\n");
    238
    239	return 1;
    240}
    241
    242static inline int default_trigger(struct tdescr *td)
    243{
    244	return !raise(td->sig_trig);
    245}
    246
    247int test_init(struct tdescr *td)
    248{
    249	if (td->sig_trig == sig_copyctx) {
    250		fprintf(stdout,
    251			"Signal %d is RESERVED, cannot be used as a trigger. Aborting\n",
    252			sig_copyctx);
    253		return 0;
    254	}
    255	/* just in case */
    256	unblock_signal(sig_copyctx);
    257
    258	td->minsigstksz = getauxval(AT_MINSIGSTKSZ);
    259	if (!td->minsigstksz)
    260		td->minsigstksz = MINSIGSTKSZ;
    261	fprintf(stderr, "Detected MINSTKSIGSZ:%d\n", td->minsigstksz);
    262
    263	if (td->feats_required || td->feats_incompatible) {
    264		td->feats_supported = 0;
    265		/*
    266		 * Checking for CPU required features using both the
    267		 * auxval and the arm64 MRS Emulation to read sysregs.
    268		 */
    269		if (getauxval(AT_HWCAP) & HWCAP_SSBS)
    270			td->feats_supported |= FEAT_SSBS;
    271		if (getauxval(AT_HWCAP) & HWCAP_SVE)
    272			td->feats_supported |= FEAT_SVE;
    273		if (getauxval(AT_HWCAP2) & HWCAP2_SME)
    274			td->feats_supported |= FEAT_SME;
    275		if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
    276			td->feats_supported |= FEAT_SME_FA64;
    277		if (feats_ok(td)) {
    278			if (td->feats_required & td->feats_supported)
    279				fprintf(stderr,
    280					"Required Features: [%s] supported\n",
    281					feats_to_string(td->feats_required &
    282							td->feats_supported));
    283			if (!(td->feats_incompatible & td->feats_supported))
    284				fprintf(stderr,
    285					"Incompatible Features: [%s] absent\n",
    286					feats_to_string(td->feats_incompatible));
    287		} else {
    288			if ((td->feats_required & td->feats_supported) !=
    289			    td->feats_supported)
    290				fprintf(stderr,
    291					"Required Features: [%s] NOT supported\n",
    292					feats_to_string(td->feats_required &
    293							~td->feats_supported));
    294			if (td->feats_incompatible & td->feats_supported)
    295				fprintf(stderr,
    296					"Incompatible Features: [%s] supported\n",
    297					feats_to_string(td->feats_incompatible &
    298							~td->feats_supported));
    299
    300
    301			td->result = KSFT_SKIP;
    302			return 0;
    303		}
    304	}
    305
    306	/* Perform test specific additional initialization */
    307	if (td->init && !td->init(td)) {
    308		fprintf(stderr, "FAILED Testcase initialization.\n");
    309		return 0;
    310	}
    311	td->initialized = 1;
    312	fprintf(stderr, "Testcase initialized.\n");
    313
    314	return 1;
    315}
    316
    317int test_setup(struct tdescr *td)
    318{
    319	/* assert core invariants symptom of a rotten testcase */
    320	assert(current);
    321	assert(td);
    322	assert(td->name);
    323	assert(td->run);
    324
    325	/* Default result is FAIL if test setup fails */
    326	td->result = KSFT_FAIL;
    327	if (td->setup)
    328		return td->setup(td);
    329	else
    330		return default_setup(td);
    331}
    332
    333int test_run(struct tdescr *td)
    334{
    335	if (td->trigger)
    336		return td->trigger(td);
    337	else if (td->sig_trig)
    338		return default_trigger(td);
    339	else
    340		return td->run(td, NULL, NULL);
    341}
    342
    343void test_result(struct tdescr *td)
    344{
    345	if (td->initialized && td->result != KSFT_SKIP && td->check_result)
    346		td->check_result(td);
    347	default_result(td, 0);
    348}
    349
    350void test_cleanup(struct tdescr *td)
    351{
    352	if (td->cleanup)
    353		td->cleanup(td);
    354}