Source
x
// SPDX-License-Identifier: GPL-2.0
/*
* fail_function.c: Function-based error injection
*/
static int fei_kprobe_handler(struct kprobe *kp, struct pt_regs *regs);
static void fei_post_handler(struct kprobe *kp, struct pt_regs *regs,
unsigned long flags)
{
/*
* A dummy post handler is required to prohibit optimizing, because
* jump optimization does not support execution path overriding.
*/
}
struct fei_attr {
struct list_head list;
struct kprobe kp;
unsigned long retval;
};
static DEFINE_MUTEX(fei_lock);
static LIST_HEAD(fei_attr_list);
static DECLARE_FAULT_ATTR(fei_fault_attr);
static struct dentry *fei_debugfs_dir;
static unsigned long adjust_error_retval(unsigned long addr, unsigned long retv)
{
switch (get_injectable_error_type(addr)) {
case EI_ETYPE_NULL:
if (retv != 0)
return 0;
break;
case EI_ETYPE_ERRNO:
if (retv < (unsigned long)-MAX_ERRNO)
return (unsigned long)-EINVAL;
break;
case EI_ETYPE_ERRNO_NULL:
if (retv != 0 && retv < (unsigned long)-MAX_ERRNO)
return (unsigned long)-EINVAL;
break;
}
return retv;
}
static struct fei_attr *fei_attr_new(const char *sym, unsigned long addr)
{
struct fei_attr *attr;
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
if (attr) {
attr->kp.symbol_name = kstrdup(sym, GFP_KERNEL);
if (!attr->kp.symbol_name) {
kfree(attr);
return NULL;
}
attr->kp.pre_handler = fei_kprobe_handler;
attr->kp.post_handler = fei_post_handler;
attr->retval = adjust_error_retval(addr, 0);
INIT_LIST_HEAD(&attr->list);
}
return attr;
}
static void fei_attr_free(struct fei_attr *attr)
{
if (attr) {
kfree(attr->kp.symbol_name);
kfree(attr);
}
}
static struct fei_attr *fei_attr_lookup(const char *sym)
{
struct fei_attr *attr;
list_for_each_entry(attr, &fei_attr_list, list) {
if (!strcmp(attr->kp.symbol_name, sym))
return attr;
}
return NULL;