diff options
author | Masami Hiramatsu <mhiramat@kernel.org> | 2018-01-12 12:55:03 -0500 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2018-01-12 20:33:38 -0500 |
commit | 540adea3809f61115d2a1ea4ed6e627613452ba1 (patch) | |
tree | 03ba07d13807d06d52053b2d02565075f210c2e2 /lib/error-inject.c | |
parent | 66665ad2f1023d3ffb0c12eea9e0a6d0b613ecb3 (diff) |
error-injection: Separate error-injection from kprobe
Since error-injection framework is not limited to be used
by kprobes, nor bpf. Other kernel subsystems can use it
freely for checking safeness of error-injection, e.g.
livepatch, ftrace etc.
So this separate error-injection framework from kprobes.
Some differences has been made:
- "kprobe" word is removed from any APIs/structures.
- BPF_ALLOW_ERROR_INJECTION() is renamed to
ALLOW_ERROR_INJECTION() since it is not limited for BPF too.
- CONFIG_FUNCTION_ERROR_INJECTION is the config item of this
feature. It is automatically enabled if the arch supports
error injection feature for kprobe or ftrace etc.
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Reviewed-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'lib/error-inject.c')
-rw-r--r-- | lib/error-inject.c | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/lib/error-inject.c b/lib/error-inject.c new file mode 100644 index 000000000000..bccadcf3c981 --- /dev/null +++ b/lib/error-inject.c | |||
@@ -0,0 +1,213 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // error-inject.c: Function-level error injection table | ||
3 | #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 | |||
12 | /* Whitelist of symbols that can be overridden for error injection. */ | ||
13 | static LIST_HEAD(error_injection_list); | ||
14 | static DEFINE_MUTEX(ei_mutex); | ||
15 | struct ei_entry { | ||
16 | struct list_head list; | ||
17 | unsigned long start_addr; | ||
18 | unsigned long end_addr; | ||
19 | void *priv; | ||
20 | }; | ||
21 | |||
22 | bool within_error_injection_list(unsigned long addr) | ||
23 | { | ||
24 | struct ei_entry *ent; | ||
25 | bool ret = false; | ||
26 | |||
27 | mutex_lock(&ei_mutex); | ||
28 | list_for_each_entry(ent, &error_injection_list, list) { | ||
29 | if (addr >= ent->start_addr && addr < ent->end_addr) { | ||
30 | ret = true; | ||
31 | break; | ||
32 | } | ||
33 | } | ||
34 | mutex_unlock(&ei_mutex); | ||
35 | return ret; | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * Lookup and populate the error_injection_list. | ||
40 | * | ||
41 | * For safety reasons we only allow certain functions to be overridden with | ||
42 | * bpf_error_injection, so we need to populate the list of the symbols that have | ||
43 | * been marked as safe for overriding. | ||
44 | */ | ||
45 | static void populate_error_injection_list(unsigned long *start, | ||
46 | unsigned long *end, void *priv) | ||
47 | { | ||
48 | unsigned long *iter; | ||
49 | struct ei_entry *ent; | ||
50 | unsigned long entry, offset = 0, size = 0; | ||
51 | |||
52 | mutex_lock(&ei_mutex); | ||
53 | for (iter = start; iter < end; iter++) { | ||
54 | entry = arch_deref_entry_point((void *)*iter); | ||
55 | |||
56 | if (!kernel_text_address(entry) || | ||
57 | !kallsyms_lookup_size_offset(entry, &size, &offset)) { | ||
58 | pr_err("Failed to find error inject entry at %p\n", | ||
59 | (void *)entry); | ||
60 | continue; | ||
61 | } | ||
62 | |||
63 | ent = kmalloc(sizeof(*ent), GFP_KERNEL); | ||
64 | if (!ent) | ||
65 | break; | ||
66 | ent->start_addr = entry; | ||
67 | ent->end_addr = entry + size; | ||
68 | ent->priv = priv; | ||
69 | INIT_LIST_HEAD(&ent->list); | ||
70 | list_add_tail(&ent->list, &error_injection_list); | ||
71 | } | ||
72 | mutex_unlock(&ei_mutex); | ||
73 | } | ||
74 | |||
75 | /* Markers of the _error_inject_whitelist section */ | ||
76 | extern unsigned long __start_error_injection_whitelist[]; | ||
77 | extern unsigned long __stop_error_injection_whitelist[]; | ||
78 | |||
79 | static void __init populate_kernel_ei_list(void) | ||
80 | { | ||
81 | populate_error_injection_list(__start_error_injection_whitelist, | ||
82 | __stop_error_injection_whitelist, | ||
83 | NULL); | ||
84 | } | ||
85 | |||
86 | #ifdef CONFIG_MODULES | ||
87 | static void module_load_ei_list(struct module *mod) | ||
88 | { | ||
89 | if (!mod->num_ei_funcs) | ||
90 | return; | ||
91 | |||
92 | populate_error_injection_list(mod->ei_funcs, | ||
93 | mod->ei_funcs + mod->num_ei_funcs, mod); | ||
94 | } | ||
95 | |||
96 | static void module_unload_ei_list(struct module *mod) | ||
97 | { | ||
98 | struct ei_entry *ent, *n; | ||
99 | |||
100 | if (!mod->num_ei_funcs) | ||
101 | return; | ||
102 | |||
103 | mutex_lock(&ei_mutex); | ||
104 | list_for_each_entry_safe(ent, n, &error_injection_list, list) { | ||
105 | if (ent->priv == mod) { | ||
106 | list_del_init(&ent->list); | ||
107 | kfree(ent); | ||
108 | } | ||
109 | } | ||
110 | mutex_unlock(&ei_mutex); | ||
111 | } | ||
112 | |||
113 | /* Module notifier call back, checking error injection table on the module */ | ||
114 | static int ei_module_callback(struct notifier_block *nb, | ||
115 | unsigned long val, void *data) | ||
116 | { | ||
117 | struct module *mod = data; | ||
118 | |||
119 | if (val == MODULE_STATE_COMING) | ||
120 | module_load_ei_list(mod); | ||
121 | else if (val == MODULE_STATE_GOING) | ||
122 | module_unload_ei_list(mod); | ||
123 | |||
124 | return NOTIFY_DONE; | ||
125 | } | ||
126 | |||
127 | static struct notifier_block ei_module_nb = { | ||
128 | .notifier_call = ei_module_callback, | ||
129 | .priority = 0 | ||
130 | }; | ||
131 | |||
132 | static __init int module_ei_init(void) | ||
133 | { | ||
134 | return register_module_notifier(&ei_module_nb); | ||
135 | } | ||
136 | #else /* !CONFIG_MODULES */ | ||
137 | #define module_ei_init() (0) | ||
138 | #endif | ||
139 | |||
140 | /* | ||
141 | * error_injection/whitelist -- shows which functions can be overridden for | ||
142 | * error injection. | ||
143 | */ | ||
144 | static void *ei_seq_start(struct seq_file *m, loff_t *pos) | ||
145 | { | ||
146 | mutex_lock(&ei_mutex); | ||
147 | return seq_list_start(&error_injection_list, *pos); | ||
148 | } | ||
149 | |||
150 | static void ei_seq_stop(struct seq_file *m, void *v) | ||
151 | { | ||
152 | mutex_unlock(&ei_mutex); | ||
153 | } | ||
154 | |||
155 | static void *ei_seq_next(struct seq_file *m, void *v, loff_t *pos) | ||
156 | { | ||
157 | return seq_list_next(v, &error_injection_list, pos); | ||
158 | } | ||
159 | |||
160 | static int ei_seq_show(struct seq_file *m, void *v) | ||
161 | { | ||
162 | struct ei_entry *ent = list_entry(v, struct ei_entry, list); | ||
163 | |||
164 | seq_printf(m, "%pf\n", (void *)ent->start_addr); | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static const struct seq_operations ei_seq_ops = { | ||
169 | .start = ei_seq_start, | ||
170 | .next = ei_seq_next, | ||
171 | .stop = ei_seq_stop, | ||
172 | .show = ei_seq_show, | ||
173 | }; | ||
174 | |||
175 | static int ei_open(struct inode *inode, struct file *filp) | ||
176 | { | ||
177 | return seq_open(filp, &ei_seq_ops); | ||
178 | } | ||
179 | |||
180 | static const struct file_operations debugfs_ei_ops = { | ||
181 | .open = ei_open, | ||
182 | .read = seq_read, | ||
183 | .llseek = seq_lseek, | ||
184 | .release = seq_release, | ||
185 | }; | ||
186 | |||
187 | static int __init ei_debugfs_init(void) | ||
188 | { | ||
189 | struct dentry *dir, *file; | ||
190 | |||
191 | dir = debugfs_create_dir("error_injection", NULL); | ||
192 | if (!dir) | ||
193 | return -ENOMEM; | ||
194 | |||
195 | file = debugfs_create_file("list", 0444, dir, NULL, &debugfs_ei_ops); | ||
196 | if (!file) { | ||
197 | debugfs_remove(dir); | ||
198 | return -ENOMEM; | ||
199 | } | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int __init init_error_injection(void) | ||
205 | { | ||
206 | populate_kernel_ei_list(); | ||
207 | |||
208 | if (!module_ei_init()) | ||
209 | ei_debugfs_init(); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | late_initcall(init_error_injection); | ||