Commit e158c803 authored by Zheng Yejian's avatar Zheng Yejian
Browse files

livepatch: Fix patching functions which have static_call

hulk inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8MGE6


CVE: NA

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

It was reported that if 'static_call' is used in a old function, then
the livepatch module created by kpatch for that old function cannot be
inserted normally.

Root cause is that relocation of static_call symbols in livepatch module
has not been done while initing:
  load_module
    prepare_coming_module
      blocking_notifier_call_chain_robust
        notifier_call_chain_robust
          static_call_module_notify
            <-- 1. static_call symbols init here, but relocation is done
                   at below MARK "2."
    do_init_module
      do_one_initcall
        klp_register_patch
          klp_init_patch
            klp_init_object
              klp_init_object_loaded    <--  2. relocate .klp.xxx here

To solve it, we move the static_call initialization after relocation.

Signed-off-by: default avatarZheng Yejian <zhengyejian1@huawei.com>
parent a0c9db1e
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -343,4 +343,10 @@ static inline int static_call_text_reserved(void *start, void *end)

#endif /* CONFIG_HAVE_STATIC_CALL */

#if defined(CONFIG_HAVE_STATIC_CALL_INLINE) && defined(CONFIG_LIVEPATCH_WO_FTRACE)
int klp_static_call_register(struct module *mod);
#else
static inline int klp_static_call_register(struct module *mod) { return 0; }
#endif

#endif /* _LINUX_STATIC_CALL_H */
+13 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#ifdef CONFIG_LIVEPATCH_RESTRICT_KPROBE
#include <linux/kprobes.h>
#endif /* CONFIG_LIVEPATCH_RESTRICT_KPROBE */
#include <linux/static_call.h>
#endif /* CONFIG_LIVEPATCH_FTRACE */

/*
@@ -1067,6 +1068,18 @@ static int klp_init_patch(struct klp_patch *patch)
		pr_err("register jump label failed, ret=%d\n", ret);
		return ret;
	}
	ret = klp_static_call_register(patch->mod);
	if (ret) {
		/*
		 * We no need to distinctly clean pre-registered jump_label
		 * here because it will be clean at path:
		 *   load_module
		 *     do_init_module
		 *       fail_free_freeinit:  <-- notify GOING here
		 */
		pr_err("register static call failed, ret=%d\n", ret);
		return ret;
	}
	klp_for_each_object(patch, obj)
		klp_load_hook(obj);
#endif
+20 −0
Original line number Diff line number Diff line
@@ -367,6 +367,11 @@ static int static_call_add_module(struct module *mod)
	struct static_call_site *stop = start + mod->num_static_call_sites;
	struct static_call_site *site;

#ifdef CONFIG_LIVEPATCH_WO_FTRACE
	if (unlikely(!mod_klp_rel_completed(mod)))
		return 0;
#endif

	for (site = start; site != stop; site++) {
		unsigned long s_key = __static_call_key(site);
		unsigned long addr = s_key & ~STATIC_CALL_SITE_FLAGS;
@@ -409,6 +414,11 @@ static void static_call_del_module(struct module *mod)
	struct static_call_mod *site_mod, **prev;
	struct static_call_site *site;

#ifdef CONFIG_LIVEPATCH_WO_FTRACE
	if (unlikely(!mod_klp_rel_completed(mod)))
		return;
#endif

	for (site = start; site < stop; site++) {
		key = static_call_key(site);
		if (key == prev_key)
@@ -461,6 +471,16 @@ static struct notifier_block static_call_module_nb = {
	.notifier_call = static_call_module_notify,
};

#ifdef CONFIG_LIVEPATCH_WO_FTRACE
int klp_static_call_register(struct module *mod)
{
	int ret;

	ret = static_call_module_notify(&static_call_module_nb, MODULE_STATE_COMING, mod);
	return notifier_to_errno(ret);
}
#endif /* CONFIG_LIVEPATCH_WO_FTRACE */

#else

static inline int __static_call_mod_text_reserved(void *start, void *end)