Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751156AbdLZHsw (ORCPT ); Tue, 26 Dec 2017 02:48:52 -0500 Received: from mail.kernel.org ([198.145.29.99]:35448 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750741AbdLZHsu (ORCPT ); Tue, 26 Dec 2017 02:48:50 -0500 DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 9A437218C9 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=kernel.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=mhiramat@kernel.org From: Masami Hiramatsu To: Alexei Starovoitov , Josef Bacik Cc: rostedt@goodmis.org, mingo@redhat.com, davem@davemloft.net, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, ast@kernel.org, kernel-team@fb.com, daniel@iogearbox.net, linux-btrfs@vger.kernel.org, darrick.wong@oracle.com, mhiramat@kernel.org, Josef Bacik , Akinobu Mita Subject: [RFC PATCH bpf-next v2 4/4] error-injection: Support fault injection framework Date: Tue, 26 Dec 2017 16:48:25 +0900 Message-Id: <151427450538.32561.2776225740675148782.stgit@devbox> X-Mailer: git-send-email 2.13.6 In-Reply-To: <151427438796.32561.4235654585430455286.stgit@devbox> References: <151427438796.32561.4235654585430455286.stgit@devbox> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 6459 Lines: 237 Support in-kernel fault-injection framework via debugfs. This allows you to inject a conditional error to specified function using debugfs interfaces. Signed-off-by: Masami Hiramatsu --- Documentation/fault-injection/fault-injection.txt | 5 + kernel/Makefile | 1 kernel/fail_function.c | 169 +++++++++++++++++++++ lib/Kconfig.debug | 10 + 4 files changed, 185 insertions(+) create mode 100644 kernel/fail_function.c diff --git a/Documentation/fault-injection/fault-injection.txt b/Documentation/fault-injection/fault-injection.txt index 918972babcd8..6243a588dd71 100644 --- a/Documentation/fault-injection/fault-injection.txt +++ b/Documentation/fault-injection/fault-injection.txt @@ -30,6 +30,11 @@ o fail_mmc_request injects MMC data errors on devices permitted by setting debugfs entries under /sys/kernel/debug/mmc0/fail_mmc_request +o fail_function + + injects error return on specific functions by setting debugfs entries + under /sys/kernel/debug/fail_function. No boot option supported. + Configure fault-injection capabilities behavior ----------------------------------------------- diff --git a/kernel/Makefile b/kernel/Makefile index 172d151d429c..f85ae5dfa474 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_AUDIT_TREE) += audit_tree.o obj-$(CONFIG_GCOV_KERNEL) += gcov/ obj-$(CONFIG_KCOV) += kcov.o obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_FAIL_FUNCTION) += fail_function.o obj-$(CONFIG_KGDB) += debug/ obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o obj-$(CONFIG_LOCKUP_DETECTOR) += watchdog.o diff --git a/kernel/fail_function.c b/kernel/fail_function.c new file mode 100644 index 000000000000..203d3582487a --- /dev/null +++ b/kernel/fail_function.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fail_function.c: Function-based error injection + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int fei_kprobe_handler(struct kprobe *kp, struct pt_regs *regs); + +static DEFINE_MUTEX(fei_lock); +static struct { + struct kprobe kp; + unsigned long retval; + struct fault_attr attr; +} fei_attr = { + .kp = { .pre_handler = fei_kprobe_handler, }, + .retval = ~0UL, /* This indicates -1 in long/int return value */ + .attr = FAULT_ATTR_INITIALIZER, +}; + +static int fei_kprobe_handler(struct kprobe *kp, struct pt_regs *regs) +{ + if (should_fail(&fei_attr.attr, 1)) { + regs_set_return_value(regs, fei_attr.retval); + override_function_with_return(regs); + /* Kprobe specific fixup */ + reset_current_kprobe(); + preempt_enable_no_resched(); + return 1; + } + + return 0; +} +NOKPROBE_SYMBOL(fei_kprobe_handler) + +static void *fei_seq_start(struct seq_file *m, loff_t *pos) +{ + mutex_lock(&fei_lock); + return *pos == 0 ? (void *)1 : NULL; +} + +static void fei_seq_stop(struct seq_file *m, void *v) +{ + mutex_unlock(&fei_lock); +} + +static void *fei_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + return NULL; +} + +static int fei_seq_show(struct seq_file *m, void *v) +{ + if (fei_attr.kp.addr) + seq_printf(m, "%pf\n", fei_attr.kp.addr); + else + seq_puts(m, "# not specified\n"); + return 0; +} + +static const struct seq_operations fei_seq_ops = { + .start = fei_seq_start, + .next = fei_seq_next, + .stop = fei_seq_stop, + .show = fei_seq_show, +}; + +static int fei_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &fei_seq_ops); +} + +static ssize_t fei_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + unsigned long addr; + char *buf, *sym; + int ret; + + /* cut off if it is too long */ + if (count > KSYM_NAME_LEN) + count = KSYM_NAME_LEN; + buf = kmalloc(sizeof(char) * (count + 1), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, buffer, count)) { + ret = -EFAULT; + goto out; + } + buf[count] = '\0'; + sym = strstrip(buf); + + if (strlen(sym) == 0 || sym[0] == '0') { + if (fei_attr.kp.addr) { + unregister_kprobe(&fei_attr.kp); + fei_attr.kp.addr = NULL; + } + ret = count; + goto out; + } + + addr = kallsyms_lookup_name(sym); + if (!addr) { + ret = -EINVAL; + goto out; + } + if (!within_error_injection_list(addr)) { + ret = -ERANGE; + goto out; + } + + if (fei_attr.kp.addr) { + unregister_kprobe(&fei_attr.kp); + fei_attr.kp.addr = NULL; + } + fei_attr.kp.addr = (void *)addr; + ret = register_kprobe(&fei_attr.kp); + if (ret < 0) + fei_attr.kp.addr = NULL; + else + ret = count; +out: + kfree(buf); + return ret; +} + +static const struct file_operations fei_ops = { + .open = fei_open, + .read = seq_read, + .write = fei_write, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init fei_debugfs_init(void) +{ + struct dentry *dir; + + dir = fault_create_debugfs_attr("fail_function", NULL, + &fei_attr.attr); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + // injectable attribute is just a symlink of error_inject/list + if (!debugfs_create_symlink("injectable", dir, + "../error_injection/list")) + goto error; + + if (!debugfs_create_file("inject", 0600, dir, NULL, &fei_ops)) + goto error; + + if (!debugfs_create_ulong("retval", 0600, dir, &fei_attr.retval)) + goto error; + + return 0; +error: + debugfs_remove_recursive(dir); + return -ENOMEM; +} + +late_initcall(fei_debugfs_init); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index fe88ac0f003c..15c6609a1bb8 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1551,6 +1551,16 @@ config FAIL_FUTEX help Provide fault-injection capability for futexes. +config FAIL_FUNCTION + bool "Fault-injection capability for functions" + depends on FAULT_INJECTION_DEBUG_FS && FUNCTION_ERROR_INJECTION + help + Provide function-based fault-injection capability. + This will allow you to override a specific function with a return + with given return value. As a result, function caller will see + an error value and have to handle it. This is useful to test the + error handling in various subsystems. + config FAULT_INJECTION_DEBUG_FS bool "Debugfs entries for fault-injection capabilities" depends on FAULT_INJECTION && SYSFS && DEBUG_FS