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

vdso.c (7364B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <errno.h>
      3#include <unistd.h>
      4#include <stdio.h>
      5#include <string.h>
      6#include <sys/types.h>
      7#include <sys/stat.h>
      8#include <fcntl.h>
      9#include <stdlib.h>
     10#include <linux/kernel.h>
     11
     12#include "vdso.h"
     13#include "dso.h"
     14#include <internal/lib.h>
     15#include "map.h"
     16#include "symbol.h"
     17#include "machine.h"
     18#include "thread.h"
     19#include "linux/string.h"
     20#include <linux/zalloc.h>
     21#include "debug.h"
     22
     23/*
     24 * Include definition of find_map() also used in perf-read-vdso.c for
     25 * building perf-read-vdso32 and perf-read-vdsox32.
     26 */
     27#include "find-map.c"
     28
     29#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"
     30
     31struct vdso_file {
     32	bool found;
     33	bool error;
     34	char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
     35	const char *dso_name;
     36	const char *read_prog;
     37};
     38
     39struct vdso_info {
     40	struct vdso_file vdso;
     41#if BITS_PER_LONG == 64
     42	struct vdso_file vdso32;
     43	struct vdso_file vdsox32;
     44#endif
     45};
     46
     47static struct vdso_info *vdso_info__new(void)
     48{
     49	static const struct vdso_info vdso_info_init = {
     50		.vdso    = {
     51			.temp_file_name = VDSO__TEMP_FILE_NAME,
     52			.dso_name = DSO__NAME_VDSO,
     53		},
     54#if BITS_PER_LONG == 64
     55		.vdso32  = {
     56			.temp_file_name = VDSO__TEMP_FILE_NAME,
     57			.dso_name = DSO__NAME_VDSO32,
     58			.read_prog = "perf-read-vdso32",
     59		},
     60		.vdsox32  = {
     61			.temp_file_name = VDSO__TEMP_FILE_NAME,
     62			.dso_name = DSO__NAME_VDSOX32,
     63			.read_prog = "perf-read-vdsox32",
     64		},
     65#endif
     66	};
     67
     68	return memdup(&vdso_info_init, sizeof(vdso_info_init));
     69}
     70
     71static char *get_file(struct vdso_file *vdso_file)
     72{
     73	char *vdso = NULL;
     74	char *buf = NULL;
     75	void *start, *end;
     76	size_t size;
     77	int fd;
     78
     79	if (vdso_file->found)
     80		return vdso_file->temp_file_name;
     81
     82	if (vdso_file->error || find_map(&start, &end, VDSO__MAP_NAME))
     83		return NULL;
     84
     85	size = end - start;
     86
     87	buf = memdup(start, size);
     88	if (!buf)
     89		return NULL;
     90
     91	fd = mkstemp(vdso_file->temp_file_name);
     92	if (fd < 0)
     93		goto out;
     94
     95	if (size == (size_t) write(fd, buf, size))
     96		vdso = vdso_file->temp_file_name;
     97
     98	close(fd);
     99
    100 out:
    101	free(buf);
    102
    103	vdso_file->found = (vdso != NULL);
    104	vdso_file->error = !vdso_file->found;
    105	return vdso;
    106}
    107
    108void machine__exit_vdso(struct machine *machine)
    109{
    110	struct vdso_info *vdso_info = machine->vdso_info;
    111
    112	if (!vdso_info)
    113		return;
    114
    115	if (vdso_info->vdso.found)
    116		unlink(vdso_info->vdso.temp_file_name);
    117#if BITS_PER_LONG == 64
    118	if (vdso_info->vdso32.found)
    119		unlink(vdso_info->vdso32.temp_file_name);
    120	if (vdso_info->vdsox32.found)
    121		unlink(vdso_info->vdsox32.temp_file_name);
    122#endif
    123
    124	zfree(&machine->vdso_info);
    125}
    126
    127static struct dso *__machine__addnew_vdso(struct machine *machine, const char *short_name,
    128					  const char *long_name)
    129{
    130	struct dso *dso;
    131
    132	dso = dso__new(short_name);
    133	if (dso != NULL) {
    134		__dsos__add(&machine->dsos, dso);
    135		dso__set_long_name(dso, long_name, false);
    136		/* Put dso here because __dsos_add already got it */
    137		dso__put(dso);
    138	}
    139
    140	return dso;
    141}
    142
    143static enum dso_type machine__thread_dso_type(struct machine *machine,
    144					      struct thread *thread)
    145{
    146	enum dso_type dso_type = DSO__TYPE_UNKNOWN;
    147	struct map *map;
    148
    149	maps__for_each_entry(thread->maps, map) {
    150		struct dso *dso = map->dso;
    151		if (!dso || dso->long_name[0] != '/')
    152			continue;
    153		dso_type = dso__type(dso, machine);
    154		if (dso_type != DSO__TYPE_UNKNOWN)
    155			break;
    156	}
    157
    158	return dso_type;
    159}
    160
    161#if BITS_PER_LONG == 64
    162
    163static int vdso__do_copy_compat(FILE *f, int fd)
    164{
    165	char buf[4096];
    166	size_t count;
    167
    168	while (1) {
    169		count = fread(buf, 1, sizeof(buf), f);
    170		if (ferror(f))
    171			return -errno;
    172		if (feof(f))
    173			break;
    174		if (count && writen(fd, buf, count) != (ssize_t)count)
    175			return -errno;
    176	}
    177
    178	return 0;
    179}
    180
    181static int vdso__copy_compat(const char *prog, int fd)
    182{
    183	FILE *f;
    184	int err;
    185
    186	f = popen(prog, "r");
    187	if (!f)
    188		return -errno;
    189
    190	err = vdso__do_copy_compat(f, fd);
    191
    192	if (pclose(f) == -1)
    193		return -errno;
    194
    195	return err;
    196}
    197
    198static int vdso__create_compat_file(const char *prog, char *temp_name)
    199{
    200	int fd, err;
    201
    202	fd = mkstemp(temp_name);
    203	if (fd < 0)
    204		return -errno;
    205
    206	err = vdso__copy_compat(prog, fd);
    207
    208	if (close(fd) == -1)
    209		return -errno;
    210
    211	return err;
    212}
    213
    214static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
    215{
    216	int err;
    217
    218	if (vdso_file->found)
    219		return vdso_file->temp_file_name;
    220
    221	if (vdso_file->error)
    222		return NULL;
    223
    224	err = vdso__create_compat_file(vdso_file->read_prog,
    225				       vdso_file->temp_file_name);
    226	if (err) {
    227		pr_err("%s failed, error %d\n", vdso_file->read_prog, err);
    228		vdso_file->error = true;
    229		return NULL;
    230	}
    231
    232	vdso_file->found = true;
    233
    234	return vdso_file->temp_file_name;
    235}
    236
    237static struct dso *__machine__findnew_compat(struct machine *machine,
    238					     struct vdso_file *vdso_file)
    239{
    240	const char *file_name;
    241	struct dso *dso;
    242
    243	dso = __dsos__find(&machine->dsos, vdso_file->dso_name, true);
    244	if (dso)
    245		goto out;
    246
    247	file_name = vdso__get_compat_file(vdso_file);
    248	if (!file_name)
    249		goto out;
    250
    251	dso = __machine__addnew_vdso(machine, vdso_file->dso_name, file_name);
    252out:
    253	return dso;
    254}
    255
    256static int __machine__findnew_vdso_compat(struct machine *machine,
    257					  struct thread *thread,
    258					  struct vdso_info *vdso_info,
    259					  struct dso **dso)
    260{
    261	enum dso_type dso_type;
    262
    263	dso_type = machine__thread_dso_type(machine, thread);
    264
    265#ifndef HAVE_PERF_READ_VDSO32
    266	if (dso_type == DSO__TYPE_32BIT)
    267		return 0;
    268#endif
    269#ifndef HAVE_PERF_READ_VDSOX32
    270	if (dso_type == DSO__TYPE_X32BIT)
    271		return 0;
    272#endif
    273
    274	switch (dso_type) {
    275	case DSO__TYPE_32BIT:
    276		*dso = __machine__findnew_compat(machine, &vdso_info->vdso32);
    277		return 1;
    278	case DSO__TYPE_X32BIT:
    279		*dso = __machine__findnew_compat(machine, &vdso_info->vdsox32);
    280		return 1;
    281	case DSO__TYPE_UNKNOWN:
    282	case DSO__TYPE_64BIT:
    283	default:
    284		return 0;
    285	}
    286}
    287
    288#endif
    289
    290static struct dso *machine__find_vdso(struct machine *machine,
    291				      struct thread *thread)
    292{
    293	struct dso *dso = NULL;
    294	enum dso_type dso_type;
    295
    296	dso_type = machine__thread_dso_type(machine, thread);
    297	switch (dso_type) {
    298	case DSO__TYPE_32BIT:
    299		dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO32, true);
    300		if (!dso) {
    301			dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO,
    302					   true);
    303			if (dso && dso_type != dso__type(dso, machine))
    304				dso = NULL;
    305		}
    306		break;
    307	case DSO__TYPE_X32BIT:
    308		dso = __dsos__find(&machine->dsos, DSO__NAME_VDSOX32, true);
    309		break;
    310	case DSO__TYPE_64BIT:
    311	case DSO__TYPE_UNKNOWN:
    312	default:
    313		dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
    314		break;
    315	}
    316
    317	return dso;
    318}
    319
    320struct dso *machine__findnew_vdso(struct machine *machine,
    321				  struct thread *thread)
    322{
    323	struct vdso_info *vdso_info;
    324	struct dso *dso = NULL;
    325
    326	down_write(&machine->dsos.lock);
    327	if (!machine->vdso_info)
    328		machine->vdso_info = vdso_info__new();
    329
    330	vdso_info = machine->vdso_info;
    331	if (!vdso_info)
    332		goto out_unlock;
    333
    334	dso = machine__find_vdso(machine, thread);
    335	if (dso)
    336		goto out_unlock;
    337
    338#if BITS_PER_LONG == 64
    339	if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso))
    340		goto out_unlock;
    341#endif
    342
    343	dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
    344	if (!dso) {
    345		char *file;
    346
    347		file = get_file(&vdso_info->vdso);
    348		if (file)
    349			dso = __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
    350	}
    351
    352out_unlock:
    353	dso__get(dso);
    354	up_write(&machine->dsos.lock);
    355	return dso;
    356}
    357
    358bool dso__is_vdso(struct dso *dso)
    359{
    360	return !strcmp(dso->short_name, DSO__NAME_VDSO) ||
    361	       !strcmp(dso->short_name, DSO__NAME_VDSO32) ||
    362	       !strcmp(dso->short_name, DSO__NAME_VDSOX32);
    363}