mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									66665ad2f1
								
							
						
					
					
						commit
						540adea380
					
				
					 22 changed files with 317 additions and 213 deletions
				
			
		| 
						 | 
					@ -196,7 +196,7 @@ config HAVE_OPTPROBES
 | 
				
			||||||
config HAVE_KPROBES_ON_FTRACE
 | 
					config HAVE_KPROBES_ON_FTRACE
 | 
				
			||||||
	bool
 | 
						bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config HAVE_KPROBE_OVERRIDE
 | 
					config HAVE_FUNCTION_ERROR_INJECTION
 | 
				
			||||||
	bool
 | 
						bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config HAVE_NMI
 | 
					config HAVE_NMI
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -154,7 +154,7 @@ config X86
 | 
				
			||||||
	select HAVE_KERNEL_XZ
 | 
						select HAVE_KERNEL_XZ
 | 
				
			||||||
	select HAVE_KPROBES
 | 
						select HAVE_KPROBES
 | 
				
			||||||
	select HAVE_KPROBES_ON_FTRACE
 | 
						select HAVE_KPROBES_ON_FTRACE
 | 
				
			||||||
	select HAVE_KPROBE_OVERRIDE
 | 
						select HAVE_FUNCTION_ERROR_INJECTION
 | 
				
			||||||
	select HAVE_KRETPROBES
 | 
						select HAVE_KRETPROBES
 | 
				
			||||||
	select HAVE_KVM
 | 
						select HAVE_KVM
 | 
				
			||||||
	select HAVE_LIVEPATCH			if X86_64
 | 
						select HAVE_LIVEPATCH			if X86_64
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								arch/x86/include/asm/error-injection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								arch/x86/include/asm/error-injection.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					#ifndef _ASM_ERROR_INJECTION_H
 | 
				
			||||||
 | 
					#define _ASM_ERROR_INJECTION_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/compiler.h>
 | 
				
			||||||
 | 
					#include <linux/linkage.h>
 | 
				
			||||||
 | 
					#include <asm/ptrace.h>
 | 
				
			||||||
 | 
					#include <asm-generic/error-injection.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					asmlinkage void just_return_func(void);
 | 
				
			||||||
 | 
					void override_function_with_return(struct pt_regs *regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _ASM_ERROR_INJECTION_H */
 | 
				
			||||||
| 
						 | 
					@ -1183,17 +1183,3 @@ int arch_trampoline_kprobe(struct kprobe *p)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
asmlinkage void override_func(void);
 | 
					 | 
				
			||||||
asm(
 | 
					 | 
				
			||||||
	".type override_func, @function\n"
 | 
					 | 
				
			||||||
	"override_func:\n"
 | 
					 | 
				
			||||||
	"	ret\n"
 | 
					 | 
				
			||||||
	".size override_func, .-override_func\n"
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void arch_kprobe_override_function(struct pt_regs *regs)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	regs->ip = (unsigned long)&override_func;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
NOKPROBE_SYMBOL(arch_kprobe_override_function);
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@ lib-y += memcpy_$(BITS).o
 | 
				
			||||||
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
 | 
					lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
 | 
				
			||||||
lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o insn-eval.o
 | 
					lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o insn-eval.o
 | 
				
			||||||
lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
 | 
					lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
 | 
				
			||||||
 | 
					lib-$(CONFIG_FUNCTION_ERROR_INJECTION)	+= error-inject.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
 | 
					obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										19
									
								
								arch/x86/lib/error-inject.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								arch/x86/lib/error-inject.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/error-injection.h>
 | 
				
			||||||
 | 
					#include <linux/kprobes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					asmlinkage void just_return_func(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					asm(
 | 
				
			||||||
 | 
						".type just_return_func, @function\n"
 | 
				
			||||||
 | 
						"just_return_func:\n"
 | 
				
			||||||
 | 
						"	ret\n"
 | 
				
			||||||
 | 
						".size just_return_func, .-just_return_func\n"
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void override_function_with_return(struct pt_regs *regs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						regs->ip = (unsigned long)&just_return_func;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					NOKPROBE_SYMBOL(override_function_with_return);
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@
 | 
				
			||||||
#include <linux/ratelimit.h>
 | 
					#include <linux/ratelimit.h>
 | 
				
			||||||
#include <linux/uuid.h>
 | 
					#include <linux/uuid.h>
 | 
				
			||||||
#include <linux/semaphore.h>
 | 
					#include <linux/semaphore.h>
 | 
				
			||||||
#include <linux/bpf.h>
 | 
					#include <linux/error-injection.h>
 | 
				
			||||||
#include <asm/unaligned.h>
 | 
					#include <asm/unaligned.h>
 | 
				
			||||||
#include "ctree.h"
 | 
					#include "ctree.h"
 | 
				
			||||||
#include "disk-io.h"
 | 
					#include "disk-io.h"
 | 
				
			||||||
| 
						 | 
					@ -3124,7 +3124,7 @@ int open_ctree(struct super_block *sb,
 | 
				
			||||||
		goto fail_block_groups;
 | 
							goto fail_block_groups;
 | 
				
			||||||
	goto retry_root_backup;
 | 
						goto retry_root_backup;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
BPF_ALLOW_ERROR_INJECTION(open_ctree);
 | 
					ALLOW_ERROR_INJECTION(open_ctree);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
 | 
					static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/math64.h>
 | 
					#include <linux/math64.h>
 | 
				
			||||||
#include <linux/ratelimit.h>
 | 
					#include <linux/ratelimit.h>
 | 
				
			||||||
#include <linux/bpf.h>
 | 
					#include <linux/error-injection.h>
 | 
				
			||||||
#include "ctree.h"
 | 
					#include "ctree.h"
 | 
				
			||||||
#include "free-space-cache.h"
 | 
					#include "free-space-cache.h"
 | 
				
			||||||
#include "transaction.h"
 | 
					#include "transaction.h"
 | 
				
			||||||
| 
						 | 
					@ -333,7 +333,7 @@ static int io_ctl_init(struct btrfs_io_ctl *io_ctl, struct inode *inode,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
BPF_ALLOW_ERROR_INJECTION(io_ctl_init);
 | 
					ALLOW_ERROR_INJECTION(io_ctl_init);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void io_ctl_free(struct btrfs_io_ctl *io_ctl)
 | 
					static void io_ctl_free(struct btrfs_io_ctl *io_ctl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								include/asm-generic/error-injection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								include/asm-generic/error-injection.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					#ifndef _ASM_GENERIC_ERROR_INJECTION_H
 | 
				
			||||||
 | 
					#define _ASM_GENERIC_ERROR_INJECTION_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
 | 
				
			||||||
 | 
					#ifdef CONFIG_FUNCTION_ERROR_INJECTION
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Whitelist ganerating macro. Specify functions which can be
 | 
				
			||||||
 | 
					 * error-injectable using this macro.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define ALLOW_ERROR_INJECTION(fname)					\
 | 
				
			||||||
 | 
					static unsigned long __used						\
 | 
				
			||||||
 | 
						__attribute__((__section__("_error_injection_whitelist")))	\
 | 
				
			||||||
 | 
						_eil_addr_##fname = (unsigned long)fname;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define ALLOW_ERROR_INJECTION(fname)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _ASM_GENERIC_ERROR_INJECTION_H */
 | 
				
			||||||
| 
						 | 
					@ -136,13 +136,13 @@
 | 
				
			||||||
#define KPROBE_BLACKLIST()
 | 
					#define KPROBE_BLACKLIST()
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_BPF_KPROBE_OVERRIDE
 | 
					#ifdef CONFIG_FUNCTION_ERROR_INJECTION
 | 
				
			||||||
#define ERROR_INJECT_LIST()	. = ALIGN(8);						\
 | 
					#define ERROR_INJECT_WHITELIST()	. = ALIGN(8);			      \
 | 
				
			||||||
				VMLINUX_SYMBOL(__start_kprobe_error_inject_list) = .;	\
 | 
								VMLINUX_SYMBOL(__start_error_injection_whitelist) = .;\
 | 
				
			||||||
				KEEP(*(_kprobe_error_inject_list))			\
 | 
								KEEP(*(_error_injection_whitelist))		      \
 | 
				
			||||||
				VMLINUX_SYMBOL(__stop_kprobe_error_inject_list) = .;
 | 
								VMLINUX_SYMBOL(__stop_error_injection_whitelist) = .;
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#define ERROR_INJECT_LIST()
 | 
					#define ERROR_INJECT_WHITELIST()
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_EVENT_TRACING
 | 
					#ifdef CONFIG_EVENT_TRACING
 | 
				
			||||||
| 
						 | 
					@ -573,7 +573,7 @@
 | 
				
			||||||
	FTRACE_EVENTS()							\
 | 
						FTRACE_EVENTS()							\
 | 
				
			||||||
	TRACE_SYSCALLS()						\
 | 
						TRACE_SYSCALLS()						\
 | 
				
			||||||
	KPROBE_BLACKLIST()						\
 | 
						KPROBE_BLACKLIST()						\
 | 
				
			||||||
	ERROR_INJECT_LIST()						\
 | 
						ERROR_INJECT_WHITELIST()					\
 | 
				
			||||||
	MEM_DISCARD(init.rodata)					\
 | 
						MEM_DISCARD(init.rodata)					\
 | 
				
			||||||
	CLK_OF_TABLES()							\
 | 
						CLK_OF_TABLES()							\
 | 
				
			||||||
	RESERVEDMEM_OF_TABLES()						\
 | 
						RESERVEDMEM_OF_TABLES()						\
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -613,15 +613,4 @@ extern const struct bpf_func_proto bpf_sock_map_update_proto;
 | 
				
			||||||
void bpf_user_rnd_init_once(void);
 | 
					void bpf_user_rnd_init_once(void);
 | 
				
			||||||
u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
 | 
					u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
 | 
					 | 
				
			||||||
#ifdef CONFIG_BPF_KPROBE_OVERRIDE
 | 
					 | 
				
			||||||
#define BPF_ALLOW_ERROR_INJECTION(fname)				\
 | 
					 | 
				
			||||||
static unsigned long __used						\
 | 
					 | 
				
			||||||
	__attribute__((__section__("_kprobe_error_inject_list")))	\
 | 
					 | 
				
			||||||
	_eil_addr_##fname = (unsigned long)fname;
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#define BPF_ALLOW_ERROR_INJECTION(fname)
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* _LINUX_BPF_H */
 | 
					#endif /* _LINUX_BPF_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								include/linux/error-injection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								include/linux/error-injection.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					#ifndef _LINUX_ERROR_INJECTION_H
 | 
				
			||||||
 | 
					#define _LINUX_ERROR_INJECTION_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_FUNCTION_ERROR_INJECTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm/error-injection.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern bool within_error_injection_list(unsigned long addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#else /* !CONFIG_FUNCTION_ERROR_INJECTION */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm-generic/error-injection.h>
 | 
				
			||||||
 | 
					static inline bool within_error_injection_list(unsigned long addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _LINUX_ERROR_INJECTION_H */
 | 
				
			||||||
| 
						 | 
					@ -271,7 +271,6 @@ extern bool arch_kprobe_on_func_entry(unsigned long offset);
 | 
				
			||||||
extern bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset);
 | 
					extern bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern bool within_kprobe_blacklist(unsigned long addr);
 | 
					extern bool within_kprobe_blacklist(unsigned long addr);
 | 
				
			||||||
extern bool within_kprobe_error_injection_list(unsigned long addr);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct kprobe_insn_cache {
 | 
					struct kprobe_insn_cache {
 | 
				
			||||||
	struct mutex mutex;
 | 
						struct mutex mutex;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -476,9 +476,9 @@ struct module {
 | 
				
			||||||
	unsigned int num_ctors;
 | 
						unsigned int num_ctors;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_BPF_KPROBE_OVERRIDE
 | 
					#ifdef CONFIG_FUNCTION_ERROR_INJECTION
 | 
				
			||||||
	unsigned int num_kprobe_ei_funcs;
 | 
						unsigned int num_ei_funcs;
 | 
				
			||||||
	unsigned long *kprobe_ei_funcs;
 | 
						unsigned long *ei_funcs;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
} ____cacheline_aligned __randomize_layout;
 | 
					} ____cacheline_aligned __randomize_layout;
 | 
				
			||||||
#ifndef MODULE_ARCH_INIT
 | 
					#ifndef MODULE_ARCH_INIT
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										163
									
								
								kernel/kprobes.c
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								kernel/kprobes.c
									
									
									
									
									
								
							| 
						 | 
					@ -83,16 +83,6 @@ static raw_spinlock_t *kretprobe_table_lock_ptr(unsigned long hash)
 | 
				
			||||||
	return &(kretprobe_table_locks[hash].lock);
 | 
						return &(kretprobe_table_locks[hash].lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* List of symbols that can be overriden for error injection. */
 | 
					 | 
				
			||||||
static LIST_HEAD(kprobe_error_injection_list);
 | 
					 | 
				
			||||||
static DEFINE_MUTEX(kprobe_ei_mutex);
 | 
					 | 
				
			||||||
struct kprobe_ei_entry {
 | 
					 | 
				
			||||||
	struct list_head list;
 | 
					 | 
				
			||||||
	unsigned long start_addr;
 | 
					 | 
				
			||||||
	unsigned long end_addr;
 | 
					 | 
				
			||||||
	void *priv;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Blacklist -- list of struct kprobe_blacklist_entry */
 | 
					/* Blacklist -- list of struct kprobe_blacklist_entry */
 | 
				
			||||||
static LIST_HEAD(kprobe_blacklist);
 | 
					static LIST_HEAD(kprobe_blacklist);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1404,17 +1394,6 @@ bool within_kprobe_blacklist(unsigned long addr)
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool within_kprobe_error_injection_list(unsigned long addr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct kprobe_ei_entry *ent;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	list_for_each_entry(ent, &kprobe_error_injection_list, list) {
 | 
					 | 
				
			||||||
		if (addr >= ent->start_addr && addr < ent->end_addr)
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * If we have a symbol_name argument, look it up and add the offset field
 | 
					 * If we have a symbol_name argument, look it up and add the offset field
 | 
				
			||||||
 * to it. This way, we can specify a relative address to a symbol.
 | 
					 * to it. This way, we can specify a relative address to a symbol.
 | 
				
			||||||
| 
						 | 
					@ -2189,86 +2168,6 @@ static int __init populate_kprobe_blacklist(unsigned long *start,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_BPF_KPROBE_OVERRIDE
 | 
					 | 
				
			||||||
/* Markers of the _kprobe_error_inject_list section */
 | 
					 | 
				
			||||||
extern unsigned long __start_kprobe_error_inject_list[];
 | 
					 | 
				
			||||||
extern unsigned long __stop_kprobe_error_inject_list[];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * Lookup and populate the kprobe_error_injection_list.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * For safety reasons we only allow certain functions to be overriden with
 | 
					 | 
				
			||||||
 * bpf_error_injection, so we need to populate the list of the symbols that have
 | 
					 | 
				
			||||||
 * been marked as safe for overriding.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static void populate_kprobe_error_injection_list(unsigned long *start,
 | 
					 | 
				
			||||||
						 unsigned long *end,
 | 
					 | 
				
			||||||
						 void *priv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	unsigned long *iter;
 | 
					 | 
				
			||||||
	struct kprobe_ei_entry *ent;
 | 
					 | 
				
			||||||
	unsigned long entry, offset = 0, size = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mutex_lock(&kprobe_ei_mutex);
 | 
					 | 
				
			||||||
	for (iter = start; iter < end; iter++) {
 | 
					 | 
				
			||||||
		entry = arch_deref_entry_point((void *)*iter);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!kernel_text_address(entry) ||
 | 
					 | 
				
			||||||
		    !kallsyms_lookup_size_offset(entry, &size, &offset)) {
 | 
					 | 
				
			||||||
			pr_err("Failed to find error inject entry at %p\n",
 | 
					 | 
				
			||||||
				(void *)entry);
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ent = kmalloc(sizeof(*ent), GFP_KERNEL);
 | 
					 | 
				
			||||||
		if (!ent)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		ent->start_addr = entry;
 | 
					 | 
				
			||||||
		ent->end_addr = entry + size;
 | 
					 | 
				
			||||||
		ent->priv = priv;
 | 
					 | 
				
			||||||
		INIT_LIST_HEAD(&ent->list);
 | 
					 | 
				
			||||||
		list_add_tail(&ent->list, &kprobe_error_injection_list);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	mutex_unlock(&kprobe_ei_mutex);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void __init populate_kernel_kprobe_ei_list(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	populate_kprobe_error_injection_list(__start_kprobe_error_inject_list,
 | 
					 | 
				
			||||||
					     __stop_kprobe_error_inject_list,
 | 
					 | 
				
			||||||
					     NULL);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void module_load_kprobe_ei_list(struct module *mod)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!mod->num_kprobe_ei_funcs)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	populate_kprobe_error_injection_list(mod->kprobe_ei_funcs,
 | 
					 | 
				
			||||||
					     mod->kprobe_ei_funcs +
 | 
					 | 
				
			||||||
					     mod->num_kprobe_ei_funcs, mod);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void module_unload_kprobe_ei_list(struct module *mod)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct kprobe_ei_entry *ent, *n;
 | 
					 | 
				
			||||||
	if (!mod->num_kprobe_ei_funcs)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mutex_lock(&kprobe_ei_mutex);
 | 
					 | 
				
			||||||
	list_for_each_entry_safe(ent, n, &kprobe_error_injection_list, list) {
 | 
					 | 
				
			||||||
		if (ent->priv == mod) {
 | 
					 | 
				
			||||||
			list_del_init(&ent->list);
 | 
					 | 
				
			||||||
			kfree(ent);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	mutex_unlock(&kprobe_ei_mutex);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
static inline void __init populate_kernel_kprobe_ei_list(void) {}
 | 
					 | 
				
			||||||
static inline void module_load_kprobe_ei_list(struct module *m) {}
 | 
					 | 
				
			||||||
static inline void module_unload_kprobe_ei_list(struct module *m) {}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Module notifier call back, checking kprobes on the module */
 | 
					/* Module notifier call back, checking kprobes on the module */
 | 
				
			||||||
static int kprobes_module_callback(struct notifier_block *nb,
 | 
					static int kprobes_module_callback(struct notifier_block *nb,
 | 
				
			||||||
				   unsigned long val, void *data)
 | 
									   unsigned long val, void *data)
 | 
				
			||||||
| 
						 | 
					@ -2279,11 +2178,6 @@ static int kprobes_module_callback(struct notifier_block *nb,
 | 
				
			||||||
	unsigned int i;
 | 
						unsigned int i;
 | 
				
			||||||
	int checkcore = (val == MODULE_STATE_GOING);
 | 
						int checkcore = (val == MODULE_STATE_GOING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (val == MODULE_STATE_COMING)
 | 
					 | 
				
			||||||
		module_load_kprobe_ei_list(mod);
 | 
					 | 
				
			||||||
	else if (val == MODULE_STATE_GOING)
 | 
					 | 
				
			||||||
		module_unload_kprobe_ei_list(mod);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (val != MODULE_STATE_GOING && val != MODULE_STATE_LIVE)
 | 
						if (val != MODULE_STATE_GOING && val != MODULE_STATE_LIVE)
 | 
				
			||||||
		return NOTIFY_DONE;
 | 
							return NOTIFY_DONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2346,8 +2240,6 @@ static int __init init_kprobes(void)
 | 
				
			||||||
		pr_err("Please take care of using kprobes.\n");
 | 
							pr_err("Please take care of using kprobes.\n");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	populate_kernel_kprobe_ei_list();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (kretprobe_blacklist_size) {
 | 
						if (kretprobe_blacklist_size) {
 | 
				
			||||||
		/* lookup the function address from its name */
 | 
							/* lookup the function address from its name */
 | 
				
			||||||
		for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
 | 
							for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
 | 
				
			||||||
| 
						 | 
					@ -2515,56 +2407,6 @@ static const struct file_operations debugfs_kprobe_blacklist_ops = {
 | 
				
			||||||
	.release        = seq_release,
 | 
						.release        = seq_release,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * kprobes/error_injection_list -- shows which functions can be overriden for
 | 
					 | 
				
			||||||
 * error injection.
 | 
					 | 
				
			||||||
 * */
 | 
					 | 
				
			||||||
static void *kprobe_ei_seq_start(struct seq_file *m, loff_t *pos)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	mutex_lock(&kprobe_ei_mutex);
 | 
					 | 
				
			||||||
	return seq_list_start(&kprobe_error_injection_list, *pos);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void kprobe_ei_seq_stop(struct seq_file *m, void *v)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	mutex_unlock(&kprobe_ei_mutex);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void *kprobe_ei_seq_next(struct seq_file *m, void *v, loff_t *pos)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return seq_list_next(v, &kprobe_error_injection_list, pos);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int kprobe_ei_seq_show(struct seq_file *m, void *v)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	char buffer[KSYM_SYMBOL_LEN];
 | 
					 | 
				
			||||||
	struct kprobe_ei_entry *ent =
 | 
					 | 
				
			||||||
		list_entry(v, struct kprobe_ei_entry, list);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sprint_symbol(buffer, ent->start_addr);
 | 
					 | 
				
			||||||
	seq_printf(m, "%s\n", buffer);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct seq_operations kprobe_ei_seq_ops = {
 | 
					 | 
				
			||||||
	.start = kprobe_ei_seq_start,
 | 
					 | 
				
			||||||
	.next  = kprobe_ei_seq_next,
 | 
					 | 
				
			||||||
	.stop  = kprobe_ei_seq_stop,
 | 
					 | 
				
			||||||
	.show  = kprobe_ei_seq_show,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int kprobe_ei_open(struct inode *inode, struct file *filp)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return seq_open(filp, &kprobe_ei_seq_ops);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct file_operations debugfs_kprobe_ei_ops = {
 | 
					 | 
				
			||||||
	.open           = kprobe_ei_open,
 | 
					 | 
				
			||||||
	.read           = seq_read,
 | 
					 | 
				
			||||||
	.llseek         = seq_lseek,
 | 
					 | 
				
			||||||
	.release        = seq_release,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void arm_all_kprobes(void)
 | 
					static void arm_all_kprobes(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct hlist_head *head;
 | 
						struct hlist_head *head;
 | 
				
			||||||
| 
						 | 
					@ -2706,11 +2548,6 @@ static int __init debugfs_kprobe_init(void)
 | 
				
			||||||
	if (!file)
 | 
						if (!file)
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	file = debugfs_create_file("error_injection_list", 0444, dir, NULL,
 | 
					 | 
				
			||||||
				  &debugfs_kprobe_ei_ops);
 | 
					 | 
				
			||||||
	if (!file)
 | 
					 | 
				
			||||||
		goto error;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3118,10 +3118,10 @@ static int find_module_sections(struct module *mod, struct load_info *info)
 | 
				
			||||||
					     sizeof(*mod->ftrace_callsites),
 | 
										     sizeof(*mod->ftrace_callsites),
 | 
				
			||||||
					     &mod->num_ftrace_callsites);
 | 
										     &mod->num_ftrace_callsites);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef CONFIG_BPF_KPROBE_OVERRIDE
 | 
					#ifdef CONFIG_FUNCTION_ERROR_INJECTION
 | 
				
			||||||
	mod->kprobe_ei_funcs = section_objs(info, "_kprobe_error_inject_list",
 | 
						mod->ei_funcs = section_objs(info, "_error_injection_whitelist",
 | 
				
			||||||
					    sizeof(*mod->kprobe_ei_funcs),
 | 
										    sizeof(*mod->ei_funcs),
 | 
				
			||||||
					    &mod->num_kprobe_ei_funcs);
 | 
										    &mod->num_ei_funcs);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	mod->extable = section_objs(info, "__ex_table",
 | 
						mod->extable = section_objs(info, "__ex_table",
 | 
				
			||||||
				    sizeof(*mod->extable), &mod->num_exentries);
 | 
									    sizeof(*mod->extable), &mod->num_exentries);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -533,7 +533,7 @@ config FUNCTION_PROFILER
 | 
				
			||||||
config BPF_KPROBE_OVERRIDE
 | 
					config BPF_KPROBE_OVERRIDE
 | 
				
			||||||
	bool "Enable BPF programs to override a kprobed function"
 | 
						bool "Enable BPF programs to override a kprobed function"
 | 
				
			||||||
	depends on BPF_EVENTS
 | 
						depends on BPF_EVENTS
 | 
				
			||||||
	depends on HAVE_KPROBE_OVERRIDE
 | 
						depends on FUNCTION_ERROR_INJECTION
 | 
				
			||||||
	default n
 | 
						default n
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	 Allows BPF to override the execution of a probed function and
 | 
						 Allows BPF to override the execution of a probed function and
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
#include <linux/uaccess.h>
 | 
					#include <linux/uaccess.h>
 | 
				
			||||||
#include <linux/ctype.h>
 | 
					#include <linux/ctype.h>
 | 
				
			||||||
#include <linux/kprobes.h>
 | 
					#include <linux/kprobes.h>
 | 
				
			||||||
#include <asm/kprobes.h>
 | 
					#include <linux/error-injection.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "trace_probe.h"
 | 
					#include "trace_probe.h"
 | 
				
			||||||
#include "trace.h"
 | 
					#include "trace.h"
 | 
				
			||||||
| 
						 | 
					@ -84,7 +84,7 @@ EXPORT_SYMBOL_GPL(trace_call_bpf);
 | 
				
			||||||
BPF_CALL_2(bpf_override_return, struct pt_regs *, regs, unsigned long, rc)
 | 
					BPF_CALL_2(bpf_override_return, struct pt_regs *, regs, unsigned long, rc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	regs_set_return_value(regs, rc);
 | 
						regs_set_return_value(regs, rc);
 | 
				
			||||||
	arch_kprobe_override_function(regs);
 | 
						override_function_with_return(regs);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/uaccess.h>
 | 
					#include <linux/uaccess.h>
 | 
				
			||||||
#include <linux/rculist.h>
 | 
					#include <linux/rculist.h>
 | 
				
			||||||
 | 
					#include <linux/error-injection.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "trace_probe.h"
 | 
					#include "trace_probe.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,7 +108,7 @@ bool trace_kprobe_error_injectable(struct trace_event_call *call)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		addr = (unsigned long)tk->rp.kp.addr;
 | 
							addr = (unsigned long)tk->rp.kp.addr;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return within_kprobe_error_injection_list(addr);
 | 
						return within_error_injection_list(addr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int register_kprobe_event(struct trace_kprobe *tk);
 | 
					static int register_kprobe_event(struct trace_kprobe *tk);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1500,6 +1500,10 @@ config FAULT_INJECTION
 | 
				
			||||||
	  Provide fault-injection framework.
 | 
						  Provide fault-injection framework.
 | 
				
			||||||
	  For more details, see Documentation/fault-injection/.
 | 
						  For more details, see Documentation/fault-injection/.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config FUNCTION_ERROR_INJECTION
 | 
				
			||||||
 | 
						def_bool y
 | 
				
			||||||
 | 
						depends on HAVE_FUNCTION_ERROR_INJECTION && KPROBES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config FAILSLAB
 | 
					config FAILSLAB
 | 
				
			||||||
	bool "Fault-injection capability for kmalloc"
 | 
						bool "Fault-injection capability for kmalloc"
 | 
				
			||||||
	depends on FAULT_INJECTION
 | 
						depends on FAULT_INJECTION
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -149,6 +149,7 @@ obj-$(CONFIG_NETDEV_NOTIFIER_ERROR_INJECT) += netdev-notifier-error-inject.o
 | 
				
			||||||
obj-$(CONFIG_MEMORY_NOTIFIER_ERROR_INJECT) += memory-notifier-error-inject.o
 | 
					obj-$(CONFIG_MEMORY_NOTIFIER_ERROR_INJECT) += memory-notifier-error-inject.o
 | 
				
			||||||
obj-$(CONFIG_OF_RECONFIG_NOTIFIER_ERROR_INJECT) += \
 | 
					obj-$(CONFIG_OF_RECONFIG_NOTIFIER_ERROR_INJECT) += \
 | 
				
			||||||
	of-reconfig-notifier-error-inject.o
 | 
						of-reconfig-notifier-error-inject.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lib-$(CONFIG_GENERIC_BUG) += bug.o
 | 
					lib-$(CONFIG_GENERIC_BUG) += bug.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										213
									
								
								lib/error-inject.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								lib/error-inject.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,213 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					// error-inject.c: Function-level error injection table
 | 
				
			||||||
 | 
					#include <linux/error-injection.h>
 | 
				
			||||||
 | 
					#include <linux/debugfs.h>
 | 
				
			||||||
 | 
					#include <linux/kallsyms.h>
 | 
				
			||||||
 | 
					#include <linux/kprobes.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					#include <linux/mutex.h>
 | 
				
			||||||
 | 
					#include <linux/list.h>
 | 
				
			||||||
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Whitelist of symbols that can be overridden for error injection. */
 | 
				
			||||||
 | 
					static LIST_HEAD(error_injection_list);
 | 
				
			||||||
 | 
					static DEFINE_MUTEX(ei_mutex);
 | 
				
			||||||
 | 
					struct ei_entry {
 | 
				
			||||||
 | 
						struct list_head list;
 | 
				
			||||||
 | 
						unsigned long start_addr;
 | 
				
			||||||
 | 
						unsigned long end_addr;
 | 
				
			||||||
 | 
						void *priv;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool within_error_injection_list(unsigned long addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ei_entry *ent;
 | 
				
			||||||
 | 
						bool ret = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&ei_mutex);
 | 
				
			||||||
 | 
						list_for_each_entry(ent, &error_injection_list, list) {
 | 
				
			||||||
 | 
							if (addr >= ent->start_addr && addr < ent->end_addr) {
 | 
				
			||||||
 | 
								ret = true;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mutex_unlock(&ei_mutex);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Lookup and populate the error_injection_list.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * For safety reasons we only allow certain functions to be overridden with
 | 
				
			||||||
 | 
					 * bpf_error_injection, so we need to populate the list of the symbols that have
 | 
				
			||||||
 | 
					 * been marked as safe for overriding.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void populate_error_injection_list(unsigned long *start,
 | 
				
			||||||
 | 
										  unsigned long *end, void *priv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long *iter;
 | 
				
			||||||
 | 
						struct ei_entry *ent;
 | 
				
			||||||
 | 
						unsigned long entry, offset = 0, size = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&ei_mutex);
 | 
				
			||||||
 | 
						for (iter = start; iter < end; iter++) {
 | 
				
			||||||
 | 
							entry = arch_deref_entry_point((void *)*iter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!kernel_text_address(entry) ||
 | 
				
			||||||
 | 
							    !kallsyms_lookup_size_offset(entry, &size, &offset)) {
 | 
				
			||||||
 | 
								pr_err("Failed to find error inject entry at %p\n",
 | 
				
			||||||
 | 
									(void *)entry);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ent = kmalloc(sizeof(*ent), GFP_KERNEL);
 | 
				
			||||||
 | 
							if (!ent)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							ent->start_addr = entry;
 | 
				
			||||||
 | 
							ent->end_addr = entry + size;
 | 
				
			||||||
 | 
							ent->priv = priv;
 | 
				
			||||||
 | 
							INIT_LIST_HEAD(&ent->list);
 | 
				
			||||||
 | 
							list_add_tail(&ent->list, &error_injection_list);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mutex_unlock(&ei_mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Markers of the _error_inject_whitelist section */
 | 
				
			||||||
 | 
					extern unsigned long __start_error_injection_whitelist[];
 | 
				
			||||||
 | 
					extern unsigned long __stop_error_injection_whitelist[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void __init populate_kernel_ei_list(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						populate_error_injection_list(__start_error_injection_whitelist,
 | 
				
			||||||
 | 
									      __stop_error_injection_whitelist,
 | 
				
			||||||
 | 
									      NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_MODULES
 | 
				
			||||||
 | 
					static void module_load_ei_list(struct module *mod)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!mod->num_ei_funcs)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						populate_error_injection_list(mod->ei_funcs,
 | 
				
			||||||
 | 
									      mod->ei_funcs + mod->num_ei_funcs, mod);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void module_unload_ei_list(struct module *mod)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ei_entry *ent, *n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!mod->num_ei_funcs)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&ei_mutex);
 | 
				
			||||||
 | 
						list_for_each_entry_safe(ent, n, &error_injection_list, list) {
 | 
				
			||||||
 | 
							if (ent->priv == mod) {
 | 
				
			||||||
 | 
								list_del_init(&ent->list);
 | 
				
			||||||
 | 
								kfree(ent);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mutex_unlock(&ei_mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Module notifier call back, checking error injection table on the module */
 | 
				
			||||||
 | 
					static int ei_module_callback(struct notifier_block *nb,
 | 
				
			||||||
 | 
								      unsigned long val, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct module *mod = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (val == MODULE_STATE_COMING)
 | 
				
			||||||
 | 
							module_load_ei_list(mod);
 | 
				
			||||||
 | 
						else if (val == MODULE_STATE_GOING)
 | 
				
			||||||
 | 
							module_unload_ei_list(mod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NOTIFY_DONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct notifier_block ei_module_nb = {
 | 
				
			||||||
 | 
						.notifier_call = ei_module_callback,
 | 
				
			||||||
 | 
						.priority = 0
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __init int module_ei_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return register_module_notifier(&ei_module_nb);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#else /* !CONFIG_MODULES */
 | 
				
			||||||
 | 
					#define module_ei_init()	(0)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * error_injection/whitelist -- shows which functions can be overridden for
 | 
				
			||||||
 | 
					 * error injection.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void *ei_seq_start(struct seq_file *m, loff_t *pos)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mutex_lock(&ei_mutex);
 | 
				
			||||||
 | 
						return seq_list_start(&error_injection_list, *pos);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void ei_seq_stop(struct seq_file *m, void *v)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mutex_unlock(&ei_mutex);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void *ei_seq_next(struct seq_file *m, void *v, loff_t *pos)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return seq_list_next(v, &error_injection_list, pos);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ei_seq_show(struct seq_file *m, void *v)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ei_entry *ent = list_entry(v, struct ei_entry, list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						seq_printf(m, "%pf\n", (void *)ent->start_addr);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct seq_operations ei_seq_ops = {
 | 
				
			||||||
 | 
						.start = ei_seq_start,
 | 
				
			||||||
 | 
						.next  = ei_seq_next,
 | 
				
			||||||
 | 
						.stop  = ei_seq_stop,
 | 
				
			||||||
 | 
						.show  = ei_seq_show,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int ei_open(struct inode *inode, struct file *filp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return seq_open(filp, &ei_seq_ops);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct file_operations debugfs_ei_ops = {
 | 
				
			||||||
 | 
						.open           = ei_open,
 | 
				
			||||||
 | 
						.read           = seq_read,
 | 
				
			||||||
 | 
						.llseek         = seq_lseek,
 | 
				
			||||||
 | 
						.release        = seq_release,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __init ei_debugfs_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dentry *dir, *file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dir = debugfs_create_dir("error_injection", NULL);
 | 
				
			||||||
 | 
						if (!dir)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						file = debugfs_create_file("list", 0444, dir, NULL, &debugfs_ei_ops);
 | 
				
			||||||
 | 
						if (!file) {
 | 
				
			||||||
 | 
							debugfs_remove(dir);
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __init init_error_injection(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						populate_kernel_ei_list();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!module_ei_init())
 | 
				
			||||||
 | 
							ei_debugfs_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					late_initcall(init_error_injection);
 | 
				
			||||||
		Loading…
	
		Reference in a new issue