From patchwork Fri Aug 19 02:55:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Li Huafei X-Patchwork-Id: 12948253 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2FBB5C00140 for ; Fri, 19 Aug 2022 02:58:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=LgqqxivSUy9cx7C+NCZVgXo6ey6aFgfHn1JXl1dYfSo=; b=lS8Y0yBrLAVZZZ gVDqpNJtkOItlHlRyUIMrDGn+CQ8G/N9R8RGo0AzZ5eru3xUhESTu2Gb1rx2S0WcQqUIses+zelYT mZmV+VciXOV7jb3wmiJsnw6UnrpeFa2hmxgHyUGDjoJNM8drz51zJHK/mRMBrhDL2EGIhkgwTxequ DZt4LC2orO9fY21LmE5wehB00VnTdsrK3AHgpzz/DpjqeueVdkRPaYhTnpv+g11NA3YOYwmy7XGZI 1eWl8LF2SibpkuYxURwjUZahnYeoJAzLg8YvuEt9FsG5uLp6TOY7c19MzqBUBOZYbCtEWz8szS9w0 Wb7636IP0xrEC2uDPuvQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1oOsDX-00FELa-Jl; Fri, 19 Aug 2022 02:58:39 +0000 Received: from szxga08-in.huawei.com ([45.249.212.255]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1oOsDR-00FE8P-6z for linux-riscv@lists.infradead.org; Fri, 19 Aug 2022 02:58:37 +0000 Received: from dggemv704-chm.china.huawei.com (unknown [172.30.72.56]) by szxga08-in.huawei.com (SkyGuard) with ESMTP id 4M85vs3sXHz1N7HC; Fri, 19 Aug 2022 10:55:05 +0800 (CST) Received: from kwepemm600010.china.huawei.com (7.193.23.86) by dggemv704-chm.china.huawei.com (10.3.19.47) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.24; Fri, 19 Aug 2022 10:58:28 +0800 Received: from ubuntu1804.huawei.com (10.67.174.174) by kwepemm600010.china.huawei.com (7.193.23.86) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.24; Fri, 19 Aug 2022 10:58:27 +0800 From: Li Huafei To: , , CC: , , , , , , , , , , , , , Subject: [PATCH v2 2/2] riscv: kprobe: Allow coexistence of ftrace and kprobe Date: Fri, 19 Aug 2022 10:55:22 +0800 Message-ID: <20220819025522.154189-2-lihuafei1@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220819025522.154189-1-lihuafei1@huawei.com> References: <20220819025522.154189-1-lihuafei1@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.67.174.174] X-ClientProxiedBy: dggems704-chm.china.huawei.com (10.3.19.181) To kwepemm600010.china.huawei.com (7.193.23.86) X-CFilter-Loop: Reflected X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220818_195833_734932_A1862EF8 X-CRM114-Status: GOOD ( 19.76 ) X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org When using ftrace and kprobe at the same time, it was found that one might cause the other to be unavailable. This can be reproduced by the following steps. # cd /sys/kernel/debug/tracing/ # echo cmdline_proc_show > set_ftrace_filter # echo function > current_tracer # echo 'p cmdline_proc_show' > kprobe_events # echo 'p cmdline_proc_show+4' >> kprobe_events # ls events/kprobes/ enable p_cmdline_proc_show_0 filter p_cmdline_proc_show_4 # echo 1 > events/kprobes/p_cmdline_proc_show_4/enable # echo 1 > events/kprobes/p_cmdline_proc_show_0/enable [ 129.830108] 00000000ebed457d: expected (ffdb0097 4f0080e7) but got (00100073 4f0080e7) [ 129.835990] ------------[ ftrace bug ]------------ [ 129.839510] ftrace failed to modify [ 129.839536] [] cmdline_proc_show+0x0/0x46 [ 129.845831] actual: 23:3c:11:fe:73:00:10:00 [ 129.849299] Updating ftrace call site to call a different ftrace function [ 129.853998] ftrace record flags: e0000002 [ 129.856771] (2) R [ 129.856771] expected tramp: ffffffff80008e60 [ 129.861688] ------------[ cut here ]------------ [ 129.865092] WARNING: CPU: 0 PID: 14 at kernel/trace/ftrace.c:2085 ftrace_bug+0x21a/0x24c [ 129.870949] Modules linked in: [ 129.873301] CPU: 0 PID: 14 Comm: migration/0 Not tainted 5.18.0-rc3-00002-gd8bfcd250f58 #6 [ 129.879217] Hardware name: riscv-virtio,qemu (DT) [ 129.882666] Stopper: multi_cpu_stop+0x0/0x168 <- stop_machine_cpuslocked+0xfa/0x12e [ 129.888430] epc : ftrace_bug+0x21a/0x24c [ 129.891254] ra : ftrace_bug+0x21a/0x24c [ 129.894057] epc : ffffffff807c3bee ra : ffffffff807c3bee sp : ff20000000283c80 [ 129.899144] gp : ffffffff813a83b8 tp : ff60000080021600 t0 : ffffffff804155c0 [ 129.904257] t1 : 0720072007200720 t2 : 7420646574636570 s0 : ff20000000283cb0 [ 129.909402] s1 : ff6000007fe622a0 a0 : 0000000000000022 a1 : c0000000ffffefff [ 129.914472] a2 : 0000000000000001 a3 : 0000000000000000 a4 : 341adec112294700 [ 129.919487] a5 : 341adec112294700 a6 : 0000000000000730 a7 : 0000000000000030 [ 129.924595] s2 : ffffffff80258910 s3 : ffffffffffffffea s4 : 0000000000000000 [ 129.929776] s5 : 0000000000000a35 s6 : ffffffff80d667c8 s7 : ff6000007fe04000 [ 129.934892] s8 : 0000000000000004 s9 : 0000000000000002 s10: 0000000000000001 [ 129.939992] s11: 0000000000000003 t3 : ff6000007ff20f00 t4 : ff6000007ff20f00 [ 129.945134] t5 : ff6000007ff20000 t6 : ff200000002839c8 [ 129.948838] status: 0000000000000100 badaddr: 0000000000000000 cause: 0000000000000003 [ 129.954580] [] ftrace_replace_code+0xce/0xd0 [ 129.958771] [] ftrace_modify_all_code+0xb4/0x12c [ 129.964420] [] __ftrace_modify_code+0x12/0x1c [ 129.969163] [] multi_cpu_stop+0x9a/0x168 [ 129.973035] [] cpu_stopper_thread+0xb4/0x15e [ 129.977205] [] smpboot_thread_fn+0x106/0x1e4 [ 129.981277] [] kthread+0xee/0x108 [ 129.984763] [] ret_from_exception+0x0/0xc [ 129.988809] ---[ end trace 0000000000000000 ]--- # cat trace # tracer: function # # WARNING: FUNCTION TRACING IS CORRUPTED # MAY BE MISSING FUNCTION EVENTS # entries-in-buffer/entries-written: 0/0 #P:4 # # _-----=> irqs-off/BH-disabled # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / _-=> migrate-disable # |||| / delay # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | As you can see, the ftrace functionality is broken. This is because DYNAMIC_FTRACE uses 4 instructions to make the jump from the function entry to the ftrace trampoline. After 'echo function > current_tracer': : 0xffffffff80258910 <+0>: sd ra,-8(sp) 0xffffffff80258914 <+4>: auipc ra,0xffdb0 0xffffffff80258918 <+8>: jalr 1264(ra) # 0xffffffff80008e04 0xffffffff8025891c <+12>: ld ra,-8(sp) After 'echo 1 > events/kprobes/p_cmdline_proc_show_4/enable': : 0xffffffff80258910 <+0>: sd ra,-8(sp) 0xffffffff80258914 <+4>: ebreak 0xffffffff80258918 <+8>: jalr 1264(ra) 0xffffffff8025891c <+12>: ld ra,-8(sp) This time the second instruction is replaced with a breakpoint instruction and the ftrace-related instructions are broken. Then, when enabling 'p_cmdline_proc_show_0', a regs ftrace handler is registered based on KPROBES_ON_FTRACE, ftrace_modify_call() changes 'ftrace_caller' to 'ftrace_regs_caller', and checks for ftrace-related instructions is modified or not. Here it has been modified, causing ftrace to report a warning and disable itself. In turn, the breakpoints inserted by kprobe may be overwritten by ftrace. The problem is that we think that only the first instruction address at the function entry is needed for ftrace, but it is actually the first 4. As Masami mentioned in [1], we can treat the first 4 instructions as a 16-byte virtual instruction, and func+4, func+8 and func+12 will be changed to func+0. This way, ftrace and kprobe do not bother each other and can coexist. After this patch: # cd /sys/kernel/debug/tracing/ # echo cmdline_proc_show > set_ftrace_filter # echo function > current_tracer # echo 'p cmdline_proc_show' > kprobe_events # echo 'p cmdline_proc_show+4' >> kprobe_events # echo 'p cmdline_proc_show+8' >> kprobe_events # echo 'p cmdline_proc_show+12' >> kprobe_events # echo 'p cmdline_proc_show+16' >> kprobe_events # ls events/kprobes/ enable p_cmdline_proc_show_12 p_cmdline_proc_show_8 filter p_cmdline_proc_show_16 p_cmdline_proc_show_0 p_cmdline_proc_show_4 # echo 1 > events/kprobes/enable # cat ../kprobes/list ffffffff8025cba4 k cmdline_proc_show+0x0 [FTRACE] ffffffff8025cba4 k cmdline_proc_show+0x0 [FTRACE] ffffffff8025cba4 k cmdline_proc_show+0x0 [FTRACE] ffffffff8025cba4 k cmdline_proc_show+0x0 [FTRACE] ffffffff8025cbb4 k cmdline_proc_show+0x10 # cat /proc/cmdline nokaslr rootwait root=/dev/vda rw # cat trace # tracer: function # # entries-in-buffer/entries-written: 6/6 #P:4 # # _-----=> irqs-off/BH-disabled # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / _-=> migrate-disable # |||| / delay # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | cat-144 [000] ...2. 179.164908: p_cmdline_proc_show_12: (cmdline_proc_show+0x0/0x46) cat-144 [000] ...2. 179.165617: p_cmdline_proc_show_8: (cmdline_proc_show+0x0/0x46) cat-144 [000] ...2. 179.165653: p_cmdline_proc_show_4: (cmdline_proc_show+0x0/0x46) cat-144 [000] ...2. 179.165655: p_cmdline_proc_show_0: (cmdline_proc_show+0x0/0x46) cat-144 [000] ...2. 179.165837: cmdline_proc_show <-seq_read_iter cat-144 [000] d.... 179.166099: p_cmdline_proc_show_16: (cmdline_proc_show+0x10/0x46) [1] https://patchwork.kernel.org/project/linux-arm-kernel/patch/20191218140622.57bbaca5@xhacker.debian/ Fixes: c22b0bcb1dd0 ("riscv: Add kprobes supported") Suggested-by: Guo Ren Signed-off-by: Li Huafei --- v1: https://lore.kernel.org/lkml/20220426015751.88582-2-lihuafei1@huawei.com/ Changlog in v1 -> v2: - Allows probing func+offs(<16) instead of returning -EILSEQ, which does not change the arch_adjust_kprobe_addr() interface convention. - ftrace_location(addr) is unnecessary, consistent with powerpc and x86. arch/riscv/include/asm/ftrace.h | 26 ++++++++++++++++++++++++++ arch/riscv/kernel/ftrace.c | 26 -------------------------- arch/riscv/kernel/probes/kprobes.c | 20 ++++++++++++++++++++ 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h index 04dad3380041..c59e4a63f1c1 100644 --- a/arch/riscv/include/asm/ftrace.h +++ b/arch/riscv/include/asm/ftrace.h @@ -77,6 +77,32 @@ do { \ */ #define MCOUNT_INSN_SIZE 8 +/* + * Put 16 bytes at the front of the function within the patchable function + * entry nops' area. + * + * 0: REG_S ra, -SZREG(sp) + * 1: auipc ra, 0x? + * 2: jalr -?(ra) + * 3: REG_L ra, -SZREG(sp) + * + * So the opcodes is: + * 0: 0xfe113c23 (sd)/0xfe112e23 (sw) + * 1: 0x???????? -> auipc + * 2: 0x???????? -> jalr + * 3: 0xff813083 (ld)/0xffc12083 (lw) + */ +#if __riscv_xlen == 64 +#define INSN0 0xfe113c23 +#define INSN3 0xff813083 +#elif __riscv_xlen == 32 +#define INSN0 0xfe112e23 +#define INSN3 0xffc12083 +#endif + +#define FUNC_ENTRY_SIZE 16 +#define FUNC_ENTRY_JMP 4 + #ifndef __ASSEMBLY__ struct dyn_ftrace; int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec); diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c index 552088e9acc4..9d951aab662a 100644 --- a/arch/riscv/kernel/ftrace.c +++ b/arch/riscv/kernel/ftrace.c @@ -70,32 +70,6 @@ static int __ftrace_modify_call(unsigned long hook_pos, unsigned long target, return 0; } -/* - * Put 16 bytes at the front of the function within the patchable function - * entry nops' area. - * - * 0: REG_S ra, -SZREG(sp) - * 1: auipc ra, 0x? - * 2: jalr -?(ra) - * 3: REG_L ra, -SZREG(sp) - * - * So the opcodes is: - * 0: 0xfe113c23 (sd)/0xfe112e23 (sw) - * 1: 0x???????? -> auipc - * 2: 0x???????? -> jalr - * 3: 0xff813083 (ld)/0xffc12083 (lw) - */ -#if __riscv_xlen == 64 -#define INSN0 0xfe113c23 -#define INSN3 0xff813083 -#elif __riscv_xlen == 32 -#define INSN0 0xfe112e23 -#define INSN3 0xffc12083 -#endif - -#define FUNC_ENTRY_SIZE 16 -#define FUNC_ENTRY_JMP 4 - int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) { unsigned int call[4] = {INSN0, 0, 0, INSN3}; diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c index e6e950b7cf32..ef7aabec9681 100644 --- a/arch/riscv/kernel/probes/kprobes.c +++ b/arch/riscv/kernel/probes/kprobes.c @@ -21,6 +21,26 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); static void __kprobes post_kprobe_handler(struct kprobe *, struct kprobe_ctlblk *, struct pt_regs *); +#ifdef CONFIG_DYNAMIC_FTRACE +kprobe_opcode_t *arch_adjust_kprobe_addr(unsigned long addr, unsigned long offset, + bool *on_func_entry) +{ + /* + * The first 4 instructions at the beginning of the ftraced function + * are used as a jump to ftrace trampoline, which we can think of as + * a 16-byte virtual instruction. func+4, func+8 and func+12 will be + * changed to func+0. This allows the function entry to be probed with + * the help of KPROBES_ON_FTRACE and does not break ftrace + * functionality. + */ + if (offset < FUNC_ENTRY_SIZE) + offset = 0; + + *on_func_entry = !offset; + return (kprobe_opcode_t *)(addr + offset); +} +#endif + static void __kprobes arch_prepare_ss_slot(struct kprobe *p) { unsigned long offset = GET_INSN_LENGTH(p->opcode);