Live-Patching Archive on lore.kernel.org
 help / color / Atom feed
From: Petr Mladek <pmladek@suse.com>
To: Jiri Kosina <jikos@kernel.org>,
	Josh Poimboeuf <jpoimboe@redhat.com>,
	Miroslav Benes <mbenes@suse.cz>
Cc: Joe Lawrence <joe.lawrence@redhat.com>,
	Kamalesh Babulal <kamalesh@linux.vnet.ibm.com>,
	Nicolai Stange <nstange@suse.de>,
	live-patching@vger.kernel.org, linux-kernel@vger.kernel.org,
	Petr Mladek <pmladek@suse.com>
Subject: [POC 08/23] livepatch: Automatically load livepatch module when the patch module is loaded
Date: Fri, 17 Jan 2020 16:03:08 +0100
Message-ID: <20200117150323.21801-9-pmladek@suse.com> (raw)
In-Reply-To: <20200117150323.21801-1-pmladek@suse.com>

The klp_module_coming() callback is called from the module loader when
any module is being loaded. It allows to load the related livepatch modules
in MODULE_COMMING state before mod->init() is called. It prevents the module
from loading when the livepatching fails from any reason.

klp_module_coming() originally did several tasks: livepatch-specific
reallocations, registered ftrace handlers, and called object callbacks
when any defined.

All the mentioned tasks were moved into by klp_add_object() that is
called in mod->init() of livepatch modules that are livepatching
other modules.

Instead, klp_module_coming() has to load the needed livepatch module(s).
This functionality is already used in the kernel.

It is solved by two tricks:

1. The list is searched repeatedly from the beginning. The already
   loaded objects are skipped. One object is handled in each
   iteration.

   This solves the problem when a livepatch is removed or added
   in the meantime. Especially, it prevents a crash when
   the given struct klp_patch disappeared from the list in
   the meantime.

   The object will get removed automatically when the livepatch
   gets removed.

   There might be an attempt to load the module twice: via
   the coming notifier and via klp_enable_livepatch(). It is
   already handled in the module loader code. Both call will
   either succeed or fail the same way.

2. There might be a false error when the livepatch gets removed
   in the meantime. It is solved by double checking the existence.

   It does not solve the situation when the livepatch is removed
   and loaded again in the meantime. It will be solved by a separate
   patch. Anyway, it is not much realistic scenario.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 kernel/livepatch/core.c                            | 81 ++++++++++++++++++++--
 .../testing/selftests/livepatch/test-callbacks.sh  |  1 +
 2 files changed, 77 insertions(+), 5 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index bb851f916182..34e3ee2be7ef 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -8,6 +8,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/kmod.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/mutex.h>
@@ -100,6 +101,24 @@ static struct klp_patch *klp_find_patch(const char *patch_name)
 	return NULL;
 }
 
+/*
+ * Search whether livepatch for a module is loaded.
+ * Do not use for "vmlinux" that is always loaded.
+ * Must be called under klp_mutex.
+ */
+static bool klp_is_object_loaded(struct klp_patch *patch,
+				 char *object_name)
+{
+	struct klp_object *obj;
+
+	klp_for_each_object(patch, obj) {
+		if (obj->name && !strcmp(object_name, obj->name))
+			return true;
+	}
+
+	return false;
+}
+
 struct klp_find_arg {
 	const char *objname;
 	const char *name;
@@ -1082,6 +1101,19 @@ static int __klp_disable_patch(struct klp_patch *patch)
 	return 0;
 }
 
