240 lines · c
1// SPDX-License-Identifier: GPL-2.02// error-inject.c: Function-level error injection table3#include <linux/error-injection.h>4#include <linux/debugfs.h>5#include <linux/kallsyms.h>6#include <linux/kprobes.h>7#include <linux/module.h>8#include <linux/mutex.h>9#include <linux/list.h>10#include <linux/slab.h>11#include <asm/sections.h>12 13/* Whitelist of symbols that can be overridden for error injection. */14static LIST_HEAD(error_injection_list);15static DEFINE_MUTEX(ei_mutex);16struct ei_entry {17 struct list_head list;18 unsigned long start_addr;19 unsigned long end_addr;20 int etype;21 void *priv;22};23 24bool within_error_injection_list(unsigned long addr)25{26 struct ei_entry *ent;27 bool ret = false;28 29 mutex_lock(&ei_mutex);30 list_for_each_entry(ent, &error_injection_list, list) {31 if (addr >= ent->start_addr && addr < ent->end_addr) {32 ret = true;33 break;34 }35 }36 mutex_unlock(&ei_mutex);37 return ret;38}39 40int get_injectable_error_type(unsigned long addr)41{42 struct ei_entry *ent;43 int ei_type = -EINVAL;44 45 mutex_lock(&ei_mutex);46 list_for_each_entry(ent, &error_injection_list, list) {47 if (addr >= ent->start_addr && addr < ent->end_addr) {48 ei_type = ent->etype;49 break;50 }51 }52 mutex_unlock(&ei_mutex);53 54 return ei_type;55}56 57/*58 * Lookup and populate the error_injection_list.59 *60 * For safety reasons we only allow certain functions to be overridden with61 * bpf_error_injection, so we need to populate the list of the symbols that have62 * been marked as safe for overriding.63 */64static void populate_error_injection_list(struct error_injection_entry *start,65 struct error_injection_entry *end,66 void *priv)67{68 struct error_injection_entry *iter;69 struct ei_entry *ent;70 unsigned long entry, offset = 0, size = 0;71 72 mutex_lock(&ei_mutex);73 for (iter = start; iter < end; iter++) {74 entry = (unsigned long)dereference_symbol_descriptor((void *)iter->addr);75 76 if (!kernel_text_address(entry) ||77 !kallsyms_lookup_size_offset(entry, &size, &offset)) {78 pr_err("Failed to find error inject entry at %p\n",79 (void *)entry);80 continue;81 }82 83 ent = kmalloc(sizeof(*ent), GFP_KERNEL);84 if (!ent)85 break;86 ent->start_addr = entry;87 ent->end_addr = entry + size;88 ent->etype = iter->etype;89 ent->priv = priv;90 INIT_LIST_HEAD(&ent->list);91 list_add_tail(&ent->list, &error_injection_list);92 }93 mutex_unlock(&ei_mutex);94}95 96/* Markers of the _error_inject_whitelist section */97extern struct error_injection_entry __start_error_injection_whitelist[];98extern struct error_injection_entry __stop_error_injection_whitelist[];99 100static void __init populate_kernel_ei_list(void)101{102 populate_error_injection_list(__start_error_injection_whitelist,103 __stop_error_injection_whitelist,104 NULL);105}106 107#ifdef CONFIG_MODULES108static void module_load_ei_list(struct module *mod)109{110 if (!mod->num_ei_funcs)111 return;112 113 populate_error_injection_list(mod->ei_funcs,114 mod->ei_funcs + mod->num_ei_funcs, mod);115}116 117static void module_unload_ei_list(struct module *mod)118{119 struct ei_entry *ent, *n;120 121 if (!mod->num_ei_funcs)122 return;123 124 mutex_lock(&ei_mutex);125 list_for_each_entry_safe(ent, n, &error_injection_list, list) {126 if (ent->priv == mod) {127 list_del_init(&ent->list);128 kfree(ent);129 }130 }131 mutex_unlock(&ei_mutex);132}133 134/* Module notifier call back, checking error injection table on the module */135static int ei_module_callback(struct notifier_block *nb,136 unsigned long val, void *data)137{138 struct module *mod = data;139 140 if (val == MODULE_STATE_COMING)141 module_load_ei_list(mod);142 else if (val == MODULE_STATE_GOING)143 module_unload_ei_list(mod);144 145 return NOTIFY_DONE;146}147 148static struct notifier_block ei_module_nb = {149 .notifier_call = ei_module_callback,150 .priority = 0151};152 153static __init int module_ei_init(void)154{155 return register_module_notifier(&ei_module_nb);156}157#else /* !CONFIG_MODULES */158#define module_ei_init() (0)159#endif160 161/*162 * error_injection/whitelist -- shows which functions can be overridden for163 * error injection.164 */165static void *ei_seq_start(struct seq_file *m, loff_t *pos)166{167 mutex_lock(&ei_mutex);168 return seq_list_start(&error_injection_list, *pos);169}170 171static void ei_seq_stop(struct seq_file *m, void *v)172{173 mutex_unlock(&ei_mutex);174}175 176static void *ei_seq_next(struct seq_file *m, void *v, loff_t *pos)177{178 return seq_list_next(v, &error_injection_list, pos);179}180 181static const char *error_type_string(int etype)182{183 switch (etype) {184 case EI_ETYPE_NULL:185 return "NULL";186 case EI_ETYPE_ERRNO:187 return "ERRNO";188 case EI_ETYPE_ERRNO_NULL:189 return "ERRNO_NULL";190 case EI_ETYPE_TRUE:191 return "TRUE";192 default:193 return "(unknown)";194 }195}196 197static int ei_seq_show(struct seq_file *m, void *v)198{199 struct ei_entry *ent = list_entry(v, struct ei_entry, list);200 201 seq_printf(m, "%ps\t%s\n", (void *)ent->start_addr,202 error_type_string(ent->etype));203 return 0;204}205 206static const struct seq_operations ei_sops = {207 .start = ei_seq_start,208 .next = ei_seq_next,209 .stop = ei_seq_stop,210 .show = ei_seq_show,211};212 213DEFINE_SEQ_ATTRIBUTE(ei);214 215static int __init ei_debugfs_init(void)216{217 struct dentry *dir, *file;218 219 dir = debugfs_create_dir("error_injection", NULL);220 221 file = debugfs_create_file("list", 0444, dir, NULL, &ei_fops);222 if (!file) {223 debugfs_remove(dir);224 return -ENOMEM;225 }226 227 return 0;228}229 230static int __init init_error_injection(void)231{232 populate_kernel_ei_list();233 234 if (!module_ei_init())235 ei_debugfs_init();236 237 return 0;238}239late_initcall(init_error_injection);240