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

Commit 0f49fc95 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull livepatching update from Jiri Kosina:

 - cleanup of module notifiers; this depends on a module.c cleanup which
   has been acked by Rusty; from Jessica Yu

 - small assorted fixes and MAINTAINERS update

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching:
  livepatch/module: remove livepatch module notifier
  modules: split part of complete_formation() into prepare_coming_module()
  livepatch: Update maintainers
  livepatch: Fix the error message about unresolvable ambiguity
  klp: remove CONFIG_LIVEPATCH dependency from klp headers
  klp: remove superfluous errors in asm/livepatch.h
parents 49dc2b71 7e545d6e
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -6596,9 +6596,10 @@ F: drivers/platform/x86/hp_accel.c

LIVE PATCHING
M:	Josh Poimboeuf <jpoimboe@redhat.com>
M:	Seth Jennings <sjenning@redhat.com>
M:	Jessica Yu <jeyu@redhat.com>
M:	Jiri Kosina <jikos@kernel.org>
M:	Vojtech Pavlik <vojtech@suse.com>
M:	Miroslav Benes <mbenes@suse.cz>
R:	Petr Mladek <pmladek@suse.com>
S:	Maintained
F:	kernel/livepatch/
F:	include/linux/livepatch.h
+0 −4
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@

#include <linux/module.h>

#ifdef CONFIG_LIVEPATCH
static inline int klp_check_compiler_support(void)
{
	return 0;
@@ -36,8 +35,5 @@ static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{
	regs->psw.addr = ip;
}
#else
#error Include linux/livepatch.h, not asm/livepatch.h
#endif

#endif
+0 −4
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@
#include <linux/module.h>
#include <linux/ftrace.h>

#ifdef CONFIG_LIVEPATCH
static inline int klp_check_compiler_support(void)
{
#ifndef CC_USING_FENTRY
@@ -40,8 +39,5 @@ static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{
	regs->ip = ip;
}
#else
#error Include linux/livepatch.h, not asm/livepatch.h
#endif

#endif /* _ASM_X86_LIVEPATCH_H */
+9 −0
Original line number Diff line number Diff line
@@ -134,6 +134,15 @@ int klp_unregister_patch(struct klp_patch *);
int klp_enable_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_ */
+73 −78
Original line number 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 take a reference here. The patches are removed by
	 * a going module handler instead.
	 * klp_module_going() instead.
	 */
	mod = find_module(obj->name);
	/*
	 * Do not mess work of the module coming and going notifiers.
	 * Note that the patch might still be needed before the going handler
	 * Do not mess work of klp_module_coming() and klp_module_going().
	 * Note that the patch might still be needed before klp_module_going()
	 * is called. Module functions can be called even in the GOING state
	 * until mod->exit() finishes. This is especially important for
	 * patches that modify semantic of the functions.
@@ -190,8 +190,8 @@ static int klp_find_object_symbol(const char *objname, const char *name,
	if (args.addr == 0)
		pr_err("symbol '%s' not found in symbol table\n", name);
	else if (args.count > 1 && sympos == 0) {
		pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n",
		       args.count, name, objname);
		pr_err("unresolvable ambiguity for symbol '%s' in object '%s'\n",
		       name, objname);
	} else if (sympos != args.count && sympos > 0) {
		pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n",
		       sympos, name, objname ? objname : "vmlinux");
@@ -866,71 +866,87 @@ int klp_register_patch(struct klp_patch *patch)
}
EXPORT_SYMBOL_GPL(klp_register_patch);

static int klp_module_notify_coming(struct klp_patch *patch,
				     struct klp_object *obj)
int klp_module_coming(struct module *mod)
{
	struct module *pmod = patch->mod;
	struct module *mod = obj->mod;
	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);
			if (ret) {
				pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
			pmod->name, mod->name, ret);
		return ret;
					patch->mod->name, obj->mod->name, ret);
				goto err;
			}

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

			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);
	if (ret)
			if (ret) {
				pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
			pmod->name, mod->name, ret);
	return ret;
					patch->mod->name, obj->mod->name, ret);
				goto err;
			}

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

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

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

	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);
	mutex_unlock(&klp_mutex);

	return ret;
}

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

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

	mutex_lock(&klp_mutex);

	/*
	 * Each module has to know that the notifier has been called.
	 * We never know what module will get patched by a new patch.
	 * Each module has to know that klp_module_going()
	 * 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;

	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))
				continue;

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

			klp_free_object_loaded(obj);
			break;
		}
	}

	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)
{
	int ret;
@@ -973,21 +978,11 @@ static int __init klp_init(void)
		return -EINVAL;
	}

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

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

	return 0;

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

module_init(klp_init);
Loading