From 453d38459f94417d9d5db7b75ed181a932e34ffc Mon Sep 17 00:00:00 2001 From: Cheng Jian Date: Mon, 28 Jan 2019 10:09:15 +0800 Subject: [PATCH] livepatch/arm64: fix func size less than limit euler inclusion category: feature Bugzilla: 5507 CVE: N/A ---------------------------------------- we need to modify the first 4 instructions of a livepatch function to complete the long jump if offset out of short-range. So it's important that this function must have more than 4 instructions, so we checked it when the livepatch module insmod. In fact, this corner case is highly unlikely tooccur on arm64, but it's still an effective and meaningful check to avoid crash by doing this. Signed-off-by: Cheng Jian Reviewed-by: Li Bin Signed-off-by: Yang Yingliang --- arch/arm64/kernel/livepatch.c | 26 ++++++++++++++++++++++++++ kernel/livepatch/core.c | 26 ++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kernel/livepatch.c b/arch/arm64/kernel/livepatch.c index e43afc787148..ead6a34bb523 100644 --- a/arch/arm64/kernel/livepatch.c +++ b/arch/arm64/kernel/livepatch.c @@ -34,6 +34,7 @@ #ifdef CONFIG_LIVEPATCH_WO_FTRACE #include +#include #endif #ifdef CONFIG_ARM64_MODULE_PLTS @@ -288,4 +289,29 @@ void arch_klp_unpatch_func(struct klp_func *func) #endif } } + +#ifdef CONFIG_ARM64_MODULE_PLTS +/* return 0 if the func can be patched */ +int arch_klp_func_can_patch(struct klp_func *func) +{ + unsigned long pc = func->old_addr; + unsigned long new_addr = (unsigned long)func->new_func; + unsigned long old_size = func->old_size; + + if ((long)old_size <= 0) + return -EINVAL; + + if (!offset_in_range(pc, new_addr, SZ_128M) && + (old_size < LJMP_INSN_SIZE * sizeof(u32))) { + pr_err("func %s size less than limit\n", func->old_name); + return -EPERM; + } + return 0; +} +#else +int arch_klp_func_can_patch(struct klp_func *func) +{ + return 0; +} +#endif /* #ifdef CONFIG_ARM64_MODULE_PLTS */ #endif diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 4b481176681a..1fdb688475e9 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -916,14 +916,31 @@ static void klp_free_patch(struct klp_patch *patch) list_del(&patch->list); } +#ifdef CONFIG_LIVEPATCH_WO_FTRACE +int __weak arch_klp_func_can_patch(struct klp_func *func) +{ + return 0; +} +#endif + static int klp_init_func(struct klp_object *obj, struct klp_func *func) { +#ifdef CONFIG_LIVEPATCH_WO_FTRACE + int ret; +#endif + if (!func->old_name || !func->new_func) return -EINVAL; if (strlen(func->old_name) >= KSYM_NAME_LEN) return -EINVAL; +#ifdef CONFIG_LIVEPATCH_WO_FTRACE + ret = arch_klp_func_can_patch(func); + if (ret) + return ret; +#endif + INIT_LIST_HEAD(&func->stack_node); func->patched = false; #ifdef CONFIG_LIVEPATCH_FTRACE @@ -1015,18 +1032,19 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj) if (ret) goto put; - klp_for_each_func(obj, func) { - ret = klp_init_func(obj, func); + if (klp_is_object_loaded(obj)) { + ret = klp_init_object_loaded(patch, obj); if (ret) goto out; } - if (klp_is_object_loaded(obj)) { - ret = klp_init_object_loaded(patch, obj); + klp_for_each_func(obj, func) { + ret = klp_init_func(obj, func); if (ret) goto free; } + return 0; free: -- GitLab