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

trace_events_inject.c (6787B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * trace_events_inject - trace event injection
      4 *
      5 * Copyright (C) 2019 Cong Wang <cwang@twitter.com>
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/ctype.h>
     10#include <linux/mutex.h>
     11#include <linux/slab.h>
     12#include <linux/rculist.h>
     13
     14#include "trace.h"
     15
     16static int
     17trace_inject_entry(struct trace_event_file *file, void *rec, int len)
     18{
     19	struct trace_event_buffer fbuffer;
     20	int written = 0;
     21	void *entry;
     22
     23	rcu_read_lock_sched();
     24	entry = trace_event_buffer_reserve(&fbuffer, file, len);
     25	if (entry) {
     26		memcpy(entry, rec, len);
     27		written = len;
     28		trace_event_buffer_commit(&fbuffer);
     29	}
     30	rcu_read_unlock_sched();
     31
     32	return written;
     33}
     34
     35static int
     36parse_field(char *str, struct trace_event_call *call,
     37	    struct ftrace_event_field **pf, u64 *pv)
     38{
     39	struct ftrace_event_field *field;
     40	char *field_name;
     41	int s, i = 0;
     42	int len;
     43	u64 val;
     44
     45	if (!str[i])
     46		return 0;
     47	/* First find the field to associate to */
     48	while (isspace(str[i]))
     49		i++;
     50	s = i;
     51	while (isalnum(str[i]) || str[i] == '_')
     52		i++;
     53	len = i - s;
     54	if (!len)
     55		return -EINVAL;
     56
     57	field_name = kmemdup_nul(str + s, len, GFP_KERNEL);
     58	if (!field_name)
     59		return -ENOMEM;
     60	field = trace_find_event_field(call, field_name);
     61	kfree(field_name);
     62	if (!field)
     63		return -ENOENT;
     64
     65	*pf = field;
     66	while (isspace(str[i]))
     67		i++;
     68	if (str[i] != '=')
     69		return -EINVAL;
     70	i++;
     71	while (isspace(str[i]))
     72		i++;
     73	s = i;
     74	if (isdigit(str[i]) || str[i] == '-') {
     75		char *num, c;
     76		int ret;
     77
     78		/* Make sure the field is not a string */
     79		if (is_string_field(field))
     80			return -EINVAL;
     81
     82		if (str[i] == '-')
     83			i++;
     84
     85		/* We allow 0xDEADBEEF */
     86		while (isalnum(str[i]))
     87			i++;
     88		num = str + s;
     89		c = str[i];
     90		if (c != '\0' && !isspace(c))
     91			return -EINVAL;
     92		str[i] = '\0';
     93		/* Make sure it is a value */
     94		if (field->is_signed)
     95			ret = kstrtoll(num, 0, &val);
     96		else
     97			ret = kstrtoull(num, 0, &val);
     98		str[i] = c;
     99		if (ret)
    100			return ret;
    101
    102		*pv = val;
    103		return i;
    104	} else if (str[i] == '\'' || str[i] == '"') {
    105		char q = str[i];
    106
    107		/* Make sure the field is OK for strings */
    108		if (!is_string_field(field))
    109			return -EINVAL;
    110
    111		for (i++; str[i]; i++) {
    112			if (str[i] == '\\' && str[i + 1]) {
    113				i++;
    114				continue;
    115			}
    116			if (str[i] == q)
    117				break;
    118		}
    119		if (!str[i])
    120			return -EINVAL;
    121
    122		/* Skip quotes */
    123		s++;
    124		len = i - s;
    125		if (len >= MAX_FILTER_STR_VAL)
    126			return -EINVAL;
    127
    128		*pv = (unsigned long)(str + s);
    129		str[i] = 0;
    130		/* go past the last quote */
    131		i++;
    132		return i;
    133	}
    134
    135	return -EINVAL;
    136}
    137
    138static int trace_get_entry_size(struct trace_event_call *call)
    139{
    140	struct ftrace_event_field *field;
    141	struct list_head *head;
    142	int size = 0;
    143
    144	head = trace_get_fields(call);
    145	list_for_each_entry(field, head, link) {
    146		if (field->size + field->offset > size)
    147			size = field->size + field->offset;
    148	}
    149
    150	return size;
    151}
    152
    153static void *trace_alloc_entry(struct trace_event_call *call, int *size)
    154{
    155	int entry_size = trace_get_entry_size(call);
    156	struct ftrace_event_field *field;
    157	struct list_head *head;
    158	void *entry = NULL;
    159
    160	/* We need an extra '\0' at the end. */
    161	entry = kzalloc(entry_size + 1, GFP_KERNEL);
    162	if (!entry)
    163		return NULL;
    164
    165	head = trace_get_fields(call);
    166	list_for_each_entry(field, head, link) {
    167		if (!is_string_field(field))
    168			continue;
    169		if (field->filter_type == FILTER_STATIC_STRING)
    170			continue;
    171		if (field->filter_type == FILTER_DYN_STRING ||
    172		    field->filter_type == FILTER_RDYN_STRING) {
    173			u32 *str_item;
    174			int str_loc = entry_size & 0xffff;
    175
    176			if (field->filter_type == FILTER_RDYN_STRING)
    177				str_loc -= field->offset + field->size;
    178
    179			str_item = (u32 *)(entry + field->offset);
    180			*str_item = str_loc; /* string length is 0. */
    181		} else {
    182			char **paddr;
    183
    184			paddr = (char **)(entry + field->offset);
    185			*paddr = "";
    186		}
    187	}
    188
    189	*size = entry_size + 1;
    190	return entry;
    191}
    192
    193#define INJECT_STRING "STATIC STRING CAN NOT BE INJECTED"
    194
    195/* Caller is responsible to free the *pentry. */
    196static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
    197{
    198	struct ftrace_event_field *field;
    199	void *entry = NULL;
    200	int entry_size;
    201	u64 val = 0;
    202	int len;
    203
    204	entry = trace_alloc_entry(call, &entry_size);
    205	*pentry = entry;
    206	if (!entry)
    207		return -ENOMEM;
    208
    209	tracing_generic_entry_update(entry, call->event.type,
    210				     tracing_gen_ctx());
    211
    212	while ((len = parse_field(str, call, &field, &val)) > 0) {
    213		if (is_function_field(field))
    214			return -EINVAL;
    215
    216		if (is_string_field(field)) {
    217			char *addr = (char *)(unsigned long) val;
    218
    219			if (field->filter_type == FILTER_STATIC_STRING) {
    220				strlcpy(entry + field->offset, addr, field->size);
    221			} else if (field->filter_type == FILTER_DYN_STRING ||
    222				   field->filter_type == FILTER_RDYN_STRING) {
    223				int str_len = strlen(addr) + 1;
    224				int str_loc = entry_size & 0xffff;
    225				u32 *str_item;
    226
    227				entry_size += str_len;
    228				*pentry = krealloc(entry, entry_size, GFP_KERNEL);
    229				if (!*pentry) {
    230					kfree(entry);
    231					return -ENOMEM;
    232				}
    233				entry = *pentry;
    234
    235				strlcpy(entry + (entry_size - str_len), addr, str_len);
    236				str_item = (u32 *)(entry + field->offset);
    237				if (field->filter_type == FILTER_RDYN_STRING)
    238					str_loc -= field->offset + field->size;
    239				*str_item = (str_len << 16) | str_loc;
    240			} else {
    241				char **paddr;
    242
    243				paddr = (char **)(entry + field->offset);
    244				*paddr = INJECT_STRING;
    245			}
    246		} else {
    247			switch (field->size) {
    248			case 1: {
    249				u8 tmp = (u8) val;
    250
    251				memcpy(entry + field->offset, &tmp, 1);
    252				break;
    253			}
    254			case 2: {
    255				u16 tmp = (u16) val;
    256
    257				memcpy(entry + field->offset, &tmp, 2);
    258				break;
    259			}
    260			case 4: {
    261				u32 tmp = (u32) val;
    262
    263				memcpy(entry + field->offset, &tmp, 4);
    264				break;
    265			}
    266			case 8:
    267				memcpy(entry + field->offset, &val, 8);
    268				break;
    269			default:
    270				return -EINVAL;
    271			}
    272		}
    273
    274		str += len;
    275	}
    276
    277	if (len < 0)
    278		return len;
    279
    280	return entry_size;
    281}
    282
    283static ssize_t
    284event_inject_write(struct file *filp, const char __user *ubuf, size_t cnt,
    285		   loff_t *ppos)
    286{
    287	struct trace_event_call *call;
    288	struct trace_event_file *file;
    289	int err = -ENODEV, size;
    290	void *entry = NULL;
    291	char *buf;
    292
    293	if (cnt >= PAGE_SIZE)
    294		return -EINVAL;
    295
    296	buf = memdup_user_nul(ubuf, cnt);
    297	if (IS_ERR(buf))
    298		return PTR_ERR(buf);
    299	strim(buf);
    300
    301	mutex_lock(&event_mutex);
    302	file = event_file_data(filp);
    303	if (file) {
    304		call = file->event_call;
    305		size = parse_entry(buf, call, &entry);
    306		if (size < 0)
    307			err = size;
    308		else
    309			err = trace_inject_entry(file, entry, size);
    310	}
    311	mutex_unlock(&event_mutex);
    312
    313	kfree(entry);
    314	kfree(buf);
    315
    316	if (err < 0)
    317		return err;
    318
    319	*ppos += err;
    320	return cnt;
    321}
    322
    323static ssize_t
    324event_inject_read(struct file *file, char __user *buf, size_t size,
    325		  loff_t *ppos)
    326{
    327	return -EPERM;
    328}
    329
    330const struct file_operations event_inject_fops = {
    331	.open = tracing_open_generic,
    332	.read = event_inject_read,
    333	.write = event_inject_write,
    334};