lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 12 Jul 2017 10:10:00 -0400
From:   Joe Lawrence <joe.lawrence@...hat.com>
To:     live-patching@...r.kernel.org, linux-kernel@...r.kernel.org
Cc:     Josh Poimboeuf <jpoimboe@...hat.com>, Jessica Yu <jeyu@...nel.org>,
        Jiri Kosina <jikos@...nel.org>,
        Miroslav Benes <mbenes@...e.cz>,
        Petr Mladek <pmladek@...e.com>,
        Chris J Arges <chris.j.arges@...onical.com>
Subject: [PATCH] livepatch: add (un)patch hooks

When the livepatch core executes klp_(un)patch_object, call out to a
livepatch-module specified array of callback hooks.  These hooks provide
a notification mechanism for livepatch modules when klp_objects are
(un)patching. This may be most interesting when another kernel module
is a klp_object target and the livepatch module needs to execute code
after the target is loaded, but before its module_init code is run.

The patch-hook executes right before patching objects and the
unpatch-hook executes right after unpatching objects.

Signed-off-by: Joe Lawrence <joe.lawrence@...hat.com>
---
 Documentation/livepatch/hooks.txt        |  98 +++++++++++++++++++++++++
 include/linux/livepatch.h                |  32 ++++++++
 kernel/livepatch/core.c                  |   5 --
 kernel/livepatch/patch.c                 |  35 +++++++++
 samples/livepatch/Makefile               |   2 +
 samples/livepatch/livepatch-hooks-demo.c | 122 +++++++++++++++++++++++++++++++
 samples/livepatch/livepatch-hooks-mod.c  |  38 ++++++++++
 7 files changed, 327 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/livepatch/hooks.txt
 create mode 100644 samples/livepatch/livepatch-hooks-demo.c
 create mode 100644 samples/livepatch/livepatch-hooks-mod.c

diff --git a/Documentation/livepatch/hooks.txt b/Documentation/livepatch/hooks.txt
new file mode 100644
index 000000000000..ef18101a3b90
--- /dev/null
+++ b/Documentation/livepatch/hooks.txt
@@ -0,0 +1,98 @@
+(Un)patching Hooks
+==================
+
+Livepatching (un)patch-hooks provide a mechanism to register and execute
+a set of callback functions when the kernel's livepatching core performs
+an (un)patching operation on a given kernel object.
+
+The hooks are provided and registered by a livepatch module as part of
+klp_objects that make up its klp_patch structure.  Both patch and
+unpatch-hook function signatures accept a pointer to a klp_object
+argument and return an integer status, ie:
+
+  static int patch_hook(struct klp_object *obj)
+  {
+  	/* ... */
+  }
+  static int unpatch_hook(struct klp_object *obj)
+  {
+  	/* ... */
+  }
+
+  static struct klp_hook patch_hooks[] = {
+  	{
+  		.hook = patch_hook,
+  	}, { }
+  };
+  static struct klp_hook unpatch_hooks[] = {
+  	{
+  		.hook = unpatch_hook,
+  	}, { }
+  };
+
+  static struct klp_object objs[] = {
+  	{
+  		/* ... */
+  		.patch_hooks = patch_hooks,
+  		.unpatch_hooks = unpatch_hooks,
+  	}, { }
+  };
+
+  static struct klp_patch patch = {
+  	.mod = THIS_MODULE,
+  	.objs = objs,
+  };
+
+If a hook returns non-zero status, the livepatching core will log a
+hook failure warning message.
+
+Multiple (un)patch-hooks may be registered per klp_object.  Each hook
+will execute regardless of any previously executed hook's non-zero
+return status.
+
+Hooks are optional.  The livepatching core will not execute any
+callbacks for an empty klp_hook.hook array or a NULL klp_hook.hook
+value.
+
+
+For module targets
+------------------
+
+In the case of kernel module objects, patch-hooks provide a livepatch
+module opportunity to defer execution until a target module is loaded.
+Similarly, unpatch-hooks only call back into a livepatch module after a
+target module has itself cleaned up.  In these cases, the order of
+execution looks like:
+
+  load kernel module
+  execute all patch_hooks[] for this kernel object
+  livepatch kernel object
+  execute module_init function
+
+  ...
+
+  unload kernel module
+  execute module_exit function
+  livepatch restore kernel object
+  execute all unpatch_hooks[] for this kernel object
+
+On the other hand, if a target kernel module is already present when a
+livepatch is loading, then the corresponding patch hook(s) will execute
+as soon as the livepatching kernel core enables the livepatch.
+
+It may be useful for hooks to inspect the module state of the klp_object
+it is passed (i.e. obj->mod->state).  Patch hooks can expect to see
+modules in MODULE_STATE_LIVE and MODULE_STATE_COMING states.  Unpatch
+hooks can expect modules in MODULE_STATE_LIVE and MODULE_STATE_GOING
+states.
+
+
+For vmlinux target
+------------------
+
+As the kernel is always loaded, patch-hooks for vmlinux will execute as
+soon as the livepatch core enables the livepatch.  Patch-hooks will also
+run if the livepatch is disabled and then re-enabled.
+
+Unpatch-hooks for vmlinux will only execute when the livepatch is
+disabled.
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 194991ef9347..d95050386ac7 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -87,10 +87,23 @@ struct klp_func {
 	bool transition;
 };
 
