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

fail_function.c (7124B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * fail_function.c: Function-based error injection
      4 */
      5#include <linux/error-injection.h>
      6#include <linux/debugfs.h>
      7#include <linux/fault-inject.h>
      8#include <linux/kallsyms.h>
      9#include <linux/kprobes.h>
     10#include <linux/module.h>
     11#include <linux/mutex.h>
     12#include <linux/slab.h>
     13#include <linux/uaccess.h>
     14
     15static int fei_kprobe_handler(struct kprobe *kp, struct pt_regs *regs);
     16
     17static void fei_post_handler(struct kprobe *kp, struct pt_regs *regs,
     18			     unsigned long flags)
     19{
     20	/*
     21	 * A dummy post handler is required to prohibit optimizing, because
     22	 * jump optimization does not support execution path overriding.
     23	 */
     24}
     25
     26struct fei_attr {
     27	struct list_head list;
     28	struct kprobe kp;
     29	unsigned long retval;
     30};
     31static DEFINE_MUTEX(fei_lock);
     32static LIST_HEAD(fei_attr_list);
     33static DECLARE_FAULT_ATTR(fei_fault_attr);
     34static struct dentry *fei_debugfs_dir;
     35
     36static unsigned long adjust_error_retval(unsigned long addr, unsigned long retv)
     37{
     38	switch (get_injectable_error_type(addr)) {
     39	case EI_ETYPE_NULL:
     40		return 0;
     41	case EI_ETYPE_ERRNO:
     42		if (retv < (unsigned long)-MAX_ERRNO)
     43			return (unsigned long)-EINVAL;
     44		break;
     45	case EI_ETYPE_ERRNO_NULL:
     46		if (retv != 0 && retv < (unsigned long)-MAX_ERRNO)
     47			return (unsigned long)-EINVAL;
     48		break;
     49	case EI_ETYPE_TRUE:
     50		return 1;
     51	}
     52
     53	return retv;
     54}
     55
     56static struct fei_attr *fei_attr_new(const char *sym, unsigned long addr)
     57{
     58	struct fei_attr *attr;
     59
     60	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
     61	if (attr) {
     62		attr->kp.symbol_name = kstrdup(sym, GFP_KERNEL);
     63		if (!attr->kp.symbol_name) {
     64			kfree(attr);
     65			return NULL;
     66		}
     67		attr->kp.pre_handler = fei_kprobe_handler;
     68		attr->kp.post_handler = fei_post_handler;
     69		attr->retval = adjust_error_retval(addr, 0);
     70		INIT_LIST_HEAD(&attr->list);
     71	}
     72	return attr;
     73}
     74
     75static void fei_attr_free(struct fei_attr *attr)
     76{
     77	if (attr) {
     78		kfree(attr->kp.symbol_name);
     79		kfree(attr);
     80	}
     81}
     82
     83static struct fei_attr *fei_attr_lookup(const char *sym)
     84{
     85	struct fei_attr *attr;
     86
     87	list_for_each_entry(attr, &fei_attr_list, list) {
     88		if (!strcmp(attr->kp.symbol_name, sym))
     89			return attr;
     90	}
     91
     92	return NULL;
     93}
     94
     95static bool fei_attr_is_valid(struct fei_attr *_attr)
     96{
     97	struct fei_attr *attr;
     98
     99	list_for_each_entry(attr, &fei_attr_list, list) {
    100		if (attr == _attr)
    101			return true;
    102	}
    103
    104	return false;
    105}
    106
    107static int fei_retval_set(void *data, u64 val)
    108{
    109	struct fei_attr *attr = data;
    110	unsigned long retv = (unsigned long)val;
    111	int err = 0;
    112
    113	mutex_lock(&fei_lock);
    114	/*
    115	 * Since this operation can be done after retval file is removed,
    116	 * It is safer to check the attr is still valid before accessing
    117	 * its member.
    118	 */
    119	if (!fei_attr_is_valid(attr)) {
    120		err = -ENOENT;
    121		goto out;
    122	}
    123
    124	if (attr->kp.addr) {
    125		if (adjust_error_retval((unsigned long)attr->kp.addr,
    126					val) != retv)
    127			err = -EINVAL;
    128	}
    129	if (!err)
    130		attr->retval = val;
    131out:
    132	mutex_unlock(&fei_lock);
    133
    134	return err;
    135}
    136
    137static int fei_retval_get(void *data, u64 *val)
    138{
    139	struct fei_attr *attr = data;
    140	int err = 0;
    141
    142	mutex_lock(&fei_lock);
    143	/* Here we also validate @attr to ensure it still exists. */
    144	if (!fei_attr_is_valid(attr))
    145		err = -ENOENT;
    146	else
    147		*val = attr->retval;
    148	mutex_unlock(&fei_lock);
    149
    150	return err;
    151}
    152DEFINE_DEBUGFS_ATTRIBUTE(fei_retval_ops, fei_retval_get, fei_retval_set,
    153			 "%llx\n");
    154
    155static void fei_debugfs_add_attr(struct fei_attr *attr)
    156{
    157	struct dentry *dir;
    158
    159	dir = debugfs_create_dir(attr->kp.symbol_name, fei_debugfs_dir);
    160
    161	debugfs_create_file("retval", 0600, dir, attr, &fei_retval_ops);
    162}
    163
    164static void fei_debugfs_remove_attr(struct fei_attr *attr)
    165{
    166	struct dentry *dir;
    167
    168	dir = debugfs_lookup(attr->kp.symbol_name, fei_debugfs_dir);
    169	debugfs_remove_recursive(dir);
    170}
    171
    172static int fei_kprobe_handler(struct kprobe *kp, struct pt_regs *regs)
    173{
    174	struct fei_attr *attr = container_of(kp, struct fei_attr, kp);
    175
    176	if (should_fail(&fei_fault_attr, 1)) {
    177		regs_set_return_value(regs, attr->retval);
    178		override_function_with_return(regs);
    179		return 1;
    180	}
    181
    182	return 0;
    183}
    184NOKPROBE_SYMBOL(fei_kprobe_handler)
    185
    186static void *fei_seq_start(struct seq_file *m, loff_t *pos)
    187{
    188	mutex_lock(&fei_lock);
    189	return seq_list_start(&fei_attr_list, *pos);
    190}
    191
    192static void fei_seq_stop(struct seq_file *m, void *v)
    193{
    194	mutex_unlock(&fei_lock);
    195}
    196
    197static void *fei_seq_next(struct seq_file *m, void *v, loff_t *pos)
    198{
    199	return seq_list_next(v, &fei_attr_list, pos);
    200}
    201
    202static int fei_seq_show(struct seq_file *m, void *v)
    203{
    204	struct fei_attr *attr = list_entry(v, struct fei_attr, list);
    205
    206	seq_printf(m, "%ps\n", attr->kp.addr);
    207	return 0;
    208}
    209
    210static const struct seq_operations fei_seq_ops = {
    211	.start	= fei_seq_start,
    212	.next	= fei_seq_next,
    213	.stop	= fei_seq_stop,
    214	.show	= fei_seq_show,
    215};
    216
    217static int fei_open(struct inode *inode, struct file *file)
    218{
    219	return seq_open(file, &fei_seq_ops);
    220}
    221
    222static void fei_attr_remove(struct fei_attr *attr)
    223{
    224	fei_debugfs_remove_attr(attr);
    225	unregister_kprobe(&attr->kp);
    226	list_del(&attr->list);
    227	fei_attr_free(attr);
    228}
    229
    230static void fei_attr_remove_all(void)
    231{
    232	struct fei_attr *attr, *n;
    233
    234	list_for_each_entry_safe(attr, n, &fei_attr_list, list) {
    235		fei_attr_remove(attr);
    236	}
    237}
    238
    239static ssize_t fei_write(struct file *file, const char __user *buffer,
    240			 size_t count, loff_t *ppos)
    241{
    242	struct fei_attr *attr;
    243	unsigned long addr;
    244	char *buf, *sym;
    245	int ret;
    246
    247	/* cut off if it is too long */
    248	if (count > KSYM_NAME_LEN)
    249		count = KSYM_NAME_LEN;
    250	buf = kmalloc(count + 1, GFP_KERNEL);
    251	if (!buf)
    252		return -ENOMEM;
    253
    254	if (copy_from_user(buf, buffer, count)) {
    255		ret = -EFAULT;
    256		goto out_free;
    257	}
    258	buf[count] = '\0';
    259	sym = strstrip(buf);
    260
    261	mutex_lock(&fei_lock);
    262
    263	/* Writing just spaces will remove all injection points */
    264	if (sym[0] == '\0') {
    265		fei_attr_remove_all();
    266		ret = count;
    267		goto out;
    268	}
    269	/* Writing !function will remove one injection point */
    270	if (sym[0] == '!') {
    271		attr = fei_attr_lookup(sym + 1);
    272		if (!attr) {
    273			ret = -ENOENT;
    274			goto out;
    275		}
    276		fei_attr_remove(attr);
    277		ret = count;
    278		goto out;
    279	}
    280
    281	addr = kallsyms_lookup_name(sym);
    282	if (!addr) {
    283		ret = -EINVAL;
    284		goto out;
    285	}
    286	if (!within_error_injection_list(addr)) {
    287		ret = -ERANGE;
    288		goto out;
    289	}
    290	if (fei_attr_lookup(sym)) {
    291		ret = -EBUSY;
    292		goto out;
    293	}
    294	attr = fei_attr_new(sym, addr);
    295	if (!attr) {
    296		ret = -ENOMEM;
    297		goto out;
    298	}
    299
    300	ret = register_kprobe(&attr->kp);
    301	if (!ret)
    302		fei_debugfs_add_attr(attr);
    303	if (ret < 0)
    304		fei_attr_remove(attr);
    305	else {
    306		list_add_tail(&attr->list, &fei_attr_list);
    307		ret = count;
    308	}
    309out:
    310	mutex_unlock(&fei_lock);
    311out_free:
    312	kfree(buf);
    313	return ret;
    314}
    315
    316static const struct file_operations fei_ops = {
    317	.open =		fei_open,
    318	.read =		seq_read,
    319	.write =	fei_write,
    320	.llseek =	seq_lseek,
    321	.release =	seq_release,
    322};
    323
    324static int __init fei_debugfs_init(void)
    325{
    326	struct dentry *dir;
    327
    328	dir = fault_create_debugfs_attr("fail_function", NULL,
    329					&fei_fault_attr);
    330	if (IS_ERR(dir))
    331		return PTR_ERR(dir);
    332
    333	/* injectable attribute is just a symlink of error_inject/list */
    334	debugfs_create_symlink("injectable", dir, "../error_injection/list");
    335
    336	debugfs_create_file("inject", 0600, dir, NULL, &fei_ops);
    337
    338	fei_debugfs_dir = dir;
    339
    340	return 0;
    341}
    342
    343late_initcall(fei_debugfs_init);