Commit ed8c4c72 authored by Zheng Yejian's avatar Zheng Yejian Committed by Zheng Zengkai
Browse files

livepatch/core: Restrict minimum size of function that can be patched

hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I60L10


CVE: NA

--------------------------------

If a function is patched, instructions at the beginning are modified to be
'jump codes' which jump to new function. This requires the function be big
enough, otherwise the modification may be out of function range.

Currently each architecture needs to implement arch_klp_func_can_patch()
to check function size. However, there exists following problems:
  1. arch 'x86' didn't implement arch_klp_func_can_patch();
  2. implementations in arm64 & ppc32, function size is checked only if
     there's a long jump. There is a scenario where a very short function
     is successfully patched, but as kernel module increases, someday long
     jump is required, then the function become unable to be patched.
  3. implementaions look like duplicate.

In this patch, introduce macro KLP_MAX_REPLACE_SIZE to denote the maximum
size that will be replaced on patching, then move the check ahead into
klp_init_object_loaded().

Fixes: c33e4283 ("livepatch/core: Allow implementation without ftrace")
Signed-off-by: default avatarZheng Yejian <zhengyejian1@huawei.com>
Reviewed-by: default avatarKuohai Xu <xukuohai@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent 8bf83486
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ struct arch_klp_data {
	u32 saved_opcode;
};

#define KLP_MAX_REPLACE_SIZE sizeof_field(struct arch_klp_data, old_insns)

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);
long arch_klp_save_old_code(struct arch_klp_data *arch_data, void *old_func);
+0 −25
Original line number Diff line number Diff line
@@ -496,28 +496,3 @@ void arch_klp_unpatch_func(struct klp_func *func)
		do_patch(pc, (unsigned long)next_func->new_func);
	}
}

#ifdef CONFIG_ARM_MODULE_PLTS
/* return 0 if the func can be patched */
int arch_klp_func_can_patch(struct klp_func *func)
{
	unsigned long pc = (unsigned long)func->old_func;
	unsigned long new_addr = (unsigned long)func->new_func;
	unsigned long old_size = func->old_size;

	if (!old_size)
		return -EINVAL;

	if (!offset_in_range(pc, new_addr, SZ_32M) &&
	    (old_size < LJMP_INSN_SIZE * ARM_INSN_SIZE)) {
		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_ARM_MODULE_PLTS */
+2 −0
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ struct arch_klp_data {
	u32 saved_opcode;
};

#define KLP_MAX_REPLACE_SIZE sizeof_field(struct arch_klp_data, old_insns)

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);
long arch_klp_save_old_code(struct arch_klp_data *arch_data, void *old_func);
+0 −25
Original line number Diff line number Diff line
@@ -483,28 +483,3 @@ void arch_klp_unpatch_func(struct klp_func *func)
		do_patch(pc, (unsigned long)next_func->new_func);
	}
}

#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 = (unsigned long)func->old_func;
	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
+2 −0
Original line number Diff line number Diff line
@@ -118,6 +118,8 @@ struct arch_klp_data {

#endif	/* CONFIG_PPC64 */

#define KLP_MAX_REPLACE_SIZE sizeof_field(struct arch_klp_data, old_insns)

struct stackframe {
	unsigned long sp;
	unsigned long pc;
Loading