root/kernel/fail_function.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fei_post_handler
  2. adjust_error_retval
  3. fei_attr_new
  4. fei_attr_free
  5. fei_attr_lookup
  6. fei_attr_is_valid
  7. fei_retval_set
  8. fei_retval_get
  9. fei_debugfs_add_attr
  10. fei_debugfs_remove_attr
  11. fei_kprobe_handler
  12. NOKPROBE_SYMBOL
  13. fei_seq_stop
  14. fei_seq_next
  15. fei_seq_show
  16. fei_open
  17. fei_attr_remove
  18. fei_attr_remove_all
  19. fei_write
  20. fei_debugfs_init

   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 
  15 static int fei_kprobe_handler(struct kprobe *kp, struct pt_regs *regs);
  16 
  17 static 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 
  26 struct fei_attr {
  27         struct list_head list;
  28         struct kprobe kp;
  29         unsigned long retval;
  30 };
  31 static DEFINE_MUTEX(fei_lock);
  32 static LIST_HEAD(fei_attr_list);
  33 static DECLARE_FAULT_ATTR(fei_fault_attr);
  34 static struct dentry *fei_debugfs_dir;
  35 
  36 static 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                 if (retv != 0)
  41                         return 0;
  42                 break;
  43         case EI_ETYPE_ERRNO:
  44                 if (retv < (unsigned long)-MAX_ERRNO)
  45                         return (unsigned long)-EINVAL;
  46                 break;
  47         case EI_ETYPE_ERRNO_NULL:
  48                 if (retv != 0 && retv < (unsigned long)-MAX_ERRNO)
  49                         return (unsigned long)-EINVAL;
  50                 break;
  51         }
  52 
  53         return retv;
  54 }
  55 
  56 static 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 
  75 static void fei_attr_free(struct fei_attr *attr)
  76 {
  77         if (attr) {
  78                 kfree(attr->kp.symbol_name);
  79                 kfree(attr);
  80         }
  81 }
  82 
  83 static 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 
  95 static 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 
 107 static 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;
 131 out:
 132         mutex_unlock(&fei_lock);
 133 
 134         return err;
 135 }
 136 
 137 static 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 }
 152 DEFINE_DEBUGFS_ATTRIBUTE(fei_retval_ops, fei_retval_get, fei_retval_set,
 153                          "%llx\n");
 154 
 155 static 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 
 164 static 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 
 172 static 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 }
 184 NOKPROBE_SYMBOL(fei_kprobe_handler)
 185 
 186 static 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 
 192 static void fei_seq_stop(struct seq_file *m, void *v)
 193 {
 194         mutex_unlock(&fei_lock);
 195 }
 196 
 197 static 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 
 202 static 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 
 210 static 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 
 217 static int fei_open(struct inode *inode, struct file *file)
 218 {
 219         return seq_open(file, &fei_seq_ops);
 220 }
 221 
 222 static 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 
 230 static 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 
 239 static 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;
 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         }
 309 out:
 310         kfree(buf);
 311         mutex_unlock(&fei_lock);
 312         return ret;
 313 }
 314 
 315 static const struct file_operations fei_ops = {
 316         .open =         fei_open,
 317         .read =         seq_read,
 318         .write =        fei_write,
 319         .llseek =       seq_lseek,
 320         .release =      seq_release,
 321 };
 322 
 323 static int __init fei_debugfs_init(void)
 324 {
 325         struct dentry *dir;
 326 
 327         dir = fault_create_debugfs_attr("fail_function", NULL,
 328                                         &fei_fault_attr);
 329         if (IS_ERR(dir))
 330                 return PTR_ERR(dir);
 331 
 332         /* injectable attribute is just a symlink of error_inject/list */
 333         debugfs_create_symlink("injectable", dir, "../error_injection/list");
 334 
 335         debugfs_create_file("inject", 0600, dir, NULL, &fei_ops);
 336 
 337         fei_debugfs_dir = dir;
 338 
 339         return 0;
 340 }
 341 
 342 late_initcall(fei_debugfs_init);

/* [<][>][^][v][top][bottom][index][help] */