+static int klp_try_load_object(const char *patch_name, const char *obj_name)
+{
+	int ret;
+
+	ret = request_module("%s__%s", patch_name, obj_name);
+	if (ret) {
+		pr_info("Module load failed: %s__%s\n", patch_name, obj_name);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int __klp_enable_patch(struct klp_patch *patch)
 {
 	struct klp_object *obj;
@@ -1290,19 +1322,58 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
 
 int klp_module_coming(struct module *mod)
 {
+	char patch_name[MODULE_NAME_LEN];
+	struct klp_patch *patch;
+	int ret = 0;
+
 	if (WARN_ON(mod->state != MODULE_STATE_COMING))
 		return -EINVAL;
 
 	mutex_lock(&klp_mutex);
+restart:
+	klp_for_each_patch(patch) {
+		if (!klp_is_object_name_supported(patch, mod->name))
+			continue;
+
+		if (klp_is_object_loaded(patch, mod->name))
+			continue;
+
+		strncpy(patch_name, patch->obj->patch_name, sizeof(patch_name));
+		mutex_unlock(&klp_mutex);
+
+		ret = klp_try_load_object(patch_name, mod->name);
+		/*
+		 * The load might have failed because the patch has
+		 * been removed in the meantime. In this case, the
+		 * error might be ignored.
+		 *
+		 * FIXME: It is not fully proof. The patch might have be
+		 * unloaded and loaded again in the mean time.
+		 */
+		mutex_lock(&klp_mutex);
+		if (ret) {
+			patch = klp_find_patch(patch_name);
+			if (patch)
+				goto err;
+			ret = 0;
+		}
+
+		/*
+		 * The list of patches might have been manipulated
+		 * in the meantime.
+		 */
+		goto restart;
+	}
+
 	/*
-	 * Each module has to know that klp_module_coming()
-	 * has been called. We never know what module will
-	 * get patched by a new patch.
+	 * All enabled livepatches are loaded now. From this point, any newly
+	 * enabled livepatch is responsible for loading the related livepatch
+	 * module in klp_enable_patch().
 	 */
 	mod->klp_alive = true;
+err:
 	mutex_unlock(&klp_mutex);
-
-	return 0;
+	return ret;
 }
 
 void klp_module_going(struct module *mod)
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh
index ccaed35d0901..39a4f35e5f8e 100755
--- a/tools/testing/selftests/livepatch/test-callbacks.sh
+++ b/tools/testing/selftests/livepatch/test-callbacks.sh
@@ -330,6 +330,7 @@ livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
 $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
 livepatch: pre-patch callback failed for object '$MOD_TARGET'
 livepatch: patch '$MOD_LIVEPATCH' failed for module '$MOD_TARGET', refusing to load module '$MOD_TARGET'
+livepatch: Module load failed: ${MOD_LIVEPATCH}__${MOD_TARGET}
 modprobe: ERROR: could not insert '$MOD_TARGET': No such device
 % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
 livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
-- 
2.16.4


  parent reply index

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-17 15:03 [POC 00/23] livepatch: Split livepatch module per-object Petr Mladek
2020-01-17 15:03 ` [POC 01/23] module: Allow to delete module also from inside kernel Petr Mladek
2020-01-21 11:11   ` Julien Thierry
2020-01-17 15:03 ` [POC 02/23] livepatch: Split livepatch modules per livepatched object Petr Mladek
2020-01-21 11:11   ` Julien Thierry
2020-01-28 12:16     ` Petr Mladek
2020-01-17 15:03 ` [POC 03/23] livepatch: Better checks of struct klp_object definition Petr Mladek
2020-01-21 11:27   ` Julien Thierry
2020-01-17 15:03 ` [POC 04/23] livepatch: Prevent loading livepatch sub-module unintentionally Petr Mladek
2020-01-17 15:03 ` [POC 05/23] livepatch: Initialize and free livepatch submodule Petr Mladek
2020-01-21 11:58   ` Julien Thierry
2020-01-17 15:03 ` [POC 06/23] livepatch: Enable the " Petr Mladek
2020-01-17 15:03 ` [POC 07/23] livepatch: Remove obsolete functionality from klp_module_coming() Petr Mladek
2020-01-17 15:03 ` Petr Mladek [this message]
2020-01-17 15:03 ` [POC 09/23] livepatch: Handle race when livepatches are reloaded during a module load Petr Mladek
2020-01-22 18:51   ` Julien Thierry
2020-01-17 15:03 ` [POC 10/23] livepatch: Handle modprobe exit code Petr Mladek
2020-01-17 15:03 ` [POC 11/23] livepatch: Safely detect forced transition when removing split livepatch modules Petr Mladek
2020-01-22 10:15   ` Julien Thierry
2020-01-17 15:03 ` [POC 12/23] livepatch: Automatically remove livepatch module when the object is freed Petr Mladek
2020-01-17 15:03 ` [POC 13/23] livepatch: Remove livepatch module when the livepatched module is unloaded Petr Mladek
2020-01-17 15:03 ` [POC 14/23] livepatch: Never block livepatch modules when the related module is being removed Petr Mladek
2020-01-17 15:03 ` [POC 15/23] livepatch: Prevent infinite loop when loading livepatch module Petr Mladek
2020-01-17 15:03 ` [POC 16/23] livepatch: Add patch into the global list early Petr Mladek
2020-01-17 15:03 ` [POC 17/23] livepatch: Load livepatches for modules when loading the main livepatch Petr Mladek
2020-01-17 15:03 ` [POC 18/23] module: Refactor add_unformed_module() Petr Mladek
2020-01-17 15:03 ` [POC 19/23] module/livepatch: Allow to use exported symbols from livepatch module for "vmlinux" Petr Mladek
2020-01-17 15:03 ` [POC 20/23] module/livepatch: Relocate local variables in the module loaded when the livepatch is being loaded Petr Mladek
2020-01-18 10:29   ` kbuild test robot
2020-01-17 15:03 ` [POC 21/23] livepatch: Remove obsolete arch_klp_init_object_loaded() Petr Mladek
2020-01-17 15:03 ` [POC 22/23] livepatch/module: Remove obsolete copy_module_elf() Petr Mladek
2020-01-17 15:03 ` [POC 23/23] module: Remove obsolete module_disable_ro() Petr Mladek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200117150323.21801-9-pmladek@suse.com \
    --to=pmladek@suse.com \
    --cc=jikos@kernel.org \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@redhat.com \
    --cc=kamalesh@linux.vnet.ibm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=nstange@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Live-Patching Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/live-patching/0 live-patching/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 live-patching live-patching/ https://lore.kernel.org/live-patching \
		live-patching@vger.kernel.org
	public-inbox-index live-patching

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.live-patching


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git