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

fault-inject.c (6030B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2#include <linux/kernel.h>
      3#include <linux/init.h>
      4#include <linux/random.h>
      5#include <linux/sched.h>
      6#include <linux/stat.h>
      7#include <linux/types.h>
      8#include <linux/fs.h>
      9#include <linux/export.h>
     10#include <linux/interrupt.h>
     11#include <linux/stacktrace.h>
     12#include <linux/fault-inject.h>
     13
     14/*
     15 * setup_fault_attr() is a helper function for various __setup handlers, so it
     16 * returns 0 on error, because that is what __setup handlers do.
     17 */
     18int setup_fault_attr(struct fault_attr *attr, char *str)
     19{
     20	unsigned long probability;
     21	unsigned long interval;
     22	int times;
     23	int space;
     24
     25	/* "<interval>,<probability>,<space>,<times>" */
     26	if (sscanf(str, "%lu,%lu,%d,%d",
     27			&interval, &probability, &space, &times) < 4) {
     28		printk(KERN_WARNING
     29			"FAULT_INJECTION: failed to parse arguments\n");
     30		return 0;
     31	}
     32
     33	attr->probability = probability;
     34	attr->interval = interval;
     35	atomic_set(&attr->times, times);
     36	atomic_set(&attr->space, space);
     37
     38	return 1;
     39}
     40EXPORT_SYMBOL_GPL(setup_fault_attr);
     41
     42static void fail_dump(struct fault_attr *attr)
     43{
     44	if (attr->no_warn)
     45		return;
     46
     47	if (attr->verbose > 0 && __ratelimit(&attr->ratelimit_state)) {
     48		printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure.\n"
     49		       "name %pd, interval %lu, probability %lu, "
     50		       "space %d, times %d\n", attr->dname,
     51		       attr->interval, attr->probability,
     52		       atomic_read(&attr->space),
     53		       atomic_read(&attr->times));
     54		if (attr->verbose > 1)
     55			dump_stack();
     56	}
     57}
     58
     59#define atomic_dec_not_zero(v)		atomic_add_unless((v), -1, 0)
     60
     61static bool fail_task(struct fault_attr *attr, struct task_struct *task)
     62{
     63	return in_task() && task->make_it_fail;
     64}
     65
     66#define MAX_STACK_TRACE_DEPTH 32
     67
     68#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
     69
     70static bool fail_stacktrace(struct fault_attr *attr)
     71{
     72	int depth = attr->stacktrace_depth;
     73	unsigned long entries[MAX_STACK_TRACE_DEPTH];
     74	int n, nr_entries;
     75	bool found = (attr->require_start == 0 && attr->require_end == ULONG_MAX);
     76
     77	if (depth == 0)
     78		return found;
     79
     80	nr_entries = stack_trace_save(entries, depth, 1);
     81	for (n = 0; n < nr_entries; n++) {
     82		if (attr->reject_start <= entries[n] &&
     83			       entries[n] < attr->reject_end)
     84			return false;
     85		if (attr->require_start <= entries[n] &&
     86			       entries[n] < attr->require_end)
     87			found = true;
     88	}
     89	return found;
     90}
     91
     92#else
     93
     94static inline bool fail_stacktrace(struct fault_attr *attr)
     95{
     96	return true;
     97}
     98
     99#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
    100
    101/*
    102 * This code is stolen from failmalloc-1.0
    103 * http://www.nongnu.org/failmalloc/
    104 */
    105
    106bool should_fail(struct fault_attr *attr, ssize_t size)
    107{
    108	if (in_task()) {
    109		unsigned int fail_nth = READ_ONCE(current->fail_nth);
    110
    111		if (fail_nth) {
    112			fail_nth--;
    113			WRITE_ONCE(current->fail_nth, fail_nth);
    114			if (!fail_nth)
    115				goto fail;
    116
    117			return false;
    118		}
    119	}
    120
    121	/* No need to check any other properties if the probability is 0 */
    122	if (attr->probability == 0)
    123		return false;
    124
    125	if (attr->task_filter && !fail_task(attr, current))
    126		return false;
    127
    128	if (atomic_read(&attr->times) == 0)
    129		return false;
    130
    131	if (atomic_read(&attr->space) > size) {
    132		atomic_sub(size, &attr->space);
    133		return false;
    134	}
    135
    136	if (attr->interval > 1) {
    137		attr->count++;
    138		if (attr->count % attr->interval)
    139			return false;
    140	}
    141
    142	if (attr->probability <= prandom_u32() % 100)
    143		return false;
    144
    145	if (!fail_stacktrace(attr))
    146		return false;
    147
    148fail:
    149	fail_dump(attr);
    150
    151	if (atomic_read(&attr->times) != -1)
    152		atomic_dec_not_zero(&attr->times);
    153
    154	return true;
    155}
    156EXPORT_SYMBOL_GPL(should_fail);
    157
    158#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
    159
    160static int debugfs_ul_set(void *data, u64 val)
    161{
    162	*(unsigned long *)data = val;
    163	return 0;
    164}
    165
    166static int debugfs_ul_get(void *data, u64 *val)
    167{
    168	*val = *(unsigned long *)data;
    169	return 0;
    170}
    171
    172DEFINE_SIMPLE_ATTRIBUTE(fops_ul, debugfs_ul_get, debugfs_ul_set, "%llu\n");
    173
    174static void debugfs_create_ul(const char *name, umode_t mode,
    175			      struct dentry *parent, unsigned long *value)
    176{
    177	debugfs_create_file(name, mode, parent, value, &fops_ul);
    178}
    179
    180#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
    181
    182static int debugfs_stacktrace_depth_set(void *data, u64 val)
    183{
    184	*(unsigned long *)data =
    185		min_t(unsigned long, val, MAX_STACK_TRACE_DEPTH);
    186
    187	return 0;
    188}
    189
    190DEFINE_SIMPLE_ATTRIBUTE(fops_stacktrace_depth, debugfs_ul_get,
    191			debugfs_stacktrace_depth_set, "%llu\n");
    192
    193static void debugfs_create_stacktrace_depth(const char *name, umode_t mode,
    194					    struct dentry *parent,
    195					    unsigned long *value)
    196{
    197	debugfs_create_file(name, mode, parent, value, &fops_stacktrace_depth);
    198}
    199
    200#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
    201
    202struct dentry *fault_create_debugfs_attr(const char *name,
    203			struct dentry *parent, struct fault_attr *attr)
    204{
    205	umode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
    206	struct dentry *dir;
    207
    208	dir = debugfs_create_dir(name, parent);
    209	if (IS_ERR(dir))
    210		return dir;
    211
    212	debugfs_create_ul("probability", mode, dir, &attr->probability);
    213	debugfs_create_ul("interval", mode, dir, &attr->interval);
    214	debugfs_create_atomic_t("times", mode, dir, &attr->times);
    215	debugfs_create_atomic_t("space", mode, dir, &attr->space);
    216	debugfs_create_ul("verbose", mode, dir, &attr->verbose);
    217	debugfs_create_u32("verbose_ratelimit_interval_ms", mode, dir,
    218			   &attr->ratelimit_state.interval);
    219	debugfs_create_u32("verbose_ratelimit_burst", mode, dir,
    220			   &attr->ratelimit_state.burst);
    221	debugfs_create_bool("task-filter", mode, dir, &attr->task_filter);
    222
    223#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
    224	debugfs_create_stacktrace_depth("stacktrace-depth", mode, dir,
    225					&attr->stacktrace_depth);
    226	debugfs_create_ul("require-start", mode, dir, &attr->require_start);
    227	debugfs_create_ul("require-end", mode, dir, &attr->require_end);
    228	debugfs_create_ul("reject-start", mode, dir, &attr->reject_start);
    229	debugfs_create_ul("reject-end", mode, dir, &attr->reject_end);
    230#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
    231
    232	attr->dname = dget(dir);
    233	return dir;
    234}
    235EXPORT_SYMBOL_GPL(fault_create_debugfs_attr);
    236
    237#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */