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

dso-data.c (8752B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <dirent.h>
      3#include <stdlib.h>
      4#include <linux/kernel.h>
      5#include <linux/types.h>
      6#include <sys/stat.h>
      7#include <fcntl.h>
      8#include <string.h>
      9#include <sys/time.h>
     10#include <sys/resource.h>
     11#include <api/fs/fs.h>
     12#include "dso.h"
     13#include "machine.h"
     14#include "symbol.h"
     15#include "tests.h"
     16#include "debug.h"
     17
     18static char *test_file(int size)
     19{
     20#define TEMPL "/tmp/perf-test-XXXXXX"
     21	static char buf_templ[sizeof(TEMPL)];
     22	char *templ = buf_templ;
     23	int fd, i;
     24	unsigned char *buf;
     25
     26	strcpy(buf_templ, TEMPL);
     27#undef TEMPL
     28
     29	fd = mkstemp(templ);
     30	if (fd < 0) {
     31		perror("mkstemp failed");
     32		return NULL;
     33	}
     34
     35	buf = malloc(size);
     36	if (!buf) {
     37		close(fd);
     38		return NULL;
     39	}
     40
     41	for (i = 0; i < size; i++)
     42		buf[i] = (unsigned char) ((int) i % 10);
     43
     44	if (size != write(fd, buf, size))
     45		templ = NULL;
     46
     47	free(buf);
     48	close(fd);
     49	return templ;
     50}
     51
     52#define TEST_FILE_SIZE (DSO__DATA_CACHE_SIZE * 20)
     53
     54struct test_data_offset {
     55	off_t offset;
     56	u8 data[10];
     57	int size;
     58};
     59
     60struct test_data_offset offsets[] = {
     61	/* Fill first cache page. */
     62	{
     63		.offset = 10,
     64		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
     65		.size   = 10,
     66	},
     67	/* Read first cache page. */
     68	{
     69		.offset = 10,
     70		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
     71		.size   = 10,
     72	},
     73	/* Fill cache boundary pages. */
     74	{
     75		.offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10,
     76		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
     77		.size   = 10,
     78	},
     79	/* Read cache boundary pages. */
     80	{
     81		.offset = DSO__DATA_CACHE_SIZE - DSO__DATA_CACHE_SIZE % 10,
     82		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
     83		.size   = 10,
     84	},
     85	/* Fill final cache page. */
     86	{
     87		.offset = TEST_FILE_SIZE - 10,
     88		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
     89		.size   = 10,
     90	},
     91	/* Read final cache page. */
     92	{
     93		.offset = TEST_FILE_SIZE - 10,
     94		.data   = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
     95		.size   = 10,
     96	},
     97	/* Read final cache page. */
     98	{
     99		.offset = TEST_FILE_SIZE - 3,
    100		.data   = { 7, 8, 9, 0, 0, 0, 0, 0, 0, 0 },
    101		.size   = 3,
    102	},
    103};
    104
    105/* move it from util/dso.c for compatibility */
    106static int dso__data_fd(struct dso *dso, struct machine *machine)
    107{
    108	int fd = dso__data_get_fd(dso, machine);
    109
    110	if (fd >= 0)
    111		dso__data_put_fd(dso);
    112
    113	return fd;
    114}
    115
    116static int test__dso_data(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
    117{
    118	struct machine machine;
    119	struct dso *dso;
    120	char *file = test_file(TEST_FILE_SIZE);
    121	size_t i;
    122
    123	TEST_ASSERT_VAL("No test file", file);
    124
    125	memset(&machine, 0, sizeof(machine));
    126
    127	dso = dso__new((const char *)file);
    128
    129	TEST_ASSERT_VAL("Failed to access to dso",
    130			dso__data_fd(dso, &machine) >= 0);
    131
    132	/* Basic 10 bytes tests. */
    133	for (i = 0; i < ARRAY_SIZE(offsets); i++) {
    134		struct test_data_offset *data = &offsets[i];
    135		ssize_t size;
    136		u8 buf[10];
    137
    138		memset(buf, 0, 10);
    139		size = dso__data_read_offset(dso, &machine, data->offset,
    140				     buf, 10);
    141
    142		TEST_ASSERT_VAL("Wrong size", size == data->size);
    143		TEST_ASSERT_VAL("Wrong data", !memcmp(buf, data->data, 10));
    144	}
    145
    146	/* Read cross multiple cache pages. */
    147	{
    148		ssize_t size;
    149		int c;
    150		u8 *buf;
    151
    152		buf = malloc(TEST_FILE_SIZE);
    153		TEST_ASSERT_VAL("ENOMEM\n", buf);
    154
    155		/* First iteration to fill caches, second one to read them. */
    156		for (c = 0; c < 2; c++) {
    157			memset(buf, 0, TEST_FILE_SIZE);
    158			size = dso__data_read_offset(dso, &machine, 10,
    159						     buf, TEST_FILE_SIZE);
    160
    161			TEST_ASSERT_VAL("Wrong size",
    162				size == (TEST_FILE_SIZE - 10));
    163
    164			for (i = 0; i < (size_t)size; i++)
    165				TEST_ASSERT_VAL("Wrong data",
    166					buf[i] == (i % 10));
    167		}
    168
    169		free(buf);
    170	}
    171
    172	dso__put(dso);
    173	unlink(file);
    174	return 0;
    175}
    176
    177static long open_files_cnt(void)
    178{
    179	char path[PATH_MAX];
    180	struct dirent *dent;
    181	DIR *dir;
    182	long nr = 0;
    183
    184	scnprintf(path, PATH_MAX, "%s/self/fd", procfs__mountpoint());
    185	pr_debug("fd path: %s\n", path);
    186
    187	dir = opendir(path);
    188	TEST_ASSERT_VAL("failed to open fd directory", dir);
    189
    190	while ((dent = readdir(dir)) != NULL) {
    191		if (!strcmp(dent->d_name, ".") ||
    192		    !strcmp(dent->d_name, ".."))
    193			continue;
    194
    195		nr++;
    196	}
    197
    198	closedir(dir);
    199	return nr - 1;
    200}
    201
    202static struct dso **dsos;
    203
    204static int dsos__create(int cnt, int size)
    205{
    206	int i;
    207
    208	dsos = malloc(sizeof(*dsos) * cnt);
    209	TEST_ASSERT_VAL("failed to alloc dsos array", dsos);
    210
    211	for (i = 0; i < cnt; i++) {
    212		char *file;
    213
    214		file = test_file(size);
    215		TEST_ASSERT_VAL("failed to get dso file", file);
    216
    217		dsos[i] = dso__new(file);
    218		TEST_ASSERT_VAL("failed to get dso", dsos[i]);
    219	}
    220
    221	return 0;
    222}
    223
    224static void dsos__delete(int cnt)
    225{
    226	int i;
    227
    228	for (i = 0; i < cnt; i++) {
    229		struct dso *dso = dsos[i];
    230
    231		unlink(dso->name);
    232		dso__put(dso);
    233	}
    234
    235	free(dsos);
    236}
    237
    238static int set_fd_limit(int n)
    239{
    240	struct rlimit rlim;
    241
    242	if (getrlimit(RLIMIT_NOFILE, &rlim))
    243		return -1;
    244
    245	pr_debug("file limit %ld, new %d\n", (long) rlim.rlim_cur, n);
    246
    247	rlim.rlim_cur = n;
    248	return setrlimit(RLIMIT_NOFILE, &rlim);
    249}
    250
    251static int test__dso_data_cache(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
    252{
    253	struct machine machine;
    254	long nr_end, nr = open_files_cnt();
    255	int dso_cnt, limit, i, fd;
    256
    257	/* Rest the internal dso open counter limit. */
    258	reset_fd_limit();
    259
    260	memset(&machine, 0, sizeof(machine));
    261
    262	/* set as system limit */
    263	limit = nr * 4;
    264	TEST_ASSERT_VAL("failed to set file limit", !set_fd_limit(limit));
    265
    266	/* and this is now our dso open FDs limit */
    267	dso_cnt = limit / 2;
    268	TEST_ASSERT_VAL("failed to create dsos\n",
    269		!dsos__create(dso_cnt, TEST_FILE_SIZE));
    270
    271	for (i = 0; i < (dso_cnt - 1); i++) {
    272		struct dso *dso = dsos[i];
    273
    274		/*
    275		 * Open dsos via dso__data_fd(), it opens the data
    276		 * file and keep it open (unless open file limit).
    277		 */
    278		fd = dso__data_fd(dso, &machine);
    279		TEST_ASSERT_VAL("failed to get fd", fd > 0);
    280
    281		if (i % 2) {
    282			#define BUFSIZE 10
    283			u8 buf[BUFSIZE];
    284			ssize_t n;
    285
    286			n = dso__data_read_offset(dso, &machine, 0, buf, BUFSIZE);
    287			TEST_ASSERT_VAL("failed to read dso", n == BUFSIZE);
    288		}
    289	}
    290
    291	/* verify the first one is already open */
    292	TEST_ASSERT_VAL("dsos[0] is not open", dsos[0]->data.fd != -1);
    293
    294	/* open +1 dso to reach the allowed limit */
    295	fd = dso__data_fd(dsos[i], &machine);
    296	TEST_ASSERT_VAL("failed to get fd", fd > 0);
    297
    298	/* should force the first one to be closed */
    299	TEST_ASSERT_VAL("failed to close dsos[0]", dsos[0]->data.fd == -1);
    300
    301	/* cleanup everything */
    302	dsos__delete(dso_cnt);
    303
    304	/* Make sure we did not leak any file descriptor. */
    305	nr_end = open_files_cnt();
    306	pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
    307	TEST_ASSERT_VAL("failed leaking files", nr == nr_end);
    308	return 0;
    309}
    310
    311static long new_limit(int count)
    312{
    313	int fd = open("/dev/null", O_RDONLY);
    314	long ret = fd;
    315	if (count > 0)
    316		ret = new_limit(--count);
    317	close(fd);
    318	return ret;
    319}
    320
    321static int test__dso_data_reopen(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
    322{
    323	struct machine machine;
    324	long nr_end, nr = open_files_cnt(), lim = new_limit(3);
    325	int fd, fd_extra;
    326
    327#define dso_0 (dsos[0])
    328#define dso_1 (dsos[1])
    329#define dso_2 (dsos[2])
    330
    331	/* Rest the internal dso open counter limit. */
    332	reset_fd_limit();
    333
    334	memset(&machine, 0, sizeof(machine));
    335
    336	/*
    337	 * Test scenario:
    338	 * - create 3 dso objects
    339	 * - set process file descriptor limit to current
    340	 *   files count + 3
    341	 * - test that the first dso gets closed when we
    342	 *   reach the files count limit
    343	 */
    344
    345	/* Make sure we are able to open 3 fds anyway */
    346	TEST_ASSERT_VAL("failed to set file limit",
    347			!set_fd_limit((lim)));
    348
    349	TEST_ASSERT_VAL("failed to create dsos\n", !dsos__create(3, TEST_FILE_SIZE));
    350
    351	/* open dso_0 */
    352	fd = dso__data_fd(dso_0, &machine);
    353	TEST_ASSERT_VAL("failed to get fd", fd > 0);
    354
    355	/* open dso_1 */
    356	fd = dso__data_fd(dso_1, &machine);
    357	TEST_ASSERT_VAL("failed to get fd", fd > 0);
    358
    359	/*
    360	 * open extra file descriptor and we just
    361	 * reached the files count limit
    362	 */
    363	fd_extra = open("/dev/null", O_RDONLY);
    364	TEST_ASSERT_VAL("failed to open extra fd", fd_extra > 0);
    365
    366	/* open dso_2 */
    367	fd = dso__data_fd(dso_2, &machine);
    368	TEST_ASSERT_VAL("failed to get fd", fd > 0);
    369
    370	/*
    371	 * dso_0 should get closed, because we reached
    372	 * the file descriptor limit
    373	 */
    374	TEST_ASSERT_VAL("failed to close dso_0", dso_0->data.fd == -1);
    375
    376	/* open dso_0 */
    377	fd = dso__data_fd(dso_0, &machine);
    378	TEST_ASSERT_VAL("failed to get fd", fd > 0);
    379
    380	/*
    381	 * dso_1 should get closed, because we reached
    382	 * the file descriptor limit
    383	 */
    384	TEST_ASSERT_VAL("failed to close dso_1", dso_1->data.fd == -1);
    385
    386	/* cleanup everything */
    387	close(fd_extra);
    388	dsos__delete(3);
    389
    390	/* Make sure we did not leak any file descriptor. */
    391	nr_end = open_files_cnt();
    392	pr_debug("nr start %ld, nr stop %ld\n", nr, nr_end);
    393	TEST_ASSERT_VAL("failed leaking files", nr == nr_end);
    394	return 0;
    395}
    396
    397DEFINE_SUITE("DSO data read", dso_data);
    398DEFINE_SUITE("DSO data cache", dso_data_cache);
    399DEFINE_SUITE("DSO data reopen", dso_data_reopen);