From: Li Huafei lihuafei1@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I5CJ7X
--------------------------------
Currently, the stack check of ppc32 does not consider exception frames, and the interrupted functions are omitted. Similar to ppc64, special processing of exception frames should be added.
Because the stack frame structure of ppc32 and ppc64 is the same, we can reuse the unwind_frame() code of ppc64. Then during the stack check, the exception frames need to check the NIP in addition to the PC, which is the function that is interrupted.
Fixes: e22fb775aaf6 ("livepatch/ppc32: Support livepatch without ftrace") Fixes: 1daef7b0cae5 ("livepatch/powerpc32: Add arch_klp_module_check_calltrace") Signed-off-by: Li Huafei lihuafei1@huawei.com Reviewed-by: Xu Kuohai xukuohai@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- arch/powerpc/include/asm/livepatch.h | 7 ++++ arch/powerpc/kernel/livepatch.c | 47 ++++++++++++++++++++++++ arch/powerpc/kernel/livepatch_32.c | 47 +++++++++++------------- arch/powerpc/kernel/livepatch_64.c | 55 +--------------------------- 4 files changed, 76 insertions(+), 80 deletions(-)
diff --git a/arch/powerpc/include/asm/livepatch.h b/arch/powerpc/include/asm/livepatch.h index bafbfaba190f..39dcfc3c28ce 100644 --- a/arch/powerpc/include/asm/livepatch.h +++ b/arch/powerpc/include/asm/livepatch.h @@ -118,6 +118,12 @@ struct arch_klp_data {
#endif /* CONFIG_PPC64 */
+struct stackframe { + unsigned long sp; + unsigned long pc; + unsigned long nip; +}; + #ifdef PPC64_ELF_ABI_v1 struct klp_func_node; void arch_klp_set_brk_func(struct klp_func_node *func_node, void *new_func); @@ -127,6 +133,7 @@ int arch_klp_add_breakpoint(struct arch_klp_data *arch_data, void *old_func); void arch_klp_remove_breakpoint(struct arch_klp_data *arch_data, void *old_func); long arch_klp_save_old_code(struct arch_klp_data *arch_data, void *old_func); int arch_klp_module_check_calltrace(void *data); +int klp_unwind_frame(struct task_struct *tsk, struct stackframe *frame);
#endif /* CONFIG_LIVEPATCH_FTRACE */
diff --git a/arch/powerpc/kernel/livepatch.c b/arch/powerpc/kernel/livepatch.c index b8afcc7b9939..d568e8c8b16b 100644 --- a/arch/powerpc/kernel/livepatch.c +++ b/arch/powerpc/kernel/livepatch.c @@ -21,6 +21,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> +#include <linux/ftrace.h> #include <linux/livepatch.h> #include <asm/probes.h> #include <asm/livepatch.h> @@ -65,3 +66,49 @@ int klp_brk_handler(struct pt_regs *regs)
return 1; } + +int klp_unwind_frame(struct task_struct *tsk, struct stackframe *frame) +{ + unsigned long *stack; +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + int ftrace_idx = 0; +#endif + + if (!validate_sp(frame->sp, tsk, STACK_FRAME_OVERHEAD)) + return -1; + + if (frame->nip != 0) + frame->nip = 0; + + stack = (unsigned long *)frame->sp; + + /* + * When switching to the exception stack, + * we save the NIP in pt_regs + * + * See if this is an exception frame. + * We look for the "regshere" marker in the current frame. + */ + if (validate_sp(frame->sp, tsk, STACK_INT_FRAME_SIZE) + && stack[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) { + struct pt_regs *regs = (struct pt_regs *) + (frame->sp + STACK_FRAME_OVERHEAD); + frame->nip = regs->nip; + pr_debug("--- interrupt: task = %d/%s, trap %lx at NIP=x%lx/%pS, LR=0x%lx/%pS\n", + tsk->pid, tsk->comm, regs->trap, + regs->nip, (void *)regs->nip, + regs->link, (void *)regs->link); + } + + frame->sp = stack[0]; + frame->pc = stack[STACK_FRAME_LR_SAVE]; +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + /* + * IMHO these tests do not belong in + * arch-dependent code, they are generic. + */ + frame->pc = ftrace_graph_ret_addr(tsk, &ftrace_idx, frame->pc, stack); +#endif + + return 0; +} diff --git a/arch/powerpc/kernel/livepatch_32.c b/arch/powerpc/kernel/livepatch_32.c index 603f1d61cc23..8f53386e7cf8 100644 --- a/arch/powerpc/kernel/livepatch_32.c +++ b/arch/powerpc/kernel/livepatch_32.c @@ -62,11 +62,6 @@ struct klp_func_list { int force; };
-struct stackframe { - unsigned long sp; - unsigned long pc; -}; - struct walk_stackframe_args { int enable; struct klp_func_list *check_funcs; @@ -226,20 +221,6 @@ static int klp_check_activeness_func(struct klp_patch *patch, int enable, return 0; }
-static int unwind_frame(struct task_struct *tsk, struct stackframe *frame) -{ - - unsigned long *stack; - - if (!validate_sp(frame->sp, tsk, STACK_FRAME_OVERHEAD)) - return -1; - - stack = (unsigned long *)frame->sp; - frame->sp = stack[0]; - frame->pc = stack[STACK_FRAME_LR_SAVE]; - return 0; -} - void notrace klp_walk_stackframe(struct stackframe *frame, int (*fn)(struct stackframe *, void *), struct task_struct *tsk, void *data) @@ -249,7 +230,7 @@ void notrace klp_walk_stackframe(struct stackframe *frame,
if (fn(frame, data)) break; - ret = unwind_frame(tsk, frame); + ret = klp_unwind_frame(tsk, frame); if (ret < 0) break; } @@ -273,9 +254,14 @@ static int klp_check_jump_func(struct stackframe *frame, void *data) struct walk_stackframe_args *args = data; struct klp_func_list *check_funcs = args->check_funcs;
- if (!check_func_list(check_funcs, &args->ret, frame->pc)) { + /* check the PC first */ + if (!check_func_list(check_funcs, &args->ret, frame->pc)) return args->ret; - } + + /* check NIP when the exception stack switching */ + if (frame->nip && !check_func_list(check_funcs, &args->ret, frame->nip)) + return args->ret; + return 0; }
@@ -334,6 +320,7 @@ static int do_check_calltrace(struct walk_stackframe_args *args,
frame.sp = (unsigned long)stack; frame.pc = stack[STACK_FRAME_LR_SAVE]; + frame.nip = 0; klp_walk_stackframe(&frame, fn, t, args); if (args->ret) { pr_info("PID: %d Comm: %.20s\n", t->pid, t->comm); @@ -372,11 +359,19 @@ static int check_module_calltrace(struct stackframe *frame, void *data) { struct walk_stackframe_args *args = data;
- if (within_module_core(frame->pc, args->mod)) { - pr_err("module %s is in use!\n", args->mod->name); - return (args->ret = -EBUSY); - } + /* check the PC first */ + if (within_module_core(frame->pc, args->mod)) + goto err_out; + + /* check NIP when the exception stack switching */ + if (frame->nip && within_module_core(frame->nip, args->mod)) + goto err_out; + return 0; + +err_out: + pr_err("module %s is in use!\n", args->mod->name); + return (args->ret = -EBUSY); }
int arch_klp_module_check_calltrace(void *data) diff --git a/arch/powerpc/kernel/livepatch_64.c b/arch/powerpc/kernel/livepatch_64.c index acd3658d37a3..cbb5e02cccff 100644 --- a/arch/powerpc/kernel/livepatch_64.c +++ b/arch/powerpc/kernel/livepatch_64.c @@ -67,12 +67,6 @@ struct klp_func_list { int force; };
-struct stackframe { - unsigned long sp; - unsigned long pc; - unsigned long nip; -}; - struct walk_stackframe_args { int enable; struct klp_func_list *check_funcs; @@ -246,53 +240,6 @@ static int klp_check_activeness_func(struct klp_patch *patch, int enable, return 0; }
-static int unwind_frame(struct task_struct *tsk, struct stackframe *frame) -{ - - unsigned long *stack; -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - int ftrace_idx = 0; -#endif - - if (!validate_sp(frame->sp, tsk, STACK_FRAME_OVERHEAD)) - return -1; - - if (frame->nip != 0) - frame->nip = 0; - - stack = (unsigned long *)frame->sp; - - /* - * When switching to the exception stack, - * we save the NIP in pt_regs - * - * See if this is an exception frame. - * We look for the "regshere" marker in the current frame. - */ - if (validate_sp(frame->sp, tsk, STACK_INT_FRAME_SIZE) - && stack[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) { - struct pt_regs *regs = (struct pt_regs *) - (frame->sp + STACK_FRAME_OVERHEAD); - frame->nip = regs->nip; - pr_debug("--- interrupt: task = %d/%s, trap %lx at NIP=x%lx/%pS, LR=0x%lx/%pS\n", - tsk->pid, tsk->comm, regs->trap, - regs->nip, (void *)regs->nip, - regs->link, (void *)regs->link); - } - - frame->sp = stack[0]; - frame->pc = stack[STACK_FRAME_LR_SAVE]; -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - /* - * IMHO these tests do not belong in - * arch-dependent code, they are generic. - */ - frame->pc = ftrace_graph_ret_addr(tsk, &ftrace_idx, frame->pc, stack); -#endif - - return 0; -} - static void notrace klp_walk_stackframe(struct stackframe *frame, int (*fn)(struct stackframe *, void *), struct task_struct *tsk, void *data) @@ -302,7 +249,7 @@ static void notrace klp_walk_stackframe(struct stackframe *frame,
if (fn(frame, data)) break; - ret = unwind_frame(tsk, frame); + ret = klp_unwind_frame(tsk, frame); if (ret < 0) break; }