[RFC PATCH 2/2] livepatch: implement 'sticky' patches

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



There are scenarios where it may not be safe to disable a livepatch once
it has been enabled.  Add a 'sticky' bool to struct klp_patch so that
livepatches can indicate this to the core.  If set, the livepatch may
not be directly disabled/unloaded.

Signed-off-by: Joe Lawrence <joe.lawrence@xxxxxxxxxx>
---
 .../ABI/testing/sysfs-kernel-livepatch        |  7 +++
 Documentation/livepatch/sticky-patches.txt    | 27 ++++++++
 include/linux/livepatch.h                     |  2 +
 kernel/livepatch/core.c                       | 13 +++-
 lib/livepatch/Makefile                        |  2 +
 lib/livepatch/test_klp_sticky_0.c             | 43 +++++++++++++
 lib/livepatch/test_klp_sticky_1.c             | 43 +++++++++++++
 tools/testing/selftests/livepatch/Makefile    |  1 +
 .../selftests/livepatch/test-sticky.sh        | 61 +++++++++++++++++++
 9 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/livepatch/sticky-patches.txt
 create mode 100644 lib/livepatch/test_klp_sticky_0.c
 create mode 100644 lib/livepatch/test_klp_sticky_1.c
 create mode 100755 tools/testing/selftests/livepatch/test-sticky.sh

diff --git a/Documentation/ABI/testing/sysfs-kernel-livepatch b/Documentation/ABI/testing/sysfs-kernel-livepatch
index bc4475062446..18282aaa5ca9 100644
--- a/Documentation/ABI/testing/sysfs-kernel-livepatch
+++ b/Documentation/ABI/testing/sysfs-kernel-livepatch
@@ -32,6 +32,13 @@ Description:
 		code is currently applied.  Writing 0 will disable the patch
 		while writing 1 will re-enable the patch.
 
+What:		/sys/kernel/livepatch/<patch>/sticky
+Date:		Feb 2019
+KernelVersion:	5.0.0
+Contact:	live-patching@xxxxxxxxxxxxxxx
+Description:
+		Is this livepatch not disable-able (bool).
+
 What:		/sys/kernel/livepatch/<patch>/version
 Date:		Feb 2019
 KernelVersion:	5.0.0
diff --git a/Documentation/livepatch/sticky-patches.txt b/Documentation/livepatch/sticky-patches.txt
new file mode 100644
index 000000000000..b23430a6fcba
--- /dev/null
+++ b/Documentation/livepatch/sticky-patches.txt
@@ -0,0 +1,27 @@
+==============
+Sticky Patches
+==============
+
+Livepatches may introduce new behavior or changes to in-memory data
+structures such that it would be unsafe to disable the livepatch.  Newer
+versions of the livepatch that account for such changes may be safe to
+supplement or replace the original patch.  In these instances, the
+original livepatch should set its klp_patch 'sticky' bool to true.
+
+If klp.patch.sticky is set, the livepatching core will:
+
+  - Always load the livepatch, given it meets all the usual livepatch
+    requirements.
+
+  - Allow the patch-enable transition to be reversed.
+
+  - Once the patch has fully transitioned, however, it will no longer
+    have the ability to be directly disabled.
+
+  - Subsequent livepatches may be loaded, regardless of whether they are
+    atomic-replace cumulative patches or not.
+
+  - If a subsequent atomic-replace patch is loaded that doesn't set
+    klp.sticky, it may be disabled and unloaded.  The net effect is that
+    the original sticky livepatch will be replaced and then its
+    replacement removed.
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 70d5354e11a8..a33ce73015ba 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -151,6 +151,7 @@ struct klp_object {
  * @mod:	reference to the live patch module
  * @objs:	object entries for kernel objects to be patched
  * @version:	patch version number
+ * @sticky:	patch can be replaced, but not disabled
  * @replace:	replace all actively used patches
  * @list:	list node for global list of actively used patches
  * @kobj:	kobject for sysfs resources
@@ -166,6 +167,7 @@ struct klp_patch {
 	struct module *mod;
 	struct klp_object *objs;
 	unsigned int version;
+	bool sticky;
 	bool replace;
 
 	/* internal */
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index cc120b5018e1..50bf39ec2a9d 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -380,7 +380,7 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
 	 */
 	if (patch == klp_transition_patch)
 		klp_reverse_transition();
-	else if (!enabled)
+	else if (!enabled && !patch->sticky)
 		ret = __klp_disable_patch(patch);
 	else
 		ret = -EINVAL;
@@ -412,6 +412,15 @@ static ssize_t transition_show(struct kobject *kobj,
 			patch == klp_transition_patch);
 }
 
+static ssize_t sticky_show(struct kobject *kobj,
+				   struct kobj_attribute *attr, char *buf)
+{
+	struct klp_patch *patch;
+
+	patch = container_of(kobj, struct klp_patch, kobj);
+	return snprintf(buf, PAGE_SIZE-1, "%d\n", patch->sticky);
+}
+
 static ssize_t version_show(struct kobject *kobj,
 				   struct kobj_attribute *attr, char *buf)
 {
@@ -452,11 +461,13 @@ static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr,
 
 static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled);
 static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition);
