From: Cheng Jian cj.chengjian@huawei.com
hulk inclusion category: bugfix bugzilla: 5507/31358 CVE: NA ---------------------------
When doing consistency stack checking, if we try to patch a function which has been patched already. We should check the new function(not the origin func) that is activeness currently, it's always the first entry in list func_node->func_stack.
Example : module : origin livepatch v1 livepatch v2 func : old func A -[enable]=> new func A' -[enable]=> new func A'' check : A A'
when we try to patch function A to new function A'' by livepatch v2, but the func A has already patched to function A' by livepatch v1, so function A' which provided in livepatch v1 is active in the stack instead of origin function A. Even if the long jump method is used, we jump to the new function A' using a call without LR, the origin function A will not appear in the stack. We must check the active function A' in consistency stack checking.
Signed-off-by: Cheng Jian cj.chengjian@huawei.com Reviewed-By: Xie XiuQi xiexiuqi@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/x86/kernel/livepatch.c | 99 ++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 36 deletions(-)
diff --git a/arch/x86/kernel/livepatch.c b/arch/x86/kernel/livepatch.c index 1fbfa62..c853ac5 100644 --- a/arch/x86/kernel/livepatch.c +++ b/arch/x86/kernel/livepatch.c @@ -70,6 +70,40 @@ void arch_klp_init_object_loaded(struct klp_patch *patch,
#ifdef CONFIG_LIVEPATCH_WO_FTRACE +#include <linux/slab.h> +#include <asm/nops.h> +#include <asm/sections.h> + +#define JMP_E9_INSN_SIZE 5 +union klp_code_union { + char code[JMP_E9_INSN_SIZE]; + struct { + unsigned char e9; + int offset; + } __packed; +}; + +struct klp_func_node { + struct list_head node; + struct list_head func_stack; + unsigned long old_addr; + unsigned char old_code[JMP_E9_INSN_SIZE]; +}; + +static LIST_HEAD(klp_func_list); + +static struct klp_func_node *klp_find_func_node(unsigned long old_addr) +{ + struct klp_func_node *func_node; + + list_for_each_entry(func_node, &klp_func_list, node) { + if (func_node->old_addr == old_addr) + return func_node; + } + + return NULL; +} + static inline int klp_compare_address(unsigned long stack_addr, unsigned long func_addr, unsigned long func_size, const char *func_name) @@ -81,11 +115,14 @@ static inline int klp_compare_address(unsigned long stack_addr, return 0; }
+static struct klp_func_node *klp_find_func_node(unsigned long old_addr); + static int klp_check_stack_func(struct klp_func *func, struct stack_trace *trace, int enable) { unsigned long func_addr, func_size, address; const char *func_name; + struct klp_func_node *func_node; int i;
for (i = 0; i < trace->nr_entries; i++) { @@ -94,9 +131,33 @@ static int klp_check_stack_func(struct klp_func *func, if (enable) { if (func->force) continue; - func_addr = func->old_addr; - func_size = func->old_size; + /* + * When enable, checking the currently active + * functions. + */ + func_node = klp_find_func_node(func->old_addr); + if (!func_node || + list_empty(&func_node->func_stack)) { + func_addr = func->old_addr; + func_size = func->old_size; + } else { + /* + * Previously patched function + * [the active one] + */ + struct klp_func *prev; + + prev = list_first_or_null_rcu( + &func_node->func_stack, + struct klp_func, stack_node); + func_addr = (unsigned long)prev->new_func; + func_size = prev->new_size; + } } else { + /* + * When disable, check for the function itself + * which to be unpatched. + */ func_addr = (unsigned long)func->new_func; func_size = func->new_size; } @@ -188,40 +249,6 @@ int klp_check_calltrace(struct klp_patch *patch, int enable) return ret; }
-#include <linux/slab.h> -#include <asm/nops.h> -#include <asm/sections.h> - -#define JMP_E9_INSN_SIZE 5 -union klp_code_union { - char code[JMP_E9_INSN_SIZE]; - struct { - unsigned char e9; - int offset; - } __packed; -}; - -struct klp_func_node { - struct list_head node; - struct list_head func_stack; - unsigned long old_addr; - unsigned char old_code[JMP_E9_INSN_SIZE]; -}; - -static LIST_HEAD(klp_func_list); - -static struct klp_func_node *klp_find_func_node(unsigned long old_addr) -{ - struct klp_func_node *func_node; - - list_for_each_entry(func_node, &klp_func_list, node) { - if (func_node->old_addr == old_addr) - return func_node; - } - - return NULL; -} - int arch_klp_init_func(struct klp_object *obj, struct klp_func *func) { return 0;