From 9ba142f472c1e4ec514f3bad1134b6b6ad8f6c13 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Thu, 3 Feb 2022 00:49:41 +0100 Subject: s390/test_unwind: fix and extend kprobes test Running kprobe test on a kernel built with clang 14 didn't actually trigger pgm_pre_handler() and no unwinder code was called. Even though do_report_trap() is a global symbol, clang inlined it in several local functions including illegal_op() handler, so that kprobbing a global symbol didn't have a desired effect. To achieve the same test result (unwinding from a program check handler) introduce a local function and probe an instruction in the middle, so that kprobe doesn't take KPROBE_ON_FTRACE path. While at it, add another test for KPROBE_ON_FTRACE. Signed-off-by: Vasily Gorbik --- arch/s390/lib/test_unwind.c | 83 +++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 33 deletions(-) (limited to 'arch/s390/lib') diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index 2224694a0888..b209014ce426 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -129,8 +129,9 @@ static struct unwindme *unwindme; #define UWM_CALLER 0x8 /* Unwind starting from caller. */ #define UWM_SWITCH_STACK 0x10 /* Use call_on_stack. */ #define UWM_IRQ 0x20 /* Unwind from irq context. */ -#define UWM_PGM 0x40 /* Unwind from program check handler. */ -#define UWM_FTRACE 0x80 /* Unwind from ftrace handler. */ +#define UWM_PGM 0x40 /* Unwind from program check handler */ +#define UWM_KPROBE_ON_FTRACE 0x80 /* Unwind from kprobe handler called via ftrace. */ +#define UWM_FTRACE 0x100 /* Unwind from ftrace handler. */ static __always_inline unsigned long get_psw_addr(void) { @@ -142,7 +143,7 @@ static __always_inline unsigned long get_psw_addr(void) return psw_addr; } -static int pgm_pre_handler(struct kprobe *p, struct pt_regs *regs) +static int kprobe_pre_handler(struct kprobe *p, struct pt_regs *regs) { struct unwindme *u = unwindme; @@ -151,6 +152,46 @@ static int pgm_pre_handler(struct kprobe *p, struct pt_regs *regs) return 0; } +extern const char test_unwind_kprobed_insn[]; + +static noinline void test_unwind_kprobed_func(void) +{ + asm volatile( + " nopr %%r7\n" + "test_unwind_kprobed_insn:\n" + " nopr %%r7\n" + :); +} + +static int test_unwind_kprobe(struct unwindme *u) +{ + struct kprobe kp; + int ret; + + if (!IS_ENABLED(CONFIG_KPROBES)) + kunit_skip(current_test, "requires CONFIG_KPROBES"); + if (!IS_ENABLED(CONFIG_KPROBES_ON_FTRACE) && u->flags & UWM_KPROBE_ON_FTRACE) + kunit_skip(current_test, "requires CONFIG_KPROBES_ON_FTRACE"); + + u->ret = -1; /* make sure kprobe is called */ + unwindme = u; + memset(&kp, 0, sizeof(kp)); + kp.pre_handler = kprobe_pre_handler; + kp.addr = u->flags & UWM_KPROBE_ON_FTRACE ? + (kprobe_opcode_t *)test_unwind_kprobed_func : + (kprobe_opcode_t *)test_unwind_kprobed_insn; + ret = register_kprobe(&kp); + if (ret < 0) { + kunit_err(current_test, "register_kprobe failed %d\n", ret); + return -EINVAL; + } + + test_unwind_kprobed_func(); + unregister_kprobe(&kp); + unwindme = NULL; + return u->ret; +} + static void notrace __used test_unwind_ftrace_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *fops, @@ -212,36 +253,8 @@ static noinline int unwindme_func4(struct unwindme *u) wait_event(u->task_wq, kthread_should_park()); kthread_parkme(); return 0; - } else if (u->flags & UWM_PGM) { - struct kprobe kp; - int ret; - - if (!IS_ENABLED(CONFIG_KPROBES)) - kunit_skip(current_test, "requires CONFIG_KPROBES"); - - unwindme = u; - memset(&kp, 0, sizeof(kp)); - kp.symbol_name = "do_report_trap"; - kp.pre_handler = pgm_pre_handler; - ret = register_kprobe(&kp); - if (ret < 0) { - kunit_err(current_test, "register_kprobe failed %d\n", ret); - return -EINVAL; - } - - /* - * Trigger operation exception; use insn notation to bypass - * llvm's integrated assembler sanity checks. - */ - asm volatile( - " .insn e,0x0000\n" /* illegal opcode */ - "0: nopr %%r7\n" - EX_TABLE(0b, 0b) - :); - - unregister_kprobe(&kp); - unwindme = NULL; - return u->ret; + } else if (u->flags & (UWM_PGM | UWM_KPROBE_ON_FTRACE)) { + return test_unwind_kprobe(u); } else if (u->flags & UWM_FTRACE) { return test_unwind_ftrace(u); } else { @@ -376,6 +389,10 @@ static const struct test_params param_list[] = { TEST_WITH_FLAGS(UWM_PGM | UWM_SP), TEST_WITH_FLAGS(UWM_PGM | UWM_REGS), TEST_WITH_FLAGS(UWM_PGM | UWM_SP | UWM_REGS), + TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE), + TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE | UWM_SP), + TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE | UWM_REGS), + TEST_WITH_FLAGS(UWM_KPROBE_ON_FTRACE | UWM_SP | UWM_REGS), TEST_WITH_FLAGS(UWM_FTRACE), TEST_WITH_FLAGS(UWM_FTRACE | UWM_SP), TEST_WITH_FLAGS(UWM_FTRACE | UWM_REGS), -- cgit v1.2.3