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_threads.c (6104B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Test for perf events with SIGTRAP across all threads.
      4 *
      5 * Copyright (C) 2021, Google LLC.
      6 */
      7
      8#define _GNU_SOURCE
      9
     10/* We need the latest siginfo from the kernel repo. */
     11#include <sys/types.h>
     12#include <asm/siginfo.h>
     13#define __have_siginfo_t 1
     14#define __have_sigval_t 1
     15#define __have_sigevent_t 1
     16#define __siginfo_t_defined
     17#define __sigval_t_defined
     18#define __sigevent_t_defined
     19#define _BITS_SIGINFO_CONSTS_H 1
     20#define _BITS_SIGEVENT_CONSTS_H 1
     21
     22#include <stdbool.h>
     23#include <stddef.h>
     24#include <stdint.h>
     25#include <stdio.h>
     26#include <linux/hw_breakpoint.h>
     27#include <linux/perf_event.h>
     28#include <pthread.h>
     29#include <signal.h>
     30#include <sys/ioctl.h>
     31#include <sys/syscall.h>
     32#include <unistd.h>
     33
     34#include "../kselftest_harness.h"
     35
     36#define NUM_THREADS 5
     37
     38/* Data shared between test body, threads, and signal handler. */
     39static struct {
     40	int tids_want_signal;		/* Which threads still want a signal. */
     41	int signal_count;		/* Sanity check number of signals received. */
     42	volatile int iterate_on;	/* Variable to set breakpoint on. */
     43	siginfo_t first_siginfo;	/* First observed siginfo_t. */
     44} ctx;
     45
     46/* Unique value to check si_perf_data is correctly set from perf_event_attr::sig_data. */
     47#define TEST_SIG_DATA(addr, id) (~(unsigned long)(addr) + id)
     48
     49static struct perf_event_attr make_event_attr(bool enabled, volatile void *addr,
     50					      unsigned long id)
     51{
     52	struct perf_event_attr attr = {
     53		.type		= PERF_TYPE_BREAKPOINT,
     54		.size		= sizeof(attr),
     55		.sample_period	= 1,
     56		.disabled	= !enabled,
     57		.bp_addr	= (unsigned long)addr,
     58		.bp_type	= HW_BREAKPOINT_RW,
     59		.bp_len		= HW_BREAKPOINT_LEN_1,
     60		.inherit	= 1, /* Children inherit events ... */
     61		.inherit_thread = 1, /* ... but only cloned with CLONE_THREAD. */
     62		.remove_on_exec = 1, /* Required by sigtrap. */
     63		.sigtrap	= 1, /* Request synchronous SIGTRAP on event. */
     64		.sig_data	= TEST_SIG_DATA(addr, id),
     65	};
     66	return attr;
     67}
     68
     69static void sigtrap_handler(int signum, siginfo_t *info, void *ucontext)
     70{
     71	if (info->si_code != TRAP_PERF) {
     72		fprintf(stderr, "%s: unexpected si_code %d\n", __func__, info->si_code);
     73		return;
     74	}
     75
     76	/*
     77	 * The data in siginfo_t we're interested in should all be the same
     78	 * across threads.
     79	 */
     80	if (!__atomic_fetch_add(&ctx.signal_count, 1, __ATOMIC_RELAXED))
     81		ctx.first_siginfo = *info;
     82	__atomic_fetch_sub(&ctx.tids_want_signal, syscall(__NR_gettid), __ATOMIC_RELAXED);
     83}
     84
     85static void *test_thread(void *arg)
     86{
     87	pthread_barrier_t *barrier = (pthread_barrier_t *)arg;
     88	pid_t tid = syscall(__NR_gettid);
     89	int iter;
     90	int i;
     91
     92	pthread_barrier_wait(barrier);
     93
     94	__atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
     95	iter = ctx.iterate_on; /* read */
     96	for (i = 0; i < iter - 1; i++) {
     97		__atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
     98		ctx.iterate_on = iter; /* idempotent write */
     99	}
    100
    101	return NULL;
    102}
    103
    104FIXTURE(sigtrap_threads)
    105{
    106	struct sigaction oldact;
    107	pthread_t threads[NUM_THREADS];
    108	pthread_barrier_t barrier;
    109	int fd;
    110};
    111
    112FIXTURE_SETUP(sigtrap_threads)
    113{
    114	struct perf_event_attr attr = make_event_attr(false, &ctx.iterate_on, 0);
    115	struct sigaction action = {};
    116	int i;
    117
    118	memset(&ctx, 0, sizeof(ctx));
    119
    120	/* Initialize sigtrap handler. */
    121	action.sa_flags = SA_SIGINFO | SA_NODEFER;
    122	action.sa_sigaction = sigtrap_handler;
    123	sigemptyset(&action.sa_mask);
    124	ASSERT_EQ(sigaction(SIGTRAP, &action, &self->oldact), 0);
    125
    126	/* Initialize perf event. */
    127	self->fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC);
    128	ASSERT_NE(self->fd, -1);
    129
    130	/* Spawn threads inheriting perf event. */
    131	pthread_barrier_init(&self->barrier, NULL, NUM_THREADS + 1);
    132	for (i = 0; i < NUM_THREADS; i++)
    133		ASSERT_EQ(pthread_create(&self->threads[i], NULL, test_thread, &self->barrier), 0);
    134}
    135
    136FIXTURE_TEARDOWN(sigtrap_threads)
    137{
    138	pthread_barrier_destroy(&self->barrier);
    139	close(self->fd);
    140	sigaction(SIGTRAP, &self->oldact, NULL);
    141}
    142
    143static void run_test_threads(struct __test_metadata *_metadata,
    144			     FIXTURE_DATA(sigtrap_threads) *self)
    145{
    146	int i;
    147
    148	pthread_barrier_wait(&self->barrier);
    149	for (i = 0; i < NUM_THREADS; i++)
    150		ASSERT_EQ(pthread_join(self->threads[i], NULL), 0);
    151}
    152
    153TEST_F(sigtrap_threads, remain_disabled)
    154{
    155	run_test_threads(_metadata, self);
    156	EXPECT_EQ(ctx.signal_count, 0);
    157	EXPECT_NE(ctx.tids_want_signal, 0);
    158}
    159
    160TEST_F(sigtrap_threads, enable_event)
    161{
    162	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
    163	run_test_threads(_metadata, self);
    164
    165	EXPECT_EQ(ctx.signal_count, NUM_THREADS);
    166	EXPECT_EQ(ctx.tids_want_signal, 0);
    167	EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
    168	EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
    169	EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 0));
    170
    171	/* Check enabled for parent. */
    172	ctx.iterate_on = 0;
    173	EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);
    174}
    175
    176/* Test that modification propagates to all inherited events. */
    177TEST_F(sigtrap_threads, modify_and_enable_event)
    178{
    179	struct perf_event_attr new_attr = make_event_attr(true, &ctx.iterate_on, 42);
    180
    181	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr), 0);
    182	run_test_threads(_metadata, self);
    183
    184	EXPECT_EQ(ctx.signal_count, NUM_THREADS);
    185	EXPECT_EQ(ctx.tids_want_signal, 0);
    186	EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
    187	EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
    188	EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 42));
    189
    190	/* Check enabled for parent. */
    191	ctx.iterate_on = 0;
    192	EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);
    193}
    194
    195/* Stress test event + signal handling. */
    196TEST_F(sigtrap_threads, signal_stress)
    197{
    198	ctx.iterate_on = 3000;
    199
    200	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
    201	run_test_threads(_metadata, self);
    202	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_DISABLE, 0), 0);
    203
    204	EXPECT_EQ(ctx.signal_count, NUM_THREADS * ctx.iterate_on);
    205	EXPECT_EQ(ctx.tids_want_signal, 0);
    206	EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
    207	EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
    208	EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 0));
    209}
    210
    211TEST_HARNESS_MAIN