18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// error-inject.c: Function-level error injection table 38c2ecf20Sopenharmony_ci#include <linux/error-injection.h> 48c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 58c2ecf20Sopenharmony_ci#include <linux/kallsyms.h> 68c2ecf20Sopenharmony_ci#include <linux/kprobes.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/mutex.h> 98c2ecf20Sopenharmony_ci#include <linux/list.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* Whitelist of symbols that can be overridden for error injection. */ 138c2ecf20Sopenharmony_cistatic LIST_HEAD(error_injection_list); 148c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ei_mutex); 158c2ecf20Sopenharmony_cistruct ei_entry { 168c2ecf20Sopenharmony_ci struct list_head list; 178c2ecf20Sopenharmony_ci unsigned long start_addr; 188c2ecf20Sopenharmony_ci unsigned long end_addr; 198c2ecf20Sopenharmony_ci int etype; 208c2ecf20Sopenharmony_ci void *priv; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cibool within_error_injection_list(unsigned long addr) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct ei_entry *ent; 268c2ecf20Sopenharmony_ci bool ret = false; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci mutex_lock(&ei_mutex); 298c2ecf20Sopenharmony_ci list_for_each_entry(ent, &error_injection_list, list) { 308c2ecf20Sopenharmony_ci if (addr >= ent->start_addr && addr < ent->end_addr) { 318c2ecf20Sopenharmony_ci ret = true; 328c2ecf20Sopenharmony_ci break; 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci mutex_unlock(&ei_mutex); 368c2ecf20Sopenharmony_ci return ret; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciint get_injectable_error_type(unsigned long addr) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct ei_entry *ent; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci list_for_each_entry(ent, &error_injection_list, list) { 448c2ecf20Sopenharmony_ci if (addr >= ent->start_addr && addr < ent->end_addr) 458c2ecf20Sopenharmony_ci return ent->etype; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci return EI_ETYPE_NONE; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * Lookup and populate the error_injection_list. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * For safety reasons we only allow certain functions to be overridden with 548c2ecf20Sopenharmony_ci * bpf_error_injection, so we need to populate the list of the symbols that have 558c2ecf20Sopenharmony_ci * been marked as safe for overriding. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistatic void populate_error_injection_list(struct error_injection_entry *start, 588c2ecf20Sopenharmony_ci struct error_injection_entry *end, 598c2ecf20Sopenharmony_ci void *priv) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct error_injection_entry *iter; 628c2ecf20Sopenharmony_ci struct ei_entry *ent; 638c2ecf20Sopenharmony_ci unsigned long entry, offset = 0, size = 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci mutex_lock(&ei_mutex); 668c2ecf20Sopenharmony_ci for (iter = start; iter < end; iter++) { 678c2ecf20Sopenharmony_ci entry = arch_deref_entry_point((void *)iter->addr); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!kernel_text_address(entry) || 708c2ecf20Sopenharmony_ci !kallsyms_lookup_size_offset(entry, &size, &offset)) { 718c2ecf20Sopenharmony_ci pr_err("Failed to find error inject entry at %p\n", 728c2ecf20Sopenharmony_ci (void *)entry); 738c2ecf20Sopenharmony_ci continue; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ent = kmalloc(sizeof(*ent), GFP_KERNEL); 778c2ecf20Sopenharmony_ci if (!ent) 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci ent->start_addr = entry; 808c2ecf20Sopenharmony_ci ent->end_addr = entry + size; 818c2ecf20Sopenharmony_ci ent->etype = iter->etype; 828c2ecf20Sopenharmony_ci ent->priv = priv; 838c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ent->list); 848c2ecf20Sopenharmony_ci list_add_tail(&ent->list, &error_injection_list); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci mutex_unlock(&ei_mutex); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* Markers of the _error_inject_whitelist section */ 908c2ecf20Sopenharmony_ciextern struct error_injection_entry __start_error_injection_whitelist[]; 918c2ecf20Sopenharmony_ciextern struct error_injection_entry __stop_error_injection_whitelist[]; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void __init populate_kernel_ei_list(void) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci populate_error_injection_list(__start_error_injection_whitelist, 968c2ecf20Sopenharmony_ci __stop_error_injection_whitelist, 978c2ecf20Sopenharmony_ci NULL); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 1018c2ecf20Sopenharmony_cistatic void module_load_ei_list(struct module *mod) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci if (!mod->num_ei_funcs) 1048c2ecf20Sopenharmony_ci return; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci populate_error_injection_list(mod->ei_funcs, 1078c2ecf20Sopenharmony_ci mod->ei_funcs + mod->num_ei_funcs, mod); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void module_unload_ei_list(struct module *mod) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct ei_entry *ent, *n; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (!mod->num_ei_funcs) 1158c2ecf20Sopenharmony_ci return; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci mutex_lock(&ei_mutex); 1188c2ecf20Sopenharmony_ci list_for_each_entry_safe(ent, n, &error_injection_list, list) { 1198c2ecf20Sopenharmony_ci if (ent->priv == mod) { 1208c2ecf20Sopenharmony_ci list_del_init(&ent->list); 1218c2ecf20Sopenharmony_ci kfree(ent); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci mutex_unlock(&ei_mutex); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* Module notifier call back, checking error injection table on the module */ 1288c2ecf20Sopenharmony_cistatic int ei_module_callback(struct notifier_block *nb, 1298c2ecf20Sopenharmony_ci unsigned long val, void *data) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct module *mod = data; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (val == MODULE_STATE_COMING) 1348c2ecf20Sopenharmony_ci module_load_ei_list(mod); 1358c2ecf20Sopenharmony_ci else if (val == MODULE_STATE_GOING) 1368c2ecf20Sopenharmony_ci module_unload_ei_list(mod); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic struct notifier_block ei_module_nb = { 1428c2ecf20Sopenharmony_ci .notifier_call = ei_module_callback, 1438c2ecf20Sopenharmony_ci .priority = 0 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic __init int module_ei_init(void) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci return register_module_notifier(&ei_module_nb); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci#else /* !CONFIG_MODULES */ 1518c2ecf20Sopenharmony_ci#define module_ei_init() (0) 1528c2ecf20Sopenharmony_ci#endif 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * error_injection/whitelist -- shows which functions can be overridden for 1568c2ecf20Sopenharmony_ci * error injection. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic void *ei_seq_start(struct seq_file *m, loff_t *pos) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci mutex_lock(&ei_mutex); 1618c2ecf20Sopenharmony_ci return seq_list_start(&error_injection_list, *pos); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void ei_seq_stop(struct seq_file *m, void *v) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci mutex_unlock(&ei_mutex); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void *ei_seq_next(struct seq_file *m, void *v, loff_t *pos) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci return seq_list_next(v, &error_injection_list, pos); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const char *error_type_string(int etype) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci switch (etype) { 1778c2ecf20Sopenharmony_ci case EI_ETYPE_NULL: 1788c2ecf20Sopenharmony_ci return "NULL"; 1798c2ecf20Sopenharmony_ci case EI_ETYPE_ERRNO: 1808c2ecf20Sopenharmony_ci return "ERRNO"; 1818c2ecf20Sopenharmony_ci case EI_ETYPE_ERRNO_NULL: 1828c2ecf20Sopenharmony_ci return "ERRNO_NULL"; 1838c2ecf20Sopenharmony_ci default: 1848c2ecf20Sopenharmony_ci return "(unknown)"; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int ei_seq_show(struct seq_file *m, void *v) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct ei_entry *ent = list_entry(v, struct ei_entry, list); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci seq_printf(m, "%ps\t%s\n", (void *)ent->start_addr, 1938c2ecf20Sopenharmony_ci error_type_string(ent->etype)); 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic const struct seq_operations ei_seq_ops = { 1988c2ecf20Sopenharmony_ci .start = ei_seq_start, 1998c2ecf20Sopenharmony_ci .next = ei_seq_next, 2008c2ecf20Sopenharmony_ci .stop = ei_seq_stop, 2018c2ecf20Sopenharmony_ci .show = ei_seq_show, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int ei_open(struct inode *inode, struct file *filp) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci return seq_open(filp, &ei_seq_ops); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic const struct file_operations debugfs_ei_ops = { 2108c2ecf20Sopenharmony_ci .open = ei_open, 2118c2ecf20Sopenharmony_ci .read = seq_read, 2128c2ecf20Sopenharmony_ci .llseek = seq_lseek, 2138c2ecf20Sopenharmony_ci .release = seq_release, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int __init ei_debugfs_init(void) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct dentry *dir, *file; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci dir = debugfs_create_dir("error_injection", NULL); 2218c2ecf20Sopenharmony_ci if (!dir) 2228c2ecf20Sopenharmony_ci return -ENOMEM; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci file = debugfs_create_file("list", 0444, dir, NULL, &debugfs_ei_ops); 2258c2ecf20Sopenharmony_ci if (!file) { 2268c2ecf20Sopenharmony_ci debugfs_remove(dir); 2278c2ecf20Sopenharmony_ci return -ENOMEM; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int __init init_error_injection(void) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci populate_kernel_ei_list(); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (!module_ei_init()) 2388c2ecf20Sopenharmony_ci ei_debugfs_init(); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_cilate_initcall(init_error_injection); 243