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

sigtrap.c (4739B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Basic test for sigtrap support.
      4 *
      5 * Copyright (C) 2021, Google LLC.
      6 */
      7
      8#include <errno.h>
      9#include <stdint.h>
     10#include <stdlib.h>
     11#include <linux/hw_breakpoint.h>
     12#include <linux/string.h>
     13#include <pthread.h>
     14#include <signal.h>
     15#include <sys/ioctl.h>
     16#include <sys/syscall.h>
     17#include <unistd.h>
     18
     19#include "cloexec.h"
     20#include "debug.h"
     21#include "event.h"
     22#include "tests.h"
     23#include "../perf-sys.h"
     24
     25#define NUM_THREADS 5
     26
     27static struct {
     28	int tids_want_signal;		/* Which threads still want a signal. */
     29	int signal_count;		/* Sanity check number of signals received. */
     30	volatile int iterate_on;	/* Variable to set breakpoint on. */
     31	siginfo_t first_siginfo;	/* First observed siginfo_t. */
     32} ctx;
     33
     34#define TEST_SIG_DATA (~(unsigned long)(&ctx.iterate_on))
     35
     36static struct perf_event_attr make_event_attr(void)
     37{
     38	struct perf_event_attr attr = {
     39		.type		= PERF_TYPE_BREAKPOINT,
     40		.size		= sizeof(attr),
     41		.sample_period	= 1,
     42		.disabled	= 1,
     43		.bp_addr	= (unsigned long)&ctx.iterate_on,
     44		.bp_type	= HW_BREAKPOINT_RW,
     45		.bp_len		= HW_BREAKPOINT_LEN_1,
     46		.inherit	= 1, /* Children inherit events ... */
     47		.inherit_thread = 1, /* ... but only cloned with CLONE_THREAD. */
     48		.remove_on_exec = 1, /* Required by sigtrap. */
     49		.sigtrap	= 1, /* Request synchronous SIGTRAP on event. */
     50		.sig_data	= TEST_SIG_DATA,
     51		.exclude_kernel = 1, /* To allow */
     52		.exclude_hv     = 1, /* running as !root */
     53	};
     54	return attr;
     55}
     56
     57static void
     58sigtrap_handler(int signum __maybe_unused, siginfo_t *info, void *ucontext __maybe_unused)
     59{
     60	if (!__atomic_fetch_add(&ctx.signal_count, 1, __ATOMIC_RELAXED))
     61		ctx.first_siginfo = *info;
     62	__atomic_fetch_sub(&ctx.tids_want_signal, syscall(SYS_gettid), __ATOMIC_RELAXED);
     63}
     64
     65static void *test_thread(void *arg)
     66{
     67	pthread_barrier_t *barrier = (pthread_barrier_t *)arg;
     68	pid_t tid = syscall(SYS_gettid);
     69	int i;
     70
     71	pthread_barrier_wait(barrier);
     72
     73	__atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
     74	for (i = 0; i < ctx.iterate_on - 1; i++)
     75		__atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
     76
     77	return NULL;
     78}
     79
     80static int run_test_threads(pthread_t *threads, pthread_barrier_t *barrier)
     81{
     82	int i;
     83
     84	pthread_barrier_wait(barrier);
     85	for (i = 0; i < NUM_THREADS; i++)
     86		TEST_ASSERT_EQUAL("pthread_join() failed", pthread_join(threads[i], NULL), 0);
     87
     88	return TEST_OK;
     89}
     90
     91static int run_stress_test(int fd, pthread_t *threads, pthread_barrier_t *barrier)
     92{
     93	int ret;
     94
     95	ctx.iterate_on = 3000;
     96
     97	TEST_ASSERT_EQUAL("misfired signal?", ctx.signal_count, 0);
     98	TEST_ASSERT_EQUAL("enable failed", ioctl(fd, PERF_EVENT_IOC_ENABLE, 0), 0);
     99	ret = run_test_threads(threads, barrier);
    100	TEST_ASSERT_EQUAL("disable failed", ioctl(fd, PERF_EVENT_IOC_DISABLE, 0), 0);
    101
    102	TEST_ASSERT_EQUAL("unexpected sigtraps", ctx.signal_count, NUM_THREADS * ctx.iterate_on);
    103	TEST_ASSERT_EQUAL("missing signals or incorrectly delivered", ctx.tids_want_signal, 0);
    104	TEST_ASSERT_VAL("unexpected si_addr", ctx.first_siginfo.si_addr == &ctx.iterate_on);
    105#if 0 /* FIXME: enable when libc's signal.h has si_perf_{type,data} */
    106	TEST_ASSERT_EQUAL("unexpected si_perf_type", ctx.first_siginfo.si_perf_type,
    107			  PERF_TYPE_BREAKPOINT);
    108	TEST_ASSERT_EQUAL("unexpected si_perf_data", ctx.first_siginfo.si_perf_data,
    109			  TEST_SIG_DATA);
    110#endif
    111
    112	return ret;
    113}
    114
    115static int test__sigtrap(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
    116{
    117	struct perf_event_attr attr = make_event_attr();
    118	struct sigaction action = {};
    119	struct sigaction oldact;
    120	pthread_t threads[NUM_THREADS];
    121	pthread_barrier_t barrier;
    122	char sbuf[STRERR_BUFSIZE];
    123	int i, fd, ret = TEST_FAIL;
    124
    125	if (!BP_SIGNAL_IS_SUPPORTED) {
    126		pr_debug("Test not supported on this architecture");
    127		return TEST_SKIP;
    128	}
    129
    130	pthread_barrier_init(&barrier, NULL, NUM_THREADS + 1);
    131
    132	action.sa_flags = SA_SIGINFO | SA_NODEFER;
    133	action.sa_sigaction = sigtrap_handler;
    134	sigemptyset(&action.sa_mask);
    135	if (sigaction(SIGTRAP, &action, &oldact)) {
    136		pr_debug("FAILED sigaction(): %s\n", str_error_r(errno, sbuf, sizeof(sbuf)));
    137		goto out;
    138	}
    139
    140	fd = sys_perf_event_open(&attr, 0, -1, -1, perf_event_open_cloexec_flag());
    141	if (fd < 0) {
    142		pr_debug("FAILED sys_perf_event_open(): %s\n", str_error_r(errno, sbuf, sizeof(sbuf)));
    143		goto out_restore_sigaction;
    144	}
    145
    146	for (i = 0; i < NUM_THREADS; i++) {
    147		if (pthread_create(&threads[i], NULL, test_thread, &barrier)) {
    148			pr_debug("FAILED pthread_create(): %s\n", str_error_r(errno, sbuf, sizeof(sbuf)));
    149			goto out_close_perf_event;
    150		}
    151	}
    152
    153	ret = run_stress_test(fd, threads, &barrier);
    154
    155out_close_perf_event:
    156	close(fd);
    157out_restore_sigaction:
    158	sigaction(SIGTRAP, &oldact, NULL);
    159out:
    160	pthread_barrier_destroy(&barrier);
    161	return ret;
    162}
    163
    164DEFINE_SUITE("Sigtrap", sigtrap);