1 // SPDX-License-Identifier: GPL-2.0
3 * fail_function.c: Function-based error injection
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>
15 static int fei_kprobe_handler(struct kprobe
*kp
, struct pt_regs
*regs
);
17 static void fei_post_handler(struct kprobe
*kp
, struct pt_regs
*regs
,
21 * A dummy post handler is required to prohibit optimizing, because
22 * jump optimization does not support execution path overriding.
27 struct list_head list
;
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
;
36 static unsigned long adjust_error_retval(unsigned long addr
, unsigned long retv
)
38 switch (get_injectable_error_type(addr
)) {
44 if (retv
< (unsigned long)-MAX_ERRNO
)
45 return (unsigned long)-EINVAL
;
47 case EI_ETYPE_ERRNO_NULL
:
48 if (retv
!= 0 && retv
< (unsigned long)-MAX_ERRNO
)
49 return (unsigned long)-EINVAL
;
56 static struct fei_attr
*fei_attr_new(const char *sym
, unsigned long addr
)
58 struct fei_attr
*attr
;
60 attr
= kzalloc(sizeof(*attr
), GFP_KERNEL
);
62 attr
->kp
.symbol_name
= kstrdup(sym
, GFP_KERNEL
);
63 if (!attr
->kp
.symbol_name
) {
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
);
75 static void fei_attr_free(struct fei_attr
*attr
)
78 kfree(attr
->kp
.symbol_name
);
83 static struct fei_attr
*fei_attr_lookup(const char *sym
)
85 struct fei_attr
*attr
;
87 list_for_each_entry(attr
, &fei_attr_list
, list
) {
88 if (!strcmp(attr
->kp
.symbol_name
, sym
))
95 static bool fei_attr_is_valid(struct fei_attr
*_attr
)
97 struct fei_attr
*attr
;
99 list_for_each_entry(attr
, &fei_attr_list
, list
) {
107 static int fei_retval_set(void *data
, u64 val
)
109 struct fei_attr
*attr
= data
;
110 unsigned long retv
= (unsigned long)val
;
113 mutex_lock(&fei_lock
);
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
119 if (!fei_attr_is_valid(attr
)) {
125 if (adjust_error_retval((unsigned long)attr
->kp
.addr
,
132 mutex_unlock(&fei_lock
);
137 static int fei_retval_get(void *data
, u64
*val
)
139 struct fei_attr
*attr
= data
;
142 mutex_lock(&fei_lock
);
143 /* Here we also validate @attr to ensure it still exists. */
144 if (!fei_attr_is_valid(attr
))
148 mutex_unlock(&fei_lock
);
152 DEFINE_DEBUGFS_ATTRIBUTE(fei_retval_ops
, fei_retval_get
, fei_retval_set
,
155 static int fei_debugfs_add_attr(struct fei_attr
*attr
)
159 dir
= debugfs_create_dir(attr
->kp
.symbol_name
, fei_debugfs_dir
);
163 if (!debugfs_create_file("retval", 0600, dir
, attr
, &fei_retval_ops
)) {
164 debugfs_remove_recursive(dir
);
171 static void fei_debugfs_remove_attr(struct fei_attr
*attr
)
175 dir
= debugfs_lookup(attr
->kp
.symbol_name
, fei_debugfs_dir
);
177 debugfs_remove_recursive(dir
);
180 static int fei_kprobe_handler(struct kprobe
*kp
, struct pt_regs
*regs
)
182 struct fei_attr
*attr
= container_of(kp
, struct fei_attr
, kp
);
184 if (should_fail(&fei_fault_attr
, 1)) {
185 regs_set_return_value(regs
, attr
->retval
);
186 override_function_with_return(regs
);
187 /* Kprobe specific fixup */
188 reset_current_kprobe();
189 preempt_enable_no_resched();
195 NOKPROBE_SYMBOL(fei_kprobe_handler
)
197 static void *fei_seq_start(struct seq_file
*m
, loff_t
*pos
)
199 mutex_lock(&fei_lock
);
200 return seq_list_start(&fei_attr_list
, *pos
);
203 static void fei_seq_stop(struct seq_file
*m
, void *v
)
205 mutex_unlock(&fei_lock
);
208 static void *fei_seq_next(struct seq_file
*m
, void *v
, loff_t
*pos
)
210 return seq_list_next(v
, &fei_attr_list
, pos
);
213 static int fei_seq_show(struct seq_file
*m
, void *v
)
215 struct fei_attr
*attr
= list_entry(v
, struct fei_attr
, list
);
217 seq_printf(m
, "%pf\n", attr
->kp
.addr
);
221 static const struct seq_operations fei_seq_ops
= {
222 .start
= fei_seq_start
,
223 .next
= fei_seq_next
,
224 .stop
= fei_seq_stop
,
225 .show
= fei_seq_show
,
228 static int fei_open(struct inode
*inode
, struct file
*file
)
230 return seq_open(file
, &fei_seq_ops
);
233 static void fei_attr_remove(struct fei_attr
*attr
)
235 fei_debugfs_remove_attr(attr
);
236 unregister_kprobe(&attr
->kp
);
237 list_del(&attr
->list
);
241 static void fei_attr_remove_all(void)
243 struct fei_attr
*attr
, *n
;
245 list_for_each_entry_safe(attr
, n
, &fei_attr_list
, list
) {
246 fei_attr_remove(attr
);
250 static ssize_t
fei_write(struct file
*file
, const char __user
*buffer
,
251 size_t count
, loff_t
*ppos
)
253 struct fei_attr
*attr
;
258 /* cut off if it is too long */
259 if (count
> KSYM_NAME_LEN
)
260 count
= KSYM_NAME_LEN
;
261 buf
= kmalloc(count
+ 1, GFP_KERNEL
);
265 if (copy_from_user(buf
, buffer
, count
)) {
272 mutex_lock(&fei_lock
);
274 /* Writing just spaces will remove all injection points */
275 if (sym
[0] == '\0') {
276 fei_attr_remove_all();
280 /* Writing !function will remove one injection point */
282 attr
= fei_attr_lookup(sym
+ 1);
287 fei_attr_remove(attr
);
292 addr
= kallsyms_lookup_name(sym
);
297 if (!within_error_injection_list(addr
)) {
301 if (fei_attr_lookup(sym
)) {
305 attr
= fei_attr_new(sym
, addr
);
311 ret
= register_kprobe(&attr
->kp
);
313 ret
= fei_debugfs_add_attr(attr
);
315 fei_attr_remove(attr
);
317 list_add_tail(&attr
->list
, &fei_attr_list
);
322 mutex_unlock(&fei_lock
);
326 static const struct file_operations fei_ops
= {
331 .release
= seq_release
,
334 static int __init
fei_debugfs_init(void)
338 dir
= fault_create_debugfs_attr("fail_function", NULL
,
343 /* injectable attribute is just a symlink of error_inject/list */
344 if (!debugfs_create_symlink("injectable", dir
,
345 "../error_injection/list"))
348 if (!debugfs_create_file("inject", 0600, dir
, NULL
, &fei_ops
))
351 fei_debugfs_dir
= dir
;
355 debugfs_remove_recursive(dir
);
359 late_initcall(fei_debugfs_init
);