Skip to content
Snippets Groups Projects
Commit 453d3845 authored by Cheng Jian's avatar Cheng Jian Committed by 谢秀奇
Browse files

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: default avatarCheng Jian <cj.chengjian@huawei.com>
Reviewed-by: default avatarLi Bin <huawei.libin@huawei.com>
Signed-off-by: default avatarYang Yingliang <yangyingliang@huawei.com>
parent 96bdd177
No related branches found
No related tags found
No related merge requests found
......@@ -34,6 +34,7 @@
#ifdef CONFIG_LIVEPATCH_WO_FTRACE
#include <linux/sched/debug.h>
#include <linux/kallsyms.h>
#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
......@@ -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:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment