From 2fa9f353c1180225bb112778623f44efac6f331e Mon Sep 17 00:00:00 2001 From: Li Bin Date: Sat, 29 May 2021 14:42:50 +0800 Subject: [PATCH] livepatch/arm: Support livepatch without ftrace euler inclusion category: feature bugzilla: 51923 CVE: N/A ---------------------------------------- support livepatch without ftrace for ARM supported now: livepatch relocation when init_patch after load_module; instruction patched when enable; activeness function check; enforcing the patch stacking principle; unsupport now:(willn't fix it feature) long jump (both livepatch relocation and insn patched) module plts request by livepatch-relocation Because CONFIG_ARM_MODULE_PLTS will be not set in ARM, so we needn't long jump and livepatch plts. Signed-off-by: Cheng Jian Signed-off-by: Li Bin Tested-by: Cheng Jian Tested-by: Wang Feng Tested-by: Lin DingYu Reviewed-by: Xie XiuQi Signed-off-by: zhangyi (F) Signed-off-by: Dong Kai Signed-off-by: Ye Weihua Reviewed-by: Yang Jihong Signed-off-by: Zheng Zengkai --- arch/arm/Kconfig | 3 + arch/arm/include/asm/livepatch.h | 39 ++++++ arch/arm/include/asm/thread_info.h | 2 + arch/arm/kernel/Makefile | 1 + arch/arm/kernel/livepatch.c | 208 +++++++++++++++++++++++++++++ 5 files changed, 253 insertions(+) create mode 100644 arch/arm/include/asm/livepatch.h create mode 100644 arch/arm/kernel/livepatch.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 44948e016f32..257dbc0ce341 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -124,6 +124,7 @@ config ARM select RTC_LIB select SET_FS select SYS_SUPPORTS_APM_EMULATION + select HAVE_LIVEPATCH_WO_FTRACE # Above selects are sorted alphabetically; please add new ones # according to that. Thanks. help @@ -2077,3 +2078,5 @@ source "arch/arm/crypto/Kconfig" endif source "arch/arm/Kconfig.assembler" + +source "kernel/livepatch/Kconfig" diff --git a/arch/arm/include/asm/livepatch.h b/arch/arm/include/asm/livepatch.h new file mode 100644 index 000000000000..216078d8c2b0 --- /dev/null +++ b/arch/arm/include/asm/livepatch.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * livepatch.h - arm-specific Kernel Live Patching Core + * + * Copyright (C) 2018 Huawei Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _ASM_ARM_LIVEPATCH_H +#define _ASM_ARM_LIVEPATCH_H + +#include + +struct klp_patch; +struct klp_func; + +/* kernel livepatch instruction barrier */ +#define klp_smp_isb() isb() + +int arch_klp_patch_func(struct klp_func *func); +void arch_klp_unpatch_func(struct klp_func *func); + +#ifdef CONFIG_LIVEPATCH_STOP_MACHINE_CONSISTENCY +int klp_check_calltrace(struct klp_patch *patch, int enable); +#endif + +#endif /* _ASM_ARM_LIVEPATCH_H */ diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 56fae7861fd3..2e4733a2e737 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -143,6 +143,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp *, #define TIF_SYSCALL_AUDIT 5 /* syscall auditing active */ #define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ #define TIF_SECCOMP 7 /* seccomp syscall filtering active */ +#define TIF_PATCH_PENDING 8 /* pending live patching update */ #define TIF_USING_IWMMXT 17 #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ @@ -157,6 +158,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp *, #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_SECCOMP (1 << TIF_SECCOMP) #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) +#define _TIF_PATCH_PENDING (1 << TIF_PATCH_PENDING) /* Checks for any syscall work in entry-common.S */ #define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index c38c8b019d24..20900568c568 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_ARM_ARCH_TIMER) += arch_timer.o obj-$(CONFIG_FUNCTION_TRACER) += entry-ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o patch.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o patch.o +obj-$(CONFIG_LIVEPATCH) += livepatch.o insn.o patch.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o # Main staffs in KPROBES are in arch/arm/probes/ . diff --git a/arch/arm/kernel/livepatch.c b/arch/arm/kernel/livepatch.c new file mode 100644 index 000000000000..5f87d98a9e99 --- /dev/null +++ b/arch/arm/kernel/livepatch.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * livepatch.c - arm-specific Kernel Live Patching Core + * + * Copyright (C) 2018 Huawei Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_LIVEPATCH_STOP_MACHINE_CONSISTENCY +struct walk_stackframe_args { + struct klp_patch *patch; + int enable; + int ret; +}; + +static inline int klp_compare_address(unsigned long pc, unsigned long func_addr, + unsigned long func_size, const char *func_name) +{ + if (pc >= func_addr && pc < func_addr + func_size) { + pr_err("func %s is in use!\n", func_name); + return -EBUSY; + } + return 0; +} + +static int klp_check_activeness_func(struct stackframe *frame, void *data) +{ + struct walk_stackframe_args *args = data; + struct klp_patch *patch = args->patch; + struct klp_object *obj; + struct klp_func *func; + unsigned long func_addr, func_size; + const char *func_name; + + if (args->ret) + return args->ret; + + for (obj = patch->objs; obj->funcs; obj++) { + for (func = obj->funcs; func->old_name; func++) { + if (args->enable) { + func_addr = (unsigned long)func->old_func; + func_size = func->old_size; + } else { + func_addr = (unsigned long)func->new_func; + func_size = func->new_size; + } + func_name = func->old_name; + args->ret = klp_compare_address(frame->pc, func_addr, + func_size, func_name); + if (args->ret) + return args->ret; + } + } + + return args->ret; +} + +int klp_check_calltrace(struct klp_patch *patch, int enable) +{ + struct task_struct *g, *t; + struct stackframe frame; + int ret = 0; + + struct walk_stackframe_args args = { + .patch = patch, + .enable = enable, + .ret = 0 + }; + + for_each_process_thread(g, t) { + frame.fp = thread_saved_fp(t); + frame.sp = thread_saved_sp(t); + frame.pc = thread_saved_pc(t); + walk_stackframe(&frame, klp_check_activeness_func, &args); + if (args.ret) { + ret = args.ret; + pr_info("PID: %d Comm: %.20s\n", t->pid, t->comm); + show_stack(t, NULL, KERN_INFO); + goto out; + } + } + +out: + return ret; +} +#endif + +#define LJMP_INSN_SIZE 4 + +struct klp_func_node { + struct list_head node; + struct list_head func_stack; + void *old_func; + u32 old_insn; +}; + +static LIST_HEAD(klp_func_list); + +static struct klp_func_node *klp_find_func_node(void *old_func) +{ + struct klp_func_node *func_node; + + list_for_each_entry(func_node, &klp_func_list, node) { + if (func_node->old_func == old_func) + return func_node; + } + + return NULL; +} + +long arm_insn_read(void *addr, u32 *insnp) +{ + long ret; + u32 val; + + ret = copy_from_kernel_nofault(&val, addr, LJMP_INSN_SIZE); + if (!ret) + *insnp = le32_to_cpu(val); + + return ret; +} + +int arch_klp_patch_func(struct klp_func *func) +{ + struct klp_func_node *func_node; + unsigned long pc, new_addr; + u32 insn; + long ret; + + func_node = klp_find_func_node(func->old_func); + if (!func_node) { + func_node = kzalloc(sizeof(*func_node), GFP_ATOMIC); + if (!func_node) + return -ENOMEM; + + INIT_LIST_HEAD(&func_node->func_stack); + func_node->old_func = func->old_func; + ret = arm_insn_read(func->old_func, &func_node->old_insn); + if (ret) { + kfree(func_node); + return -EPERM; + } + list_add_rcu(&func_node->node, &klp_func_list); + } + + list_add_rcu(&func->stack_node, &func_node->func_stack); + + pc = (unsigned long)func->old_func; + new_addr = (unsigned long)func->new_func; + insn = arm_gen_branch(pc, new_addr); + + __patch_text((void *)pc, insn); + + return 0; +} + +void arch_klp_unpatch_func(struct klp_func *func) +{ + struct klp_func_node *func_node; + struct klp_func *next_func; + unsigned long pc, new_addr; + u32 insn; + + func_node = klp_find_func_node(func->old_func); + pc = (unsigned long)func_node->old_func; + if (list_is_singular(&func_node->func_stack)) { + insn = func_node->old_insn; + list_del_rcu(&func->stack_node); + list_del_rcu(&func_node->node); + kfree(func_node); + + __patch_text((void *)pc, insn); + } else { + list_del_rcu(&func->stack_node); + next_func = list_first_or_null_rcu(&func_node->func_stack, + struct klp_func, stack_node); + + new_addr = (unsigned long)next_func->new_func; + insn = arm_gen_branch(pc, new_addr); + + __patch_text((void *)pc, insn); + } +} -- GitLab