brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · 887acd9 Raw
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