+static struct kobj_attribute sticky_kobj_attr = __ATTR_RO(sticky);
 static struct kobj_attribute version_kobj_attr = __ATTR_RO(version);
 static struct kobj_attribute force_kobj_attr = __ATTR_WO(force);
 static struct attribute *klp_patch_attrs[] = {
 	&enabled_kobj_attr.attr,
 	&transition_kobj_attr.attr,
+	&sticky_kobj_attr.attr,
 	&version_kobj_attr.attr,
 	&force_kobj_attr.attr,
 	NULL
diff --git a/lib/livepatch/Makefile b/lib/livepatch/Makefile
index 6890dadd4d0e..7b5f9f17efea 100644
--- a/lib/livepatch/Makefile
+++ b/lib/livepatch/Makefile
@@ -9,6 +9,8 @@ obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
 				test_klp_callbacks_mod.o \
 				test_klp_livepatch.o \
 				test_klp_shadow_vars.o \
+				test_klp_sticky_0.o \
+				test_klp_sticky_1.o \
 				test_klp_version_0.o \
 				test_klp_version_1.o \
 				test_klp_version_2.o \
diff --git a/lib/livepatch/test_klp_sticky_0.c b/lib/livepatch/test_klp_sticky_0.c
new file mode 100644
index 000000000000..50164ad94207
--- /dev/null
+++ b/lib/livepatch/test_klp_sticky_0.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019 Joe Lawrence <joe.lawrence@xxxxxxxxxx>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+static int replace;
+module_param(replace, int, 0644);
+MODULE_PARM_DESC(replace, "replace (default=0)");
+
+static int klp_sticky;
+module_param(klp_sticky, int, 0644);
+MODULE_PARM_DESC(klp_sticky, "klp_sticky (default=0)");
+
+static struct klp_object objs[] = {
+	{ }
+};
+
+static struct klp_patch patch = {
+	.mod = THIS_MODULE,
+	.objs = objs,
+};
+
+static int test_klp_version_0_init(void)
+{
+	patch.replace = replace;
+	patch.sticky = klp_sticky;
+	return klp_enable_patch(&patch);
+}
+
+static void test_klp_version_0_exit(void)
+{
+}
+
+module_init(test_klp_version_0_init);
+module_exit(test_klp_version_0_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Joe Lawrence <joe.lawrence@xxxxxxxxxx>");
+MODULE_DESCRIPTION("Livepatch test: sticky patch #0");
diff --git a/lib/livepatch/test_klp_sticky_1.c b/lib/livepatch/test_klp_sticky_1.c
new file mode 100644
index 000000000000..d6bfd47555b8
--- /dev/null
+++ b/lib/livepatch/test_klp_sticky_1.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019 Joe Lawrence <joe.lawrence@xxxxxxxxxx>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+static int replace;
+module_param(replace, int, 0644);
+MODULE_PARM_DESC(replace, "replace (default=0)");
+
+static int klp_sticky;
+module_param(klp_sticky, int, 0644);
+MODULE_PARM_DESC(klp_sticky, "klp_sticky (default=0)");
+
+static struct klp_object objs[] = {
+	{ }
+};
+
+static struct klp_patch patch = {
+	.mod = THIS_MODULE,
+	.objs = objs,
+};
+
+static int test_klp_version_0_init(void)
+{
+	patch.replace = replace;
+	patch.sticky = klp_sticky;
+	return klp_enable_patch(&patch);
+}
+
+static void test_klp_version_0_exit(void)
+{
+}
+
+module_init(test_klp_version_0_init);
+module_exit(test_klp_version_0_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Joe Lawrence <joe.lawrence@xxxxxxxxxx>");
+MODULE_DESCRIPTION("Livepatch test: sticky patch #1");
diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile
index d4fa03153dea..92a9292fc97c 100644
--- a/tools/testing/selftests/livepatch/Makefile
+++ b/tools/testing/selftests/livepatch/Makefile
@@ -4,6 +4,7 @@ TEST_GEN_PROGS := \
 	test-livepatch.sh \
 	test-callbacks.sh \
 	test-shadow-vars.sh \
+	test-sticky.sh \
 	test-versions.sh
 
 include ../lib.mk
diff --git a/tools/testing/selftests/livepatch/test-sticky.sh b/tools/testing/selftests/livepatch/test-sticky.sh
new file mode 100755
index 000000000000..00d4282af492
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test-sticky.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2019 Joe Lawrence <joe.lawrence@xxxxxxxxxx>
+
+. $(dirname $0)/functions.sh
+
+MOD_LIVEPATCH_0=test_klp_sticky_0
+MOD_LIVEPATCH_1=test_klp_sticky_1
+
+# disable_lp_fail(modname) - disable a livepatch, but expect to fail
+#	modname - module name to unload
+function disable_lp_fail()
+{
+	local mod="$1"; shift
+
+	log "% echo 0 > /sys/kernel/livepatch/$mod/enabled (expect failure)"
+	if echo 0 > /sys/kernel/livepatch/"$mod"/enabled 2>/dev/null; then
+		die "$mod unexpectedly disabled"
+	fi
+}
+
+
+set_dynamic_debug
+
+
+# TEST: sticky livepatch cannot be disabled
+#
+
+echo -n "TEST: sticky livepatch cannot be disabled ... "
+dmesg -C
+
+load_lp $MOD_LIVEPATCH_0 replace=0 klp_sticky=1
+disable_lp_fail $MOD_LIVEPATCH_0
+load_lp $MOD_LIVEPATCH_1 replace=1 klp_sticky=0
+disable_lp $MOD_LIVEPATCH_1
+unload_lp $MOD_LIVEPATCH_1
+unload_lp $MOD_LIVEPATCH_0
+
+check_result "% modprobe $MOD_LIVEPATCH_0 replace=0 klp_sticky=1
+livepatch: enabling patch '$MOD_LIVEPATCH_0'
+livepatch: '$MOD_LIVEPATCH_0': initializing patching transition
+livepatch: '$MOD_LIVEPATCH_0': starting patching transition
+livepatch: '$MOD_LIVEPATCH_0': completing patching transition
+livepatch: '$MOD_LIVEPATCH_0': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH_0/enabled (expect failure)
+% modprobe $MOD_LIVEPATCH_1 replace=1 klp_sticky=0
+livepatch: enabling patch '$MOD_LIVEPATCH_1'
+livepatch: '$MOD_LIVEPATCH_1': initializing patching transition
+livepatch: '$MOD_LIVEPATCH_1': starting patching transition
+livepatch: '$MOD_LIVEPATCH_1': completing patching transition
+livepatch: '$MOD_LIVEPATCH_1': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH_1/enabled
+livepatch: '$MOD_LIVEPATCH_1': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH_1': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH_1': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH_1': unpatching complete
+% rmmod $MOD_LIVEPATCH_1
+% rmmod $MOD_LIVEPATCH_0"
+
+
+exit 0
-- 
2.20.1




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux