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 17/23] livepatch: Load livepatches for modules when loading  the main livepatch
Date: Fri, 17 Jan 2020 16:03:17 +0100
Message-ID: <20200117150323.21801-18-pmladek@suse.com> (raw)
In-Reply-To: <20200117150323.21801-1-pmladek@suse.com>

The livepatch modules have been split per-object. The livepatch module for
"vmlinux" must always exist. It includes struct klp_patch and defines
the sysfs interface. It must be loaded first and removed last.

The related livepatch modules that are livepatching already loaded modules
must be loaded before the main livepatch gets enabled.

It might be possible to check if a livepatch module exists for each
loaded module by calling "modprobe -qR". But it would be a nightmare
to avoid races.

Instead, names of all livepatched modules are statically defined
in patch->obj_names array. klp_loaded_objects_livepatches()
iterates over this list and loads the needed modules one
by one. It must be called with klp_mutex released. It is
safe becase:

  1. The livepatch itself could not be removed until it returns
     from mod->init(). Note that it is not visible in sysfs
     interface at the moment.

  2. Any operation with other livepatches is prevented using
     klp_loading_livepatch variable. The affected operations
     are klp_enable_livepatch() and enabled_store().

  3. Modules loaded and removed in parallel are properly handled
     by klp_module_coming() and klp_module_going() callbacks.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 kernel/livepatch/core.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 79 insertions(+), 1 deletion(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 06676ec63ba7..6d4ec7642908 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -45,6 +45,7 @@ DEFINE_MUTEX(klp_mutex);
 LIST_HEAD(klp_patches);
 
 static struct kobject *klp_root_kobj;
+static struct klp_patch *klp_loading_patch;
 
 static bool klp_is_module(struct klp_object *obj)
 {
@@ -332,6 +333,16 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
 		goto out;
 	}
 
+	/*
+	 * Do not manipulate other livepatches when a new one is being
+	 * loaded to make our live easier. The patch that is being londed
+	 * is not visible in sysfs at this point.
+	 */
+	if (klp_loading_patch) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	/*
 	 * Allow to reverse a pending transition in both ways. It might be
 	 * necessary to complete the transition without forcing and breaking
@@ -1052,6 +1063,9 @@ int klp_add_object(struct klp_object *obj)
 	if (ret)
 		goto err;
 
+	if (!patch->enabled)
+		goto out;
+
 	ret = klp_init_object(patch, obj);
 	if (ret) {
 		pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
@@ -1081,6 +1095,7 @@ int klp_add_object(struct klp_object *obj)
 	if (patch != klp_transition_patch)
 		klp_post_patch_callback(obj);
 
+out:
 	mutex_unlock(&klp_mutex);
 	return 0;
 
@@ -1147,12 +1162,43 @@ static int klp_try_load_object(const char *patch_name, const char *obj_name)
 	return 0;
 }
 
+/*
+ * This function is guarded by klp_loading_patch instead of klp_mutex to avoid
+ * deadlocks. It loads other livepatch modules via modprobe called in userspace.
+ * The other modules need to take klp-mutex as well.
+ */
+static int klp_load_objects_livepatches(struct klp_patch *patch)
+{
+	int i, ret;
+
+	for (i = 0; patch->obj_names[i]; i++) {
+		char *obj_name = patch->obj_names[i];
+		struct module *patched_mod;
+		bool obj_alive;
+
+		mutex_lock(&module_mutex);
+		patched_mod = find_module(obj_name);
+		obj_alive = patched_mod ? patched_mod->klp_alive : false;
+		mutex_unlock(&module_mutex);
+
+		/* Load livepatch module only for loaded objects. */
+		if (!obj_alive)
+			continue;
+
+		ret = klp_try_load_object(patch->obj->patch_name, obj_name);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 static int __klp_enable_patch(struct klp_patch *patch)
 {
 	struct klp_object *obj;
 	int ret;
 
-	if (klp_transition_patch)
+	if (klp_transition_patch || klp_loading_patch)
 		return -EBUSY;
 
 	if (WARN_ON(patch->enabled))
@@ -1199,6 +1245,21 @@ static int __klp_enable_patch(struct klp_patch *patch)
 	return ret;
 }
 
+static int klp_check_patches_in_progress(void)
+{
+	struct klp_patch *patch;
+
+	if (klp_loading_patch)
+		patch = klp_loading_patch;
+	else if (klp_transition_patch)
+		patch = klp_transition_patch;
+	else
+		return 0;
+
+	pr_err("Already enabling livepatch: %s\n", patch->obj->mod->name);
+	return -EBUSY;
+}
+
 /**
  * klp_enable_patch() - enable the livepatch
  * @patch:	patch to be enabled
@@ -1233,6 +1294,12 @@ int klp_enable_patch(struct klp_patch *patch)
 
 	mutex_lock(&klp_mutex);
 
+	ret = klp_check_patches_in_progress();
+	if (ret) {
+		mutex_unlock(&klp_mutex);
+		return ret;
+	}
+
 	if (!klp_is_patch_compatible(patch)) {
 		pr_err("Livepatch patch (%s) is not compatible with the already installed livepatches.\n",
 			patch->obj->mod->name);
@@ -1246,6 +1313,17 @@ int klp_enable_patch(struct klp_patch *patch)
 		return ret;
 	}
 
+	klp_loading_patch = patch;
+	mutex_unlock(&klp_mutex);
+
+	ret = klp_load_objects_livepatches(patch);
+
+	mutex_lock(&klp_mutex);
+	klp_loading_patch = NULL;
+
+	if (ret)
+		goto err;
+
 	ret = klp_init_patch(patch);
 	if (ret)
 		goto err;
-- 
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 ` [POC 08/23] livepatch: Automatically load livepatch module when the patch module is loaded Petr Mladek
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 ` Petr Mladek [this message]
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-18-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