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

code-reading.c (16041B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <errno.h>
      3#include <linux/kernel.h>
      4#include <linux/types.h>
      5#include <inttypes.h>
      6#include <stdlib.h>
      7#include <unistd.h>
      8#include <stdio.h>
      9#include <string.h>
     10#include <sys/param.h>
     11#include <perf/cpumap.h>
     12#include <perf/evlist.h>
     13#include <perf/mmap.h>
     14
     15#include "debug.h"
     16#include "dso.h"
     17#include "env.h"
     18#include "parse-events.h"
     19#include "trace-event.h"
     20#include "evlist.h"
     21#include "evsel.h"
     22#include "thread_map.h"
     23#include "machine.h"
     24#include "map.h"
     25#include "symbol.h"
     26#include "event.h"
     27#include "record.h"
     28#include "util/mmap.h"
     29#include "util/string2.h"
     30#include "util/synthetic-events.h"
     31#include "thread.h"
     32
     33#include "tests.h"
     34
     35#include <linux/ctype.h>
     36
     37#define BUFSZ	1024
     38#define READLEN	128
     39
     40struct state {
     41	u64 done[1024];
     42	size_t done_cnt;
     43};
     44
     45static size_t read_objdump_chunk(const char **line, unsigned char **buf,
     46				 size_t *buf_len)
     47{
     48	size_t bytes_read = 0;
     49	unsigned char *chunk_start = *buf;
     50
     51	/* Read bytes */
     52	while (*buf_len > 0) {
     53		char c1, c2;
     54
     55		/* Get 2 hex digits */
     56		c1 = *(*line)++;
     57		if (!isxdigit(c1))
     58			break;
     59		c2 = *(*line)++;
     60		if (!isxdigit(c2))
     61			break;
     62
     63		/* Store byte and advance buf */
     64		**buf = (hex(c1) << 4) | hex(c2);
     65		(*buf)++;
     66		(*buf_len)--;
     67		bytes_read++;
     68
     69		/* End of chunk? */
     70		if (isspace(**line))
     71			break;
     72	}
     73
     74	/*
     75	 * objdump will display raw insn as LE if code endian
     76	 * is LE and bytes_per_chunk > 1. In that case reverse
     77	 * the chunk we just read.
     78	 *
     79	 * see disassemble_bytes() at binutils/objdump.c for details
     80	 * how objdump chooses display endian)
     81	 */
     82	if (bytes_read > 1 && !bigendian()) {
     83		unsigned char *chunk_end = chunk_start + bytes_read - 1;
     84		unsigned char tmp;
     85
     86		while (chunk_start < chunk_end) {
     87			tmp = *chunk_start;
     88			*chunk_start = *chunk_end;
     89			*chunk_end = tmp;
     90			chunk_start++;
     91			chunk_end--;
     92		}
     93	}
     94
     95	return bytes_read;
     96}
     97
     98static size_t read_objdump_line(const char *line, unsigned char *buf,
     99				size_t buf_len)
    100{
    101	const char *p;
    102	size_t ret, bytes_read = 0;
    103
    104	/* Skip to a colon */
    105	p = strchr(line, ':');
    106	if (!p)
    107		return 0;
    108	p++;
    109
    110	/* Skip initial spaces */
    111	while (*p) {
    112		if (!isspace(*p))
    113			break;
    114		p++;
    115	}
    116
    117	do {
    118		ret = read_objdump_chunk(&p, &buf, &buf_len);
    119		bytes_read += ret;
    120		p++;
    121	} while (ret > 0);
    122
    123	/* return number of successfully read bytes */
    124	return bytes_read;
    125}
    126
    127static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr)
    128{
    129	char *line = NULL;
    130	size_t line_len, off_last = 0;
    131	ssize_t ret;
    132	int err = 0;
    133	u64 addr, last_addr = start_addr;
    134
    135	while (off_last < *len) {
    136		size_t off, read_bytes, written_bytes;
    137		unsigned char tmp[BUFSZ];
    138
    139		ret = getline(&line, &line_len, f);
    140		if (feof(f))
    141			break;
    142		if (ret < 0) {
    143			pr_debug("getline failed\n");
    144			err = -1;
    145			break;
    146		}
    147
    148		/* read objdump data into temporary buffer */
    149		read_bytes = read_objdump_line(line, tmp, sizeof(tmp));
    150		if (!read_bytes)
    151			continue;
    152
    153		if (sscanf(line, "%"PRIx64, &addr) != 1)
    154			continue;
    155		if (addr < last_addr) {
    156			pr_debug("addr going backwards, read beyond section?\n");
    157			break;
    158		}
    159		last_addr = addr;
    160
    161		/* copy it from temporary buffer to 'buf' according
    162		 * to address on current objdump line */
    163		off = addr - start_addr;
    164		if (off >= *len)
    165			break;
    166		written_bytes = MIN(read_bytes, *len - off);
    167		memcpy(buf + off, tmp, written_bytes);
    168		off_last = off + written_bytes;
    169	}
    170
    171	/* len returns number of bytes that could not be read */
    172	*len -= off_last;
    173
    174	free(line);
    175
    176	return err;
    177}
    178
    179static int read_via_objdump(const char *filename, u64 addr, void *buf,
    180			    size_t len)
    181{
    182	char cmd[PATH_MAX * 2];
    183	const char *fmt;
    184	FILE *f;
    185	int ret;
    186
    187	fmt = "%s -z -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s";
    188	ret = snprintf(cmd, sizeof(cmd), fmt, "objdump", addr, addr + len,
    189		       filename);
    190	if (ret <= 0 || (size_t)ret >= sizeof(cmd))
    191		return -1;
    192
    193	pr_debug("Objdump command is: %s\n", cmd);
    194
    195	/* Ignore objdump errors */
    196	strcat(cmd, " 2>/dev/null");
    197
    198	f = popen(cmd, "r");
    199	if (!f) {
    200		pr_debug("popen failed\n");
    201		return -1;
    202	}
    203
    204	ret = read_objdump_output(f, buf, &len, addr);
    205	if (len) {
    206		pr_debug("objdump read too few bytes: %zd\n", len);
    207		if (!ret)
    208			ret = len;
    209	}
    210
    211	pclose(f);
    212
    213	return ret;
    214}
    215
    216static void dump_buf(unsigned char *buf, size_t len)
    217{
    218	size_t i;
    219
    220	for (i = 0; i < len; i++) {
    221		pr_debug("0x%02x ", buf[i]);
    222		if (i % 16 == 15)
    223			pr_debug("\n");
    224	}
    225	pr_debug("\n");
    226}
    227
    228static int read_object_code(u64 addr, size_t len, u8 cpumode,
    229			    struct thread *thread, struct state *state)
    230{
    231	struct addr_location al;
    232	unsigned char buf1[BUFSZ] = {0};
    233	unsigned char buf2[BUFSZ] = {0};
    234	size_t ret_len;
    235	u64 objdump_addr;
    236	const char *objdump_name;
    237	char decomp_name[KMOD_DECOMP_LEN];
    238	bool decomp = false;
    239	int ret;
    240
    241	pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr);
    242
    243	if (!thread__find_map(thread, cpumode, addr, &al) || !al.map->dso) {
    244		if (cpumode == PERF_RECORD_MISC_HYPERVISOR) {
    245			pr_debug("Hypervisor address can not be resolved - skipping\n");
    246			return 0;
    247		}
    248
    249		pr_debug("thread__find_map failed\n");
    250		return -1;
    251	}
    252
    253	pr_debug("File is: %s\n", al.map->dso->long_name);
    254
    255	if (al.map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
    256	    !dso__is_kcore(al.map->dso)) {
    257		pr_debug("Unexpected kernel address - skipping\n");
    258		return 0;
    259	}
    260
    261	pr_debug("On file address is: %#"PRIx64"\n", al.addr);
    262
    263	if (len > BUFSZ)
    264		len = BUFSZ;
    265
    266	/* Do not go off the map */
    267	if (addr + len > al.map->end)
    268		len = al.map->end - addr;
    269
    270	/* Read the object code using perf */
    271	ret_len = dso__data_read_offset(al.map->dso, thread->maps->machine,
    272					al.addr, buf1, len);
    273	if (ret_len != len) {
    274		pr_debug("dso__data_read_offset failed\n");
    275		return -1;
    276	}
    277
    278	/*
    279	 * Converting addresses for use by objdump requires more information.
    280	 * map__load() does that.  See map__rip_2objdump() for details.
    281	 */
    282	if (map__load(al.map))
    283		return -1;
    284
    285	/* objdump struggles with kcore - try each map only once */
    286	if (dso__is_kcore(al.map->dso)) {
    287		size_t d;
    288
    289		for (d = 0; d < state->done_cnt; d++) {
    290			if (state->done[d] == al.map->start) {
    291				pr_debug("kcore map tested already");
    292				pr_debug(" - skipping\n");
    293				return 0;
    294			}
    295		}
    296		if (state->done_cnt >= ARRAY_SIZE(state->done)) {
    297			pr_debug("Too many kcore maps - skipping\n");
    298			return 0;
    299		}
    300		state->done[state->done_cnt++] = al.map->start;
    301	}
    302
    303	objdump_name = al.map->dso->long_name;
    304	if (dso__needs_decompress(al.map->dso)) {
    305		if (dso__decompress_kmodule_path(al.map->dso, objdump_name,
    306						 decomp_name,
    307						 sizeof(decomp_name)) < 0) {
    308			pr_debug("decompression failed\n");
    309			return -1;
    310		}
    311
    312		decomp = true;
    313		objdump_name = decomp_name;
    314	}
    315
    316	/* Read the object code using objdump */
    317	objdump_addr = map__rip_2objdump(al.map, al.addr);
    318	ret = read_via_objdump(objdump_name, objdump_addr, buf2, len);
    319
    320	if (decomp)
    321		unlink(objdump_name);
    322
    323	if (ret > 0) {
    324		/*
    325		 * The kernel maps are inaccurate - assume objdump is right in
    326		 * that case.
    327		 */
    328		if (cpumode == PERF_RECORD_MISC_KERNEL ||
    329		    cpumode == PERF_RECORD_MISC_GUEST_KERNEL) {
    330			len -= ret;
    331			if (len) {
    332				pr_debug("Reducing len to %zu\n", len);
    333			} else if (dso__is_kcore(al.map->dso)) {
    334				/*
    335				 * objdump cannot handle very large segments
    336				 * that may be found in kcore.
    337				 */
    338				pr_debug("objdump failed for kcore");
    339				pr_debug(" - skipping\n");
    340				return 0;
    341			} else {
    342				return -1;
    343			}
    344		}
    345	}
    346	if (ret < 0) {
    347		pr_debug("read_via_objdump failed\n");
    348		return -1;
    349	}
    350
    351	/* The results should be identical */
    352	if (memcmp(buf1, buf2, len)) {
    353		pr_debug("Bytes read differ from those read by objdump\n");
    354		pr_debug("buf1 (dso):\n");
    355		dump_buf(buf1, len);
    356		pr_debug("buf2 (objdump):\n");
    357		dump_buf(buf2, len);
    358		return -1;
    359	}
    360	pr_debug("Bytes read match those read by objdump\n");
    361
    362	return 0;
    363}
    364
    365static int process_sample_event(struct machine *machine,
    366				struct evlist *evlist,
    367				union perf_event *event, struct state *state)
    368{
    369	struct perf_sample sample;
    370	struct thread *thread;
    371	int ret;
    372
    373	if (evlist__parse_sample(evlist, event, &sample)) {
    374		pr_debug("evlist__parse_sample failed\n");
    375		return -1;
    376	}
    377
    378	thread = machine__findnew_thread(machine, sample.pid, sample.tid);
    379	if (!thread) {
    380		pr_debug("machine__findnew_thread failed\n");
    381		return -1;
    382	}
    383
    384	ret = read_object_code(sample.ip, READLEN, sample.cpumode, thread, state);
    385	thread__put(thread);
    386	return ret;
    387}
    388
    389static int process_event(struct machine *machine, struct evlist *evlist,
    390			 union perf_event *event, struct state *state)
    391{
    392	if (event->header.type == PERF_RECORD_SAMPLE)
    393		return process_sample_event(machine, evlist, event, state);
    394
    395	if (event->header.type == PERF_RECORD_THROTTLE ||
    396	    event->header.type == PERF_RECORD_UNTHROTTLE)
    397		return 0;
    398
    399	if (event->header.type < PERF_RECORD_MAX) {
    400		int ret;
    401
    402		ret = machine__process_event(machine, event, NULL);
    403		if (ret < 0)
    404			pr_debug("machine__process_event failed, event type %u\n",
    405				 event->header.type);
    406		return ret;
    407	}
    408
    409	return 0;
    410}
    411
    412static int process_events(struct machine *machine, struct evlist *evlist,
    413			  struct state *state)
    414{
    415	union perf_event *event;
    416	struct mmap *md;
    417	int i, ret;
    418
    419	for (i = 0; i < evlist->core.nr_mmaps; i++) {
    420		md = &evlist->mmap[i];
    421		if (perf_mmap__read_init(&md->core) < 0)
    422			continue;
    423
    424		while ((event = perf_mmap__read_event(&md->core)) != NULL) {
    425			ret = process_event(machine, evlist, event, state);
    426			perf_mmap__consume(&md->core);
    427			if (ret < 0)
    428				return ret;
    429		}
    430		perf_mmap__read_done(&md->core);
    431	}
    432	return 0;
    433}
    434
    435static int comp(const void *a, const void *b)
    436{
    437	return *(int *)a - *(int *)b;
    438}
    439
    440static void do_sort_something(void)
    441{
    442	int buf[40960], i;
    443
    444	for (i = 0; i < (int)ARRAY_SIZE(buf); i++)
    445		buf[i] = ARRAY_SIZE(buf) - i - 1;
    446
    447	qsort(buf, ARRAY_SIZE(buf), sizeof(int), comp);
    448
    449	for (i = 0; i < (int)ARRAY_SIZE(buf); i++) {
    450		if (buf[i] != i) {
    451			pr_debug("qsort failed\n");
    452			break;
    453		}
    454	}
    455}
    456
    457static void sort_something(void)
    458{
    459	int i;
    460
    461	for (i = 0; i < 10; i++)
    462		do_sort_something();
    463}
    464
    465static void syscall_something(void)
    466{
    467	int pipefd[2];
    468	int i;
    469
    470	for (i = 0; i < 1000; i++) {
    471		if (pipe(pipefd) < 0) {
    472			pr_debug("pipe failed\n");
    473			break;
    474		}
    475		close(pipefd[1]);
    476		close(pipefd[0]);
    477	}
    478}
    479
    480static void fs_something(void)
    481{
    482	const char *test_file_name = "temp-perf-code-reading-test-file--";
    483	FILE *f;
    484	int i;
    485
    486	for (i = 0; i < 1000; i++) {
    487		f = fopen(test_file_name, "w+");
    488		if (f) {
    489			fclose(f);
    490			unlink(test_file_name);
    491		}
    492	}
    493}
    494
    495#ifdef __s390x__
    496#include "header.h" // for get_cpuid()
    497#endif
    498
    499static const char *do_determine_event(bool excl_kernel)
    500{
    501	const char *event = excl_kernel ? "cycles:u" : "cycles";
    502
    503#ifdef __s390x__
    504	char cpuid[128], model[16], model_c[16], cpum_cf_v[16];
    505	unsigned int family;
    506	int ret, cpum_cf_a;
    507
    508	if (get_cpuid(cpuid, sizeof(cpuid)))
    509		goto out_clocks;
    510	ret = sscanf(cpuid, "%*[^,],%u,%[^,],%[^,],%[^,],%x", &family, model_c,
    511		     model, cpum_cf_v, &cpum_cf_a);
    512	if (ret != 5)		 /* Not available */
    513		goto out_clocks;
    514	if (excl_kernel && (cpum_cf_a & 4))
    515		return event;
    516	if (!excl_kernel && (cpum_cf_a & 2))
    517		return event;
    518
    519	/* Fall through: missing authorization */
    520out_clocks:
    521	event = excl_kernel ? "cpu-clock:u" : "cpu-clock";
    522
    523#endif
    524	return event;
    525}
    526
    527static void do_something(void)
    528{
    529	fs_something();
    530
    531	sort_something();
    532
    533	syscall_something();
    534}
    535
    536enum {
    537	TEST_CODE_READING_OK,
    538	TEST_CODE_READING_NO_VMLINUX,
    539	TEST_CODE_READING_NO_KCORE,
    540	TEST_CODE_READING_NO_ACCESS,
    541	TEST_CODE_READING_NO_KERNEL_OBJ,
    542};
    543
    544static int do_test_code_reading(bool try_kcore)
    545{
    546	struct machine *machine;
    547	struct thread *thread;
    548	struct record_opts opts = {
    549		.mmap_pages	     = UINT_MAX,
    550		.user_freq	     = UINT_MAX,
    551		.user_interval	     = ULLONG_MAX,
    552		.freq		     = 500,
    553		.target		     = {
    554			.uses_mmap   = true,
    555		},
    556	};
    557	struct state state = {
    558		.done_cnt = 0,
    559	};
    560	struct perf_thread_map *threads = NULL;
    561	struct perf_cpu_map *cpus = NULL;
    562	struct evlist *evlist = NULL;
    563	struct evsel *evsel = NULL;
    564	int err = -1, ret;
    565	pid_t pid;
    566	struct map *map;
    567	bool have_vmlinux, have_kcore, excl_kernel = false;
    568
    569	pid = getpid();
    570
    571	machine = machine__new_host();
    572	machine->env = &perf_env;
    573
    574	ret = machine__create_kernel_maps(machine);
    575	if (ret < 0) {
    576		pr_debug("machine__create_kernel_maps failed\n");
    577		goto out_err;
    578	}
    579
    580	/* Force the use of kallsyms instead of vmlinux to try kcore */
    581	if (try_kcore)
    582		symbol_conf.kallsyms_name = "/proc/kallsyms";
    583
    584	/* Load kernel map */
    585	map = machine__kernel_map(machine);
    586	ret = map__load(map);
    587	if (ret < 0) {
    588		pr_debug("map__load failed\n");
    589		goto out_err;
    590	}
    591	have_vmlinux = dso__is_vmlinux(map->dso);
    592	have_kcore = dso__is_kcore(map->dso);
    593
    594	/* 2nd time through we just try kcore */
    595	if (try_kcore && !have_kcore)
    596		return TEST_CODE_READING_NO_KCORE;
    597
    598	/* No point getting kernel events if there is no kernel object */
    599	if (!have_vmlinux && !have_kcore)
    600		excl_kernel = true;
    601
    602	threads = thread_map__new_by_tid(pid);
    603	if (!threads) {
    604		pr_debug("thread_map__new_by_tid failed\n");
    605		goto out_err;
    606	}
    607
    608	ret = perf_event__synthesize_thread_map(NULL, threads,
    609						perf_event__process, machine,
    610						true, false);
    611	if (ret < 0) {
    612		pr_debug("perf_event__synthesize_thread_map failed\n");
    613		goto out_err;
    614	}
    615
    616	thread = machine__findnew_thread(machine, pid, pid);
    617	if (!thread) {
    618		pr_debug("machine__findnew_thread failed\n");
    619		goto out_put;
    620	}
    621
    622	cpus = perf_cpu_map__new(NULL);
    623	if (!cpus) {
    624		pr_debug("perf_cpu_map__new failed\n");
    625		goto out_put;
    626	}
    627
    628	while (1) {
    629		const char *str;
    630
    631		evlist = evlist__new();
    632		if (!evlist) {
    633			pr_debug("evlist__new failed\n");
    634			goto out_put;
    635		}
    636
    637		perf_evlist__set_maps(&evlist->core, cpus, threads);
    638
    639		str = do_determine_event(excl_kernel);
    640		pr_debug("Parsing event '%s'\n", str);
    641		ret = parse_events(evlist, str, NULL);
    642		if (ret < 0) {
    643			pr_debug("parse_events failed\n");
    644			goto out_put;
    645		}
    646
    647		evlist__config(evlist, &opts, NULL);
    648
    649		evsel = evlist__first(evlist);
    650
    651		evsel->core.attr.comm = 1;
    652		evsel->core.attr.disabled = 1;
    653		evsel->core.attr.enable_on_exec = 0;
    654
    655		ret = evlist__open(evlist);
    656		if (ret < 0) {
    657			if (!excl_kernel) {
    658				excl_kernel = true;
    659				/*
    660				 * Both cpus and threads are now owned by evlist
    661				 * and will be freed by following perf_evlist__set_maps
    662				 * call. Getting reference to keep them alive.
    663				 */
    664				perf_cpu_map__get(cpus);
    665				perf_thread_map__get(threads);
    666				perf_evlist__set_maps(&evlist->core, NULL, NULL);
    667				evlist__delete(evlist);
    668				evlist = NULL;
    669				continue;
    670			}
    671
    672			if (verbose > 0) {
    673				char errbuf[512];
    674				evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
    675				pr_debug("perf_evlist__open() failed!\n%s\n", errbuf);
    676			}
    677
    678			goto out_put;
    679		}
    680		break;
    681	}
    682
    683	ret = evlist__mmap(evlist, UINT_MAX);
    684	if (ret < 0) {
    685		pr_debug("evlist__mmap failed\n");
    686		goto out_put;
    687	}
    688
    689	evlist__enable(evlist);
    690
    691	do_something();
    692
    693	evlist__disable(evlist);
    694
    695	ret = process_events(machine, evlist, &state);
    696	if (ret < 0)
    697		goto out_put;
    698
    699	if (!have_vmlinux && !have_kcore && !try_kcore)
    700		err = TEST_CODE_READING_NO_KERNEL_OBJ;
    701	else if (!have_vmlinux && !try_kcore)
    702		err = TEST_CODE_READING_NO_VMLINUX;
    703	else if (excl_kernel)
    704		err = TEST_CODE_READING_NO_ACCESS;
    705	else
    706		err = TEST_CODE_READING_OK;
    707out_put:
    708	thread__put(thread);
    709out_err:
    710	evlist__delete(evlist);
    711	perf_cpu_map__put(cpus);
    712	perf_thread_map__put(threads);
    713	machine__delete_threads(machine);
    714	machine__delete(machine);
    715
    716	return err;
    717}
    718
    719static int test__code_reading(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
    720{
    721	int ret;
    722
    723	ret = do_test_code_reading(false);
    724	if (!ret)
    725		ret = do_test_code_reading(true);
    726
    727	switch (ret) {
    728	case TEST_CODE_READING_OK:
    729		return 0;
    730	case TEST_CODE_READING_NO_VMLINUX:
    731		pr_debug("no vmlinux\n");
    732		return 0;
    733	case TEST_CODE_READING_NO_KCORE:
    734		pr_debug("no kcore\n");
    735		return 0;
    736	case TEST_CODE_READING_NO_ACCESS:
    737		pr_debug("no access\n");
    738		return 0;
    739	case TEST_CODE_READING_NO_KERNEL_OBJ:
    740		pr_debug("no kernel obj\n");
    741		return 0;
    742	default:
    743		return -1;
    744	};
    745}
    746
    747DEFINE_SUITE("Object code reading", code_reading);