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_recursion_record.c (6163B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3#include <linux/seq_file.h>
      4#include <linux/kallsyms.h>
      5#include <linux/module.h>
      6#include <linux/ftrace.h>
      7#include <linux/fs.h>
      8
      9#include "trace_output.h"
     10
     11struct recursed_functions {
     12	unsigned long		ip;
     13	unsigned long		parent_ip;
     14};
     15
     16static struct recursed_functions recursed_functions[CONFIG_FTRACE_RECORD_RECURSION_SIZE];
     17static atomic_t nr_records;
     18
     19/*
     20 * Cache the last found function. Yes, updates to this is racey, but
     21 * so is memory cache ;-)
     22 */
     23static unsigned long cached_function;
     24
     25void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip)
     26{
     27	int index = 0;
     28	int i;
     29	unsigned long old;
     30
     31 again:
     32	/* First check the last one recorded */
     33	if (ip == cached_function)
     34		return;
     35
     36	i = atomic_read(&nr_records);
     37	/* nr_records is -1 when clearing records */
     38	smp_mb__after_atomic();
     39	if (i < 0)
     40		return;
     41
     42	/*
     43	 * If there's two writers and this writer comes in second,
     44	 * the cmpxchg() below to update the ip will fail. Then this
     45	 * writer will try again. It is possible that index will now
     46	 * be greater than nr_records. This is because the writer
     47	 * that succeeded has not updated the nr_records yet.
     48	 * This writer could keep trying again until the other writer
     49	 * updates nr_records. But if the other writer takes an
     50	 * interrupt, and that interrupt locks up that CPU, we do
     51	 * not want this CPU to lock up due to the recursion protection,
     52	 * and have a bug report showing this CPU as the cause of
     53	 * locking up the computer. To not lose this record, this
     54	 * writer will simply use the next position to update the
     55	 * recursed_functions, and it will update the nr_records
     56	 * accordingly.
     57	 */
     58	if (index < i)
     59		index = i;
     60	if (index >= CONFIG_FTRACE_RECORD_RECURSION_SIZE)
     61		return;
     62
     63	for (i = index - 1; i >= 0; i--) {
     64		if (recursed_functions[i].ip == ip) {
     65			cached_function = ip;
     66			return;
     67		}
     68	}
     69
     70	cached_function = ip;
     71
     72	/*
     73	 * We only want to add a function if it hasn't been added before.
     74	 * Add to the current location before incrementing the count.
     75	 * If it fails to add, then increment the index (save in i)
     76	 * and try again.
     77	 */
     78	old = cmpxchg(&recursed_functions[index].ip, 0, ip);
     79	if (old != 0) {
     80		/* Did something else already added this for us? */
     81		if (old == ip)
     82			return;
     83		/* Try the next location (use i for the next index) */
     84		index++;
     85		goto again;
     86	}
     87
     88	recursed_functions[index].parent_ip = parent_ip;
     89
     90	/*
     91	 * It's still possible that we could race with the clearing
     92	 *    CPU0                                    CPU1
     93	 *    ----                                    ----
     94	 *                                       ip = func
     95	 *  nr_records = -1;
     96	 *  recursed_functions[0] = 0;
     97	 *                                       i = -1
     98	 *                                       if (i < 0)
     99	 *  nr_records = 0;
    100	 *  (new recursion detected)
    101	 *      recursed_functions[0] = func
    102	 *                                            cmpxchg(recursed_functions[0],
    103	 *                                                    func, 0)
    104	 *
    105	 * But the worse that could happen is that we get a zero in
    106	 * the recursed_functions array, and it's likely that "func" will
    107	 * be recorded again.
    108	 */
    109	i = atomic_read(&nr_records);
    110	smp_mb__after_atomic();
    111	if (i < 0)
    112		cmpxchg(&recursed_functions[index].ip, ip, 0);
    113	else if (i <= index)
    114		atomic_cmpxchg(&nr_records, i, index + 1);
    115}
    116EXPORT_SYMBOL_GPL(ftrace_record_recursion);
    117
    118static DEFINE_MUTEX(recursed_function_lock);
    119static struct trace_seq *tseq;
    120
    121static void *recursed_function_seq_start(struct seq_file *m, loff_t *pos)
    122{
    123	void *ret = NULL;
    124	int index;
    125
    126	mutex_lock(&recursed_function_lock);
    127	index = atomic_read(&nr_records);
    128	if (*pos < index) {
    129		ret = &recursed_functions[*pos];
    130	}
    131
    132	tseq = kzalloc(sizeof(*tseq), GFP_KERNEL);
    133	if (!tseq)
    134		return ERR_PTR(-ENOMEM);
    135
    136	trace_seq_init(tseq);
    137
    138	return ret;
    139}
    140
    141static void *recursed_function_seq_next(struct seq_file *m, void *v, loff_t *pos)
    142{
    143	int index;
    144	int p;
    145
    146	index = atomic_read(&nr_records);
    147	p = ++(*pos);
    148
    149	return p < index ? &recursed_functions[p] : NULL;
    150}
    151
    152static void recursed_function_seq_stop(struct seq_file *m, void *v)
    153{
    154	kfree(tseq);
    155	mutex_unlock(&recursed_function_lock);
    156}
    157
    158static int recursed_function_seq_show(struct seq_file *m, void *v)
    159{
    160	struct recursed_functions *record = v;
    161	int ret = 0;
    162
    163	if (record) {
    164		trace_seq_print_sym(tseq, record->parent_ip, true);
    165		trace_seq_puts(tseq, ":\t");
    166		trace_seq_print_sym(tseq, record->ip, true);
    167		trace_seq_putc(tseq, '\n');
    168		ret = trace_print_seq(m, tseq);
    169	}
    170
    171	return ret;
    172}
    173
    174static const struct seq_operations recursed_function_seq_ops = {
    175	.start  = recursed_function_seq_start,
    176	.next   = recursed_function_seq_next,
    177	.stop   = recursed_function_seq_stop,
    178	.show   = recursed_function_seq_show
    179};
    180
    181static int recursed_function_open(struct inode *inode, struct file *file)
    182{
    183	int ret = 0;
    184
    185	mutex_lock(&recursed_function_lock);
    186	/* If this file was opened for write, then erase contents */
    187	if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
    188		/* disable updating records */
    189		atomic_set(&nr_records, -1);
    190		smp_mb__after_atomic();
    191		memset(recursed_functions, 0, sizeof(recursed_functions));
    192		smp_wmb();
    193		/* enable them again */
    194		atomic_set(&nr_records, 0);
    195	}
    196	if (file->f_mode & FMODE_READ)
    197		ret = seq_open(file, &recursed_function_seq_ops);
    198	mutex_unlock(&recursed_function_lock);
    199
    200	return ret;
    201}
    202
    203static ssize_t recursed_function_write(struct file *file,
    204				       const char __user *buffer,
    205				       size_t count, loff_t *ppos)
    206{
    207	return count;
    208}
    209
    210static int recursed_function_release(struct inode *inode, struct file *file)
    211{
    212	if (file->f_mode & FMODE_READ)
    213		seq_release(inode, file);
    214	return 0;
    215}
    216
    217static const struct file_operations recursed_functions_fops = {
    218	.open           = recursed_function_open,
    219	.write		= recursed_function_write,
    220	.read           = seq_read,
    221	.llseek         = seq_lseek,
    222	.release        = recursed_function_release,
    223};
    224
    225__init static int create_recursed_functions(void)
    226{
    227
    228	trace_create_file("recursed_functions", TRACE_MODE_WRITE,
    229			  NULL, NULL, &recursed_functions_fops);
    230	return 0;
    231}
    232
    233fs_initcall(create_recursed_functions);