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

ftrace_test.c (10946B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * User Events FTrace Test Program
      4 *
      5 * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
      6 */
      7
      8#include <errno.h>
      9#include <linux/user_events.h>
     10#include <stdio.h>
     11#include <stdlib.h>
     12#include <fcntl.h>
     13#include <sys/ioctl.h>
     14#include <sys/stat.h>
     15#include <unistd.h>
     16
     17#include "../kselftest_harness.h"
     18
     19const char *data_file = "/sys/kernel/debug/tracing/user_events_data";
     20const char *status_file = "/sys/kernel/debug/tracing/user_events_status";
     21const char *enable_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/enable";
     22const char *trace_file = "/sys/kernel/debug/tracing/trace";
     23const char *fmt_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/format";
     24
     25static int trace_bytes(void)
     26{
     27	int fd = open(trace_file, O_RDONLY);
     28	char buf[256];
     29	int bytes = 0, got;
     30
     31	if (fd == -1)
     32		return -1;
     33
     34	while (true) {
     35		got = read(fd, buf, sizeof(buf));
     36
     37		if (got == -1)
     38			return -1;
     39
     40		if (got == 0)
     41			break;
     42
     43		bytes += got;
     44	}
     45
     46	close(fd);
     47
     48	return bytes;
     49}
     50
     51static int skip_until_empty_line(FILE *fp)
     52{
     53	int c, last = 0;
     54
     55	while (true) {
     56		c = getc(fp);
     57
     58		if (c == EOF)
     59			break;
     60
     61		if (last == '\n' && c == '\n')
     62			return 0;
     63
     64		last = c;
     65	}
     66
     67	return -1;
     68}
     69
     70static int get_print_fmt(char *buffer, int len)
     71{
     72	FILE *fp = fopen(fmt_file, "r");
     73	char *newline;
     74
     75	if (!fp)
     76		return -1;
     77
     78	/* Read until empty line (Skip Common) */
     79	if (skip_until_empty_line(fp) < 0)
     80		goto err;
     81
     82	/* Read until empty line (Skip Properties) */
     83	if (skip_until_empty_line(fp) < 0)
     84		goto err;
     85
     86	/* Read in print_fmt: */
     87	if (fgets(buffer, len, fp) == NULL)
     88		goto err;
     89
     90	newline = strchr(buffer, '\n');
     91
     92	if (newline)
     93		*newline = '\0';
     94
     95	fclose(fp);
     96
     97	return 0;
     98err:
     99	fclose(fp);
    100
    101	return -1;
    102}
    103
    104static int clear(void)
    105{
    106	int fd = open(data_file, O_RDWR);
    107
    108	if (fd == -1)
    109		return -1;
    110
    111	if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
    112		if (errno != ENOENT)
    113			return -1;
    114
    115	close(fd);
    116
    117	return 0;
    118}
    119
    120static int check_print_fmt(const char *event, const char *expected)
    121{
    122	struct user_reg reg = {0};
    123	char print_fmt[256];
    124	int ret;
    125	int fd;
    126
    127	/* Ensure cleared */
    128	ret = clear();
    129
    130	if (ret != 0)
    131		return ret;
    132
    133	fd = open(data_file, O_RDWR);
    134
    135	if (fd == -1)
    136		return fd;
    137
    138	reg.size = sizeof(reg);
    139	reg.name_args = (__u64)event;
    140
    141	/* Register should work */
    142	ret = ioctl(fd, DIAG_IOCSREG, &reg);
    143
    144	close(fd);
    145
    146	if (ret != 0)
    147		return ret;
    148
    149	/* Ensure correct print_fmt */
    150	ret = get_print_fmt(print_fmt, sizeof(print_fmt));
    151
    152	if (ret != 0)
    153		return ret;
    154
    155	return strcmp(print_fmt, expected);
    156}
    157
    158FIXTURE(user) {
    159	int status_fd;
    160	int data_fd;
    161	int enable_fd;
    162};
    163
    164FIXTURE_SETUP(user) {
    165	self->status_fd = open(status_file, O_RDONLY);
    166	ASSERT_NE(-1, self->status_fd);
    167
    168	self->data_fd = open(data_file, O_RDWR);
    169	ASSERT_NE(-1, self->data_fd);
    170
    171	self->enable_fd = -1;
    172}
    173
    174FIXTURE_TEARDOWN(user) {
    175	close(self->status_fd);
    176	close(self->data_fd);
    177
    178	if (self->enable_fd != -1) {
    179		write(self->enable_fd, "0", sizeof("0"));
    180		close(self->enable_fd);
    181	}
    182
    183	ASSERT_EQ(0, clear());
    184}
    185
    186TEST_F(user, register_events) {
    187	struct user_reg reg = {0};
    188	int page_size = sysconf(_SC_PAGESIZE);
    189	char *status_page;
    190
    191	reg.size = sizeof(reg);
    192	reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
    193
    194	status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED,
    195			   self->status_fd, 0);
    196
    197	/* Register should work */
    198	ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
    199	ASSERT_EQ(0, reg.write_index);
    200	ASSERT_NE(0, reg.status_index);
    201
    202	/* Multiple registers should result in same index */
    203	ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
    204	ASSERT_EQ(0, reg.write_index);
    205	ASSERT_NE(0, reg.status_index);
    206
    207	/* Ensure disabled */
    208	self->enable_fd = open(enable_file, O_RDWR);
    209	ASSERT_NE(-1, self->enable_fd);
    210	ASSERT_NE(-1, write(self->enable_fd, "0", sizeof("0")))
    211
    212	/* MMAP should work and be zero'd */
    213	ASSERT_NE(MAP_FAILED, status_page);
    214	ASSERT_NE(NULL, status_page);
    215	ASSERT_EQ(0, status_page[reg.status_index]);
    216
    217	/* Enable event and ensure bits updated in status */
    218	ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
    219	ASSERT_EQ(EVENT_STATUS_FTRACE, status_page[reg.status_index]);
    220
    221	/* Disable event and ensure bits updated in status */
    222	ASSERT_NE(-1, write(self->enable_fd, "0", sizeof("0")))
    223	ASSERT_EQ(0, status_page[reg.status_index]);
    224
    225	/* File still open should return -EBUSY for delete */
    226	ASSERT_EQ(-1, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event"));
    227	ASSERT_EQ(EBUSY, errno);
    228
    229	/* Delete should work only after close */
    230	close(self->data_fd);
    231	self->data_fd = open(data_file, O_RDWR);
    232	ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event"));
    233
    234	/* Unmap should work */
    235	ASSERT_EQ(0, munmap(status_page, page_size));
    236}
    237
    238TEST_F(user, write_events) {
    239	struct user_reg reg = {0};
    240	struct iovec io[3];
    241	__u32 field1, field2;
    242	int before = 0, after = 0;
    243
    244	reg.size = sizeof(reg);
    245	reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
    246
    247	field1 = 1;
    248	field2 = 2;
    249
    250	io[0].iov_base = &reg.write_index;
    251	io[0].iov_len = sizeof(reg.write_index);
    252	io[1].iov_base = &field1;
    253	io[1].iov_len = sizeof(field1);
    254	io[2].iov_base = &field2;
    255	io[2].iov_len = sizeof(field2);
    256
    257	/* Register should work */
    258	ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
    259	ASSERT_EQ(0, reg.write_index);
    260	ASSERT_NE(0, reg.status_index);
    261
    262	/* Write should fail on invalid slot with ENOENT */
    263	io[0].iov_base = &field2;
    264	io[0].iov_len = sizeof(field2);
    265	ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
    266	ASSERT_EQ(ENOENT, errno);
    267	io[0].iov_base = &reg.write_index;
    268	io[0].iov_len = sizeof(reg.write_index);
    269
    270	/* Enable event */
    271	self->enable_fd = open(enable_file, O_RDWR);
    272	ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
    273
    274	/* Write should make it out to ftrace buffers */
    275	before = trace_bytes();
    276	ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 3));
    277	after = trace_bytes();
    278	ASSERT_GT(after, before);
    279}
    280
    281TEST_F(user, write_fault) {
    282	struct user_reg reg = {0};
    283	struct iovec io[2];
    284	int l = sizeof(__u64);
    285	void *anon;
    286
    287	reg.size = sizeof(reg);
    288	reg.name_args = (__u64)"__test_event u64 anon";
    289
    290	anon = mmap(NULL, l, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    291	ASSERT_NE(MAP_FAILED, anon);
    292
    293	io[0].iov_base = &reg.write_index;
    294	io[0].iov_len = sizeof(reg.write_index);
    295	io[1].iov_base = anon;
    296	io[1].iov_len = l;
    297
    298	/* Register should work */
    299	ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
    300	ASSERT_EQ(0, reg.write_index);
    301	ASSERT_NE(0, reg.status_index);
    302
    303	/* Write should work normally */
    304	ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 2));
    305
    306	/* Faulted data should zero fill and work */
    307	ASSERT_EQ(0, madvise(anon, l, MADV_DONTNEED));
    308	ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 2));
    309	ASSERT_EQ(0, munmap(anon, l));
    310}
    311
    312TEST_F(user, write_validator) {
    313	struct user_reg reg = {0};
    314	struct iovec io[3];
    315	int loc, bytes;
    316	char data[8];
    317	int before = 0, after = 0;
    318
    319	reg.size = sizeof(reg);
    320	reg.name_args = (__u64)"__test_event __rel_loc char[] data";
    321
    322	/* Register should work */
    323	ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
    324	ASSERT_EQ(0, reg.write_index);
    325	ASSERT_NE(0, reg.status_index);
    326
    327	io[0].iov_base = &reg.write_index;
    328	io[0].iov_len = sizeof(reg.write_index);
    329	io[1].iov_base = &loc;
    330	io[1].iov_len = sizeof(loc);
    331	io[2].iov_base = data;
    332	bytes = snprintf(data, sizeof(data), "Test") + 1;
    333	io[2].iov_len = bytes;
    334
    335	/* Undersized write should fail */
    336	ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 1));
    337	ASSERT_EQ(EINVAL, errno);
    338
    339	/* Enable event */
    340	self->enable_fd = open(enable_file, O_RDWR);
    341	ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
    342
    343	/* Full in-bounds write should work */
    344	before = trace_bytes();
    345	loc = DYN_LOC(0, bytes);
    346	ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 3));
    347	after = trace_bytes();
    348	ASSERT_GT(after, before);
    349
    350	/* Out of bounds write should fault (offset way out) */
    351	loc = DYN_LOC(1024, bytes);
    352	ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
    353	ASSERT_EQ(EFAULT, errno);
    354
    355	/* Out of bounds write should fault (offset 1 byte out) */
    356	loc = DYN_LOC(1, bytes);
    357	ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
    358	ASSERT_EQ(EFAULT, errno);
    359
    360	/* Out of bounds write should fault (size way out) */
    361	loc = DYN_LOC(0, bytes + 1024);
    362	ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
    363	ASSERT_EQ(EFAULT, errno);
    364
    365	/* Out of bounds write should fault (size 1 byte out) */
    366	loc = DYN_LOC(0, bytes + 1);
    367	ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
    368	ASSERT_EQ(EFAULT, errno);
    369
    370	/* Non-Null should fault */
    371	memset(data, 'A', sizeof(data));
    372	loc = DYN_LOC(0, bytes);
    373	ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
    374	ASSERT_EQ(EFAULT, errno);
    375}
    376
    377TEST_F(user, print_fmt) {
    378	int ret;
    379
    380	ret = check_print_fmt("__test_event __rel_loc char[] data",
    381			      "print fmt: \"data=%s\", __get_rel_str(data)");
    382	ASSERT_EQ(0, ret);
    383
    384	ret = check_print_fmt("__test_event __data_loc char[] data",
    385			      "print fmt: \"data=%s\", __get_str(data)");
    386	ASSERT_EQ(0, ret);
    387
    388	ret = check_print_fmt("__test_event s64 data",
    389			      "print fmt: \"data=%lld\", REC->data");
    390	ASSERT_EQ(0, ret);
    391
    392	ret = check_print_fmt("__test_event u64 data",
    393			      "print fmt: \"data=%llu\", REC->data");
    394	ASSERT_EQ(0, ret);
    395
    396	ret = check_print_fmt("__test_event s32 data",
    397			      "print fmt: \"data=%d\", REC->data");
    398	ASSERT_EQ(0, ret);
    399
    400	ret = check_print_fmt("__test_event u32 data",
    401			      "print fmt: \"data=%u\", REC->data");
    402	ASSERT_EQ(0, ret);
    403
    404	ret = check_print_fmt("__test_event int data",
    405			      "print fmt: \"data=%d\", REC->data");
    406	ASSERT_EQ(0, ret);
    407
    408	ret = check_print_fmt("__test_event unsigned int data",
    409			      "print fmt: \"data=%u\", REC->data");
    410	ASSERT_EQ(0, ret);
    411
    412	ret = check_print_fmt("__test_event s16 data",
    413			      "print fmt: \"data=%d\", REC->data");
    414	ASSERT_EQ(0, ret);
    415
    416	ret = check_print_fmt("__test_event u16 data",
    417			      "print fmt: \"data=%u\", REC->data");
    418	ASSERT_EQ(0, ret);
    419
    420	ret = check_print_fmt("__test_event short data",
    421			      "print fmt: \"data=%d\", REC->data");
    422	ASSERT_EQ(0, ret);
    423
    424	ret = check_print_fmt("__test_event unsigned short data",
    425			      "print fmt: \"data=%u\", REC->data");
    426	ASSERT_EQ(0, ret);
    427
    428	ret = check_print_fmt("__test_event s8 data",
    429			      "print fmt: \"data=%d\", REC->data");
    430	ASSERT_EQ(0, ret);
    431
    432	ret = check_print_fmt("__test_event u8 data",
    433			      "print fmt: \"data=%u\", REC->data");
    434	ASSERT_EQ(0, ret);
    435
    436	ret = check_print_fmt("__test_event char data",
    437			      "print fmt: \"data=%d\", REC->data");
    438	ASSERT_EQ(0, ret);
    439
    440	ret = check_print_fmt("__test_event unsigned char data",
    441			      "print fmt: \"data=%u\", REC->data");
    442	ASSERT_EQ(0, ret);
    443
    444	ret = check_print_fmt("__test_event char[4] data",
    445			      "print fmt: \"data=%s\", REC->data");
    446	ASSERT_EQ(0, ret);
    447}
    448
    449int main(int argc, char **argv)
    450{
    451	return test_harness_run(argc, argv);
    452}