+struct klp_object;
+
+/**
+ * struct klp_hook - hook structure for live patching
+ * @hook:	function to be executed on hook
+ *
+ */
+struct klp_hook {
+	int (*hook)(struct klp_object *obj);
+};
+
 /**
  * struct klp_object - kernel object structure for live patching
  * @name:	module name (or NULL for vmlinux)
  * @funcs:	function entries for functions to be patched in the object
+ * @patch_hooks:	functions to be executed on patching
+ * @unpatch_hooks:	functions to be executed on unpatching
  * @kobj:	kobject for sysfs resources
  * @mod:	kernel module associated with the patched object
  *		(NULL for vmlinux)
@@ -100,6 +113,8 @@ struct klp_object {
 	/* external */
 	const char *name;
 	struct klp_func *funcs;
+	struct klp_hook *patch_hooks;
+	struct klp_hook *unpatch_hooks;
 
 	/* internal */
 	struct kobject kobj;
@@ -108,6 +123,17 @@ struct klp_object {
 };
 
 /**
+ * klp_is_module() - is klp_object a module?
+ * @obj:	klp_object pointer
+ *
+ * Return: true if klp_object is a loadable module
+ */
+static inline bool klp_is_module(struct klp_object *obj)
+{
+	return obj->name;
+}
+
+/**
  * struct klp_patch - patch structure for live patching
  * @mod:	reference to the live patch module
  * @objs:	object entries for kernel objects to be patched
@@ -138,6 +164,12 @@ struct klp_patch {
 	     func->old_name || func->new_func || func->old_sympos; \
 	     func++)
 
+#define klp_for_each_patch_hook(obj, hook) \
+	for (hook = obj->patch_hooks; hook && hook->hook; hook++)
+
+#define klp_for_each_unpatch_hook(obj, hook) \
+	for (hook = obj->unpatch_hooks; hook && hook->hook; hook++)
+
 int klp_register_patch(struct klp_patch *);
 int klp_unregister_patch(struct klp_patch *);
 int klp_enable_patch(struct klp_patch *);
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index b9628e43c78f..ff3685470057 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -49,11 +49,6 @@
 
 static struct kobject *klp_root_kobj;
 
-static bool klp_is_module(struct klp_object *obj)
-{
-	return obj->name;
-}
-
 static bool klp_is_object_loaded(struct klp_object *obj)
 {
 	return !obj->name || obj->mod;
diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c
index 52c4e907c14b..c8084a18ddb7 100644
--- a/kernel/livepatch/patch.c
+++ b/kernel/livepatch/patch.c
@@ -235,25 +235,60 @@ static int klp_patch_func(struct klp_func *func)
 	return ret;
 }
 
+/**
+ * klp_run_hook - execute a given klp_hook callback
+ * @hook:	callback hook
+ * @obj:	kernel object that has been hooked
+ *
+ * Return: return value from hook, or 0 if none is currently associated
+ */
+static int klp_run_hook(struct klp_hook *hook, struct klp_object *obj)
+{
+	if (hook && hook->hook)
+		return (*hook->hook)(obj);
+
+	return 0;
+}
+
 void klp_unpatch_object(struct klp_object *obj)
 {
 	struct klp_func *func;
+	struct klp_hook *hook;
+	int ret;
 
 	klp_for_each_func(obj, func)
 		if (func->patched)
 			klp_unpatch_func(func);
 
 	obj->patched = false;
+
+	klp_for_each_unpatch_hook(obj, hook) {
+		ret = klp_run_hook(hook, obj);
+		if (ret) {
+			pr_warn("unpatch hook '%p' failed for object '%s'\n",
+				hook, klp_is_module(obj) ? obj->name : "vmlinux");
+		}
+	}
+
 }
 
 int klp_patch_object(struct klp_object *obj)
 {
 	struct klp_func *func;
+	struct klp_hook *hook;
 	int ret;
 
 	if (WARN_ON(obj->patched))
 		return -EINVAL;
 
+	klp_for_each_patch_hook(obj, hook) {
+		ret = klp_run_hook(hook, obj);
+		if (ret) {
+			pr_warn("patch hook '%p' failed for object '%s'\n",
+				hook, klp_is_module(obj) ? obj->name : "vmlinux");
+		}
+	}
+
 	klp_for_each_func(obj, func) {
 		ret = klp_patch_func(func);
 		if (ret) {
diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile
index 10319d7ea0b1..2568a56ba8f3 100644
--- a/samples/livepatch/Makefile
+++ b/samples/livepatch/Makefile
@@ -1 +1,3 @@
 obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-hooks-demo.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-hooks-mod.o
diff --git a/samples/livepatch/livepatch-hooks-mod.c b/samples/livepatch/livepatch-hooks-mod.c
new file mode 100644
index 000000000000..f4ec09a5fc53
--- /dev/null
+++ b/samples/livepatch/livepatch-hooks-mod.c
@@ -0,0 +1,38 @@
+/*
+ * livepatch-hooks-mod.c - (un)patching hooks demo support module
+ *
+ * Copyright (C) 2017 Joe Lawrence <joe.lawrence@...hat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+static int livepatch_hooks_mod_init(void)
+{
+	pr_info("%s\n", __func__);
+	return 0;
+}
+
+static void livepatch_hooks_mod_exit(void)
+{
+	pr_info("%s\n", __func__);
+}
+
+module_init(livepatch_hooks_mod_init);
+module_exit(livepatch_hooks_mod_exit);
+MODULE_LICENSE("GPL");
diff --git a/samples/livepatch/livepatch-hooks-demo.c b/samples/livepatch/livepatch-hooks-demo.c
new file mode 100644
index 000000000000..672a749a0549
--- /dev/null
+++ b/samples/livepatch/livepatch-hooks-demo.c
@@ -0,0 +1,122 @@
+/*
+ * livepatch-hooks-demo.c - (un)patching hooks livepatch demo
+ *
+ * Copyright (C) 2017 Joe Lawrence <joe.lawrence@...hat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+const char *module_state[] = {
+	[MODULE_STATE_LIVE]	= "[MODULE_STATE_LIVE] Normal state",
+	[MODULE_STATE_COMING]	= "[MODULE_STATE_COMING] Full formed, running module_init",
+	[MODULE_STATE_GOING]	= "[MODULE_STATE_GOING] Going away",
+	[MODULE_STATE_UNFORMED]	= "[MODULE_STATE_UNFORMED] Still setting it up",
+};
+
+static void hook_info(const char *hook, struct klp_object *obj)
+{
+	if (klp_is_module(obj))
+		pr_info("%s: %s\n", hook, module_state[obj->mod->state]);
+	else
+		pr_info("%s: vmlinux\n", hook);
+}
+
+/* Executed on object patching (ie, patch enablement) */
+static int patch_hook(struct klp_object *obj)
+{
+	hook_info(__func__, obj);
+	return 0;
+}
+
+/* Executed on object unpatching (ie, patch disablement) */
+static int unpatch_hook(struct klp_object *obj)
+{
+	hook_info(__func__, obj);
+	return 0;
+}
+
+static struct klp_func funcs[] = {
+	{ }
+};
+
+static struct klp_hook patch_hooks[] = {
+	{
+		.hook = patch_hook,
+	}, { }
+};
+static struct klp_hook unpatch_hooks[] = {
+	{
+		.hook = unpatch_hook,
+	}, { }
+};
+
+static struct klp_object objs[] = {
+	{
+		.name = "livepatch_hooks_mod",
+		.funcs = funcs,
+		.patch_hooks = patch_hooks,
+		.unpatch_hooks = unpatch_hooks,
+	}, { }
+};
+
+static struct klp_patch patch = {
+	.mod = THIS_MODULE,
+	.objs = objs,
+};
+
+static int livepatch_hooks_demo_init(void)
+{
+	int ret;
+
+	if (!klp_have_reliable_stack() && !patch.immediate) {
+		/*
+		 * WARNING: Be very careful when using 'patch.immediate' in
+		 * your patches.  It's ok to use it for simple patches like
+		 * this, but for more complex patches which change function
+		 * semantics, locking semantics, or data structures, it may not
+		 * be safe.  Use of this option will also prevent removal of
+		 * the patch.
+		 *
+		 * See Documentation/livepatch/livepatch.txt for more details.
+		 */
+		patch.immediate = true;
+		pr_notice("The consistency model isn't supported for your architecture.  Bypassing safety mechanisms and applying the patch immediately.\n");
+	}
+
+	ret = klp_register_patch(&patch);
+	if (ret)
+		return ret;
+	ret = klp_enable_patch(&patch);
+	if (ret) {
+		WARN_ON(klp_unregister_patch(&patch));
+		return ret;
+	}
+	return 0;
+}
+
+static void livepatch_hooks_demo_exit(void)
+{
+	WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(livepatch_hooks_demo_init);
+module_exit(livepatch_hooks_demo_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
-- 
1.8.3.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