Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7e545d6e authored by Jessica Yu's avatar Jessica Yu Committed by Jiri Kosina
Browse files

livepatch/module: remove livepatch module notifier



Remove the livepatch module notifier in favor of directly enabling and
disabling patches to modules in the module loader. Hard-coding the
function calls ensures that ftrace_module_enable() is run before
klp_module_coming() during module load, and that klp_module_going() is
run before ftrace_release_mod() during module unload. This way, ftrace
and livepatch code is run in the correct order during the module
load/unload sequence without dependence on the module notifier call chain.

Signed-off-by: default avatarJessica Yu <jeyu@redhat.com>
Reviewed-by: default avatarPetr Mladek <pmladek@suse.cz>
Acked-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Acked-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 4c973d16
Loading
Loading
Loading
Loading
+13 −0
Original line number Original line Diff line number Diff line
@@ -24,6 +24,8 @@
#include <linux/module.h>
#include <linux/module.h>
#include <linux/ftrace.h>
#include <linux/ftrace.h>


#if IS_ENABLED(CONFIG_LIVEPATCH)

#include <asm/livepatch.h>
#include <asm/livepatch.h>


enum klp_state {
enum klp_state {
@@ -132,4 +134,15 @@ int klp_unregister_patch(struct klp_patch *);
int klp_enable_patch(struct klp_patch *);
int klp_enable_patch(struct klp_patch *);
int klp_disable_patch(struct klp_patch *);
int klp_disable_patch(struct klp_patch *);


/* Called from the module loader during module coming/going states */
int klp_module_coming(struct module *mod);
void klp_module_going(struct module *mod);

#else /* !CONFIG_LIVEPATCH */

static inline int klp_module_coming(struct module *mod) { return 0; }
static inline void klp_module_going(struct module *mod) { }

#endif /* CONFIG_LIVEPATCH */

#endif /* _LINUX_LIVEPATCH_H_ */
#endif /* _LINUX_LIVEPATCH_H_ */
+71 −76
Original line number Original line Diff line number Diff line
@@ -99,12 +99,12 @@ static void klp_find_object_module(struct klp_object *obj)
	/*
	/*
	 * We do not want to block removal of patched modules and therefore
	 * We do not want to block removal of patched modules and therefore
	 * we do not take a reference here. The patches are removed by
	 * we do not take a reference here. The patches are removed by
	 * a going module handler instead.
	 * klp_module_going() instead.
	 */
	 */
	mod = find_module(obj->name);
	mod = find_module(obj->name);
	/*
	/*
	 * Do not mess work of the module coming and going notifiers.
	 * Do not mess work of klp_module_coming() and klp_module_going().
	 * Note that the patch might still be needed before the going handler
	 * Note that the patch might still be needed before klp_module_going()
	 * is called. Module functions can be called even in the GOING state
	 * is called. Module functions can be called even in the GOING state
	 * until mod->exit() finishes. This is especially important for
	 * until mod->exit() finishes. This is especially important for
	 * patches that modify semantic of the functions.
	 * patches that modify semantic of the functions.
@@ -866,71 +866,87 @@ int klp_register_patch(struct klp_patch *patch)
}
}
EXPORT_SYMBOL_GPL(klp_register_patch);
EXPORT_SYMBOL_GPL(klp_register_patch);


static int klp_module_notify_coming(struct klp_patch *patch,
int klp_module_coming(struct module *mod)
				     struct klp_object *obj)
{
{
	struct module *pmod = patch->mod;
	struct module *mod = obj->mod;
	int ret;
	int ret;
	struct klp_patch *patch;
	struct klp_object *obj;

	if (WARN_ON(mod->state != MODULE_STATE_COMING))
		return -EINVAL;

	mutex_lock(&klp_mutex);
	/*
	 * Each module has to know that klp_module_coming()
	 * has been called. We never know what module will
	 * get patched by a new patch.
	 */
	mod->klp_alive = true;

