hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8MGE6
--------------------------------
Implement the arch_klp_{check, add, remove}_breakpoint interface to support breakpoint exception optimization.
Signed-off-by: Li Huafei lihuafei1@huawei.com Signed-off-by: Zheng Yejian zhengyejian1@huawei.com --- arch/x86/include/asm/livepatch.h | 9 +++++ arch/x86/kernel/livepatch.c | 68 ++++++++++++++++++++++++++++++++ arch/x86/kernel/traps.c | 10 +++++ 3 files changed, 87 insertions(+)
diff --git a/arch/x86/include/asm/livepatch.h b/arch/x86/include/asm/livepatch.h index 359c06ad1cc2..cf1d1523462e 100644 --- a/arch/x86/include/asm/livepatch.h +++ b/arch/x86/include/asm/livepatch.h @@ -13,6 +13,11 @@ #define JMP_E9_INSN_SIZE 5 struct arch_klp_data { unsigned char old_insns[JMP_E9_INSN_SIZE]; + /* + * Saved opcode at the entry of the old func (which maybe replaced + * with breakpoint). + */ + unsigned char saved_opcode; };
#define KLP_MAX_REPLACE_SIZE sizeof_field(struct arch_klp_data, old_insns) @@ -27,6 +32,10 @@ bool arch_check_jump_insn(unsigned long func_addr); int arch_klp_check_calltrace(bool (*check_func)(void *, int *, unsigned long), void *data); void arch_klp_code_modify_prepare(void); void arch_klp_code_modify_post_process(void); +int arch_klp_check_breakpoint(struct arch_klp_data *arch_data, void *old_func); +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); +int klp_int3_handler(struct pt_regs *regs);
#endif /* CONFIG_LIVEPATCH_WO_FTRACE */
diff --git a/arch/x86/kernel/livepatch.c b/arch/x86/kernel/livepatch.c index a877ffdf96ac..57f1034a88c8 100644 --- a/arch/x86/kernel/livepatch.c +++ b/arch/x86/kernel/livepatch.c @@ -202,6 +202,74 @@ int arch_klp_check_calltrace(bool (*check_func)(void *, int *, unsigned long), v return do_check_calltrace(check_func, data); }
+int arch_klp_check_breakpoint(struct arch_klp_data *arch_data, void *old_func) +{ + int ret; + unsigned char opcode; + + ret = copy_from_kernel_nofault(&opcode, old_func, INT3_INSN_SIZE); + if (ret) + return ret; + + /* Another subsystem puts a breakpoint, reject patching at this time */ + if (opcode == INT3_INSN_OPCODE) + return -EBUSY; + + return 0; +} + +int arch_klp_add_breakpoint(struct arch_klp_data *arch_data, void *old_func) +{ + unsigned char int3 = INT3_INSN_OPCODE; + int ret; + + ret = copy_from_kernel_nofault(&arch_data->saved_opcode, old_func, + INT3_INSN_SIZE); + if (ret) + return ret; + + text_poke(old_func, &int3, INT3_INSN_SIZE); + /* arch_klp_code_modify_post_process() will do text_poke_sync() */ + + return 0; +} + +void arch_klp_remove_breakpoint(struct arch_klp_data *arch_data, void *old_func) +{ + unsigned char opcode; + int ret; + + ret = copy_from_kernel_nofault(&opcode, old_func, INT3_INSN_SIZE); + if (ret) { + pr_warn("%s: failed to read opcode, ret=%d\n", __func__, ret); + return; + } + + /* instruction have been recovered at arch_klp_unpatch_func() */ + if (opcode != INT3_INSN_OPCODE) + return; + + text_poke(old_func, &arch_data->saved_opcode, INT3_INSN_SIZE); + /* arch_klp_code_modify_post_process() will do text_poke_sync() */ +} + +int klp_int3_handler(struct pt_regs *regs) +{ + unsigned long addr = regs->ip - INT3_INSN_SIZE; + void *brk_func; + + if (user_mode(regs)) + return 0; + + brk_func = klp_get_brk_func((void *)addr); + if (!brk_func) + return 0; + + int3_emulate_jmp(regs, (unsigned long)brk_func); + return 1; +} +NOKPROBE_SYMBOL(klp_int3_handler); + void arch_klp_code_modify_prepare(void) __acquires(&text_mutex) { diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index c876f1d36a81..4bbd1f52d7d6 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -66,6 +66,10 @@ #include <asm/tdx.h> #include <asm/cfi.h>
+#ifdef CONFIG_LIVEPATCH_WO_FTRACE +#include <asm/livepatch.h> +#endif + #ifdef CONFIG_X86_64 #include <asm/x86_init.h> #else @@ -75,6 +79,7 @@
#include <asm/proto.h>
+ DECLARE_BITMAP(system_vectors, NR_VECTORS);
__always_inline int is_valid_bugaddr(unsigned long addr) @@ -714,6 +719,11 @@ static bool do_int3(struct pt_regs *regs) if (kprobe_int3_handler(regs)) return true; #endif + +#ifdef CONFIG_LIVEPATCH_WO_FTRACE + if (klp_int3_handler(regs)) + return true; +#endif res = notify_die(DIE_INT3, "int3", regs, 0, X86_TRAP_BP, SIGTRAP);
return res == NOTIFY_STOP;