	list_for_each_entry(patch, &klp_patches, list) {
		klp_for_each_object(patch, obj) {
			if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
				continue;

			obj->mod = mod;


			ret = klp_init_object_loaded(patch, obj);
			ret = klp_init_object_loaded(patch, obj);
			if (ret) {
			if (ret) {
				pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
				pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
			pmod->name, mod->name, ret);
					patch->mod->name, obj->mod->name, ret);
		return ret;
				goto err;
			}
			}


			if (patch->state == KLP_DISABLED)
			if (patch->state == KLP_DISABLED)
		return 0;
				break;


			pr_notice("applying patch '%s' to loading module '%s'\n",
			pr_notice("applying patch '%s' to loading module '%s'\n",
		  pmod->name, mod->name);
				  patch->mod->name, obj->mod->name);


			ret = klp_enable_object(obj);
			ret = klp_enable_object(obj);
	if (ret)
			if (ret) {
				pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
				pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
			pmod->name, mod->name, ret);
					patch->mod->name, obj->mod->name, ret);
	return ret;
				goto err;
			}
			}


static void klp_module_notify_going(struct klp_patch *patch,
			break;
				    struct klp_object *obj)
		}
{
	}
	struct module *pmod = patch->mod;
	struct module *mod = obj->mod;

	if (patch->state == KLP_DISABLED)
		goto disabled;


	pr_notice("reverting patch '%s' on unloading module '%s'\n",
	mutex_unlock(&klp_mutex);
		  pmod->name, mod->name);


	klp_disable_object(obj);
	return 0;


disabled:
err:
	/*
	 * If a patch is unsuccessfully applied, return
	 * error to the module loader.
	 */
	pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n",
		patch->mod->name, obj->mod->name, obj->mod->name);
	mod->klp_alive = false;
	klp_free_object_loaded(obj);
	klp_free_object_loaded(obj);
	mutex_unlock(&klp_mutex);

	return ret;
}
}


static int klp_module_notify(struct notifier_block *nb, unsigned long action,
void klp_module_going(struct module *mod)
			     void *data)
{
{
	int ret;
	struct module *mod = data;
	struct klp_patch *patch;
	struct klp_patch *patch;
	struct klp_object *obj;
	struct klp_object *obj;


	if (action != MODULE_STATE_COMING && action != MODULE_STATE_GOING)
	if (WARN_ON(mod->state != MODULE_STATE_GOING &&
		return 0;
		    mod->state != MODULE_STATE_COMING))
		return;


	mutex_lock(&klp_mutex);
	mutex_lock(&klp_mutex);

	/*
	/*
	 * Each module has to know that the notifier has been called.
	 * Each module has to know that klp_module_going()
	 * We never know what module will get patched by a new patch.
	 * has been called. We never know what module will
	 * get patched by a new patch.
	 */
	 */
	if (action == MODULE_STATE_COMING)
		mod->klp_alive = true;
	else /* MODULE_STATE_GOING */
	mod->klp_alive = false;
	mod->klp_alive = false;


	list_for_each_entry(patch, &klp_patches, list) {
	list_for_each_entry(patch, &klp_patches, list) {
@@ -938,31 +954,20 @@ static int klp_module_notify(struct notifier_block *nb, unsigned long action,
			if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
			if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
				continue;
				continue;


			if (action == MODULE_STATE_COMING) {
			if (patch->state != KLP_DISABLED) {
				obj->mod = mod;
				pr_notice("reverting patch '%s' on unloading module '%s'\n",
				ret = klp_module_notify_coming(patch, obj);
					  patch->mod->name, obj->mod->name);
				if (ret) {
				klp_disable_object(obj);
					obj->mod = NULL;
					pr_warn("patch '%s' is in an inconsistent state!\n",
						patch->mod->name);
			}
			}
			} else /* MODULE_STATE_GOING */
				klp_module_notify_going(patch, obj);


			klp_free_object_loaded(obj);
			break;
			break;
		}
		}
	}
	}


	mutex_unlock(&klp_mutex);
	mutex_unlock(&klp_mutex);

	return 0;
}
}


static struct notifier_block klp_module_nb = {
	.notifier_call = klp_module_notify,
	.priority = INT_MIN+1, /* called late but before ftrace notifier */
};

static int __init klp_init(void)
static int __init klp_init(void)
{
{
	int ret;
	int ret;
@@ -973,21 +978,11 @@ static int __init klp_init(void)
		return -EINVAL;
		return -EINVAL;
	}
	}


	ret = register_module_notifier(&klp_module_nb);
	if (ret)
		return ret;

	klp_root_kobj = kobject_create_and_add("livepatch", kernel_kobj);
	klp_root_kobj = kobject_create_and_add("livepatch", kernel_kobj);
	if (!klp_root_kobj) {
	if (!klp_root_kobj)
		ret = -ENOMEM;
		return -ENOMEM;
		goto unregister;
	}


	return 0;
	return 0;

unregister:
	unregister_module_notifier(&klp_module_nb);
	return ret;
}
}


module_init(klp_init);
module_init(klp_init);
+10 −0
Original line number Original line Diff line number Diff line
@@ -53,6 +53,7 @@
#include <asm/sections.h>
#include <asm/sections.h>
#include <linux/tracepoint.h>
#include <linux/tracepoint.h>
#include <linux/ftrace.h>
#include <linux/ftrace.h>
#include <linux/livepatch.h>
#include <linux/async.h>
#include <linux/async.h>
#include <linux/percpu.h>
#include <linux/percpu.h>
#include <linux/kmemleak.h>
#include <linux/kmemleak.h>
@@ -984,6 +985,7 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
		mod->exit();
		mod->exit();
	blocking_notifier_call_chain(&module_notify_list,
	blocking_notifier_call_chain(&module_notify_list,
				     MODULE_STATE_GOING, mod);
				     MODULE_STATE_GOING, mod);
	klp_module_going(mod);
	ftrace_release_mod(mod);
	ftrace_release_mod(mod);


	async_synchronize_full();
	async_synchronize_full();
@@ -3315,6 +3317,7 @@ static noinline int do_init_module(struct module *mod)
	module_put(mod);
	module_put(mod);
	blocking_notifier_call_chain(&module_notify_list,
	blocking_notifier_call_chain(&module_notify_list,
				     MODULE_STATE_GOING, mod);
				     MODULE_STATE_GOING, mod);
	klp_module_going(mod);
	ftrace_release_mod(mod);
	ftrace_release_mod(mod);
	free_module(mod);
	free_module(mod);
	wake_up_all(&module_wq);
	wake_up_all(&module_wq);
@@ -3401,7 +3404,13 @@ static int complete_formation(struct module *mod, struct load_info *info)


static int prepare_coming_module(struct module *mod)
static int prepare_coming_module(struct module *mod)
{
{
	int err;

	ftrace_module_enable(mod);
	ftrace_module_enable(mod);
	err = klp_module_coming(mod);
	if (err)
		return err;

	blocking_notifier_call_chain(&module_notify_list,
	blocking_notifier_call_chain(&module_notify_list,
				     MODULE_STATE_COMING, mod);
				     MODULE_STATE_COMING, mod);
	return 0;
	return 0;
@@ -3553,6 +3562,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
 coming_cleanup:
 coming_cleanup:
	blocking_notifier_call_chain(&module_notify_list,
	blocking_notifier_call_chain(&module_notify_list,
				     MODULE_STATE_GOING, mod);
				     MODULE_STATE_GOING, mod);
	klp_module_going(mod);


 bug_cleanup:
 bug_cleanup:
	/* module_bug_cleanup needs module_mutex protection */
	/* module_bug_cleanup needs module_mutex protection */