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]
Message-Id: <1498664247-12296-3-git-send-email-joe.lawrence@redhat.com>
Date:   Wed, 28 Jun 2017 11:37:27 -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@...hat.com>,
        Jiri Kosina <jikos@...nel.org>,
        Miroslav Benes <mbenes@...e.cz>, Petr Mladek <pmladek@...e.com>
Subject: [PATCH v2 2/2] livepatch: add shadow variable sample programs

Add sample livepatch modules to demonstrate the shadow variable API.

Signed-off-by: Joe Lawrence <joe.lawrence@...hat.com>
---

Reviewers -- it's probably easier to grok reading livepatch-shadow-mod.c
             *before* the two livepatch modules, livepatch-shadow-fix1.c
             and livepatch-shadow-fix2.c.

 samples/Kconfig                           |   5 +-
 samples/livepatch/Makefile                |   3 +
 samples/livepatch/livepatch-shadow-fix1.c | 160 ++++++++++++++
 samples/livepatch/livepatch-shadow-fix2.c | 157 +++++++++++++
 samples/livepatch/livepatch-shadow-mod.c  | 353 ++++++++++++++++++++++++++++++
 5 files changed, 675 insertions(+), 3 deletions(-)
 create mode 100644 samples/livepatch/livepatch-shadow-fix1.c
 create mode 100644 samples/livepatch/livepatch-shadow-fix2.c
 create mode 100644 samples/livepatch/livepatch-shadow-mod.c

diff --git a/samples/Kconfig b/samples/Kconfig
index 9cb63188d3ef..c332a3b9de05 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -71,11 +71,10 @@ config SAMPLE_RPMSG_CLIENT
 	  the rpmsg bus.
 
 config SAMPLE_LIVEPATCH
-	tristate "Build live patching sample -- loadable modules only"
+	tristate "Build live patching samples -- loadable modules only"
 	depends on LIVEPATCH && m
 	help
-	  Builds a sample live patch that replaces the procfs handler
-	  for /proc/cmdline to print "this has been live patched".
+	  Build sample live patch demonstrations.
 
 config SAMPLE_CONFIGFS
 	tristate "Build configfs patching sample -- loadable modules only"
diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile
index 10319d7ea0b1..539e81d433cd 100644
--- a/samples/livepatch/Makefile
+++ b/samples/livepatch/Makefile
@@ -1 +1,4 @@
 obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o
diff --git a/samples/livepatch/livepatch-shadow-fix1.c b/samples/livepatch/livepatch-shadow-fix1.c
new file mode 100644
index 000000000000..2e1d9cb89fad
--- /dev/null
+++ b/samples/livepatch/livepatch-shadow-fix1.c
@@ -0,0 +1,160 @@
+/*
+ * livepatch-shadow-fix1.c - Shadow variables, 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/>.
+ */
+
+/*
+ * Fixes the memory leak introduced in livepatch-shadow-mod through the
+ * use of a shadow variable.  This fix demonstrates the "extending" of
+ * short-lived data structures by patching its allocation and release
+ * functions.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+
+/* Shadow variable enums */
+#define SV_LEAK		1
+
+#define T1_PERIOD 1			/* allocator thread */
+#define T2_PERIOD (3 * T1_PERIOD)	/* cleanup thread */
+
+struct dummy {
+	struct list_head list;
+	unsigned long jiffies_expire;
+};
+
+struct dummy *livepatch_fix1_dummy_alloc(void)
+{
+	struct dummy *d;
+	void *leak;
+	void **shadow_leak;
+
+	d = kzalloc(sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return NULL;
+
+	/* Dummies live long enough to see a few t2 instances */
+	d->jiffies_expire = jiffies + 1000 * 4 * T2_PERIOD;
+
+	/*
+	 * Patch: save the extra memory location into a SV_LEAK shadow
+	 * variable.  A patched dummy_free routine can later fetch this
+	 * pointer to handle resource release.
+	 */
+	leak = kzalloc(sizeof(int), GFP_KERNEL);
+	shadow_leak =
+		klp_shadow_attach(d, SV_LEAK, &leak, sizeof(leak), GFP_KERNEL);
+
+	pr_info("%s: dummy @ %p, expires @ %lx\n",
+		__func__, d, d->jiffies_expire);
+
+	return d;
+}
+
+void livepatch_fix1_dummy_free(struct dummy *d)
+{
+	void **shadow_leak;
+
+	/*
+	 * Patch: fetch the saved SV_LEAK shadow variable, detach and
+	 * free it.  Note: handle cases where this shadow variable does
+	 * not exist (ie, dummy structures allocated before this livepatch
+	 * was loaded.)
+	 */
+	shadow_leak = klp_shadow_get(d, SV_LEAK);
+	if (shadow_leak) {
+		klp_shadow_detach(d, SV_LEAK);
+		kfree(*shadow_leak);
+		pr_info("%s: dummy @ %p, prevented leak @ %p\n",
+			 __func__, d, *shadow_leak);
+	} else {
+		pr_info("%s: dummy @ %p leaked!\n", __func__, d);
+	}
+
+	kfree(d);
+}
+
+static struct klp_func funcs[] = {
+	{
+		.old_name = "dummy_alloc",
+		.new_func = livepatch_fix1_dummy_alloc,
+	},
+	{
+		.old_name = "dummy_free",
+		.new_func = livepatch_fix1_dummy_free,
+	}, { }
+};
+
+static struct klp_object objs[] = {
+	{
+		.name = "livepatch_shadow_mod",
+		.funcs = funcs,
+	}, { }
+};
+
+static struct klp_patch patch = {
+	.mod = THIS_MODULE,
+	.objs = objs,
+};
+
+static int livepatch_shadow_fix1_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_shadow_fix1_exit(void)
+{
+	/* Cleanup any existing SV_LEAK shadow variables */
+	klp_shadow_detach_all(SV_LEAK);
+
+	WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(livepatch_shadow_fix1_init);
+module_exit(livepatch_shadow_fix1_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/samples/livepatch/livepatch-shadow-fix2.c b/samples/livepatch/livepatch-shadow-fix2.c
new file mode 100644
index 000000000000..ad6346da4aa3
--- /dev/null
+++ b/samples/livepatch/livepatch-shadow-fix2.c
@@ -0,0 +1,157 @@
+/*
+ * livepatch-shadow-fix2.c - Shadow variables, 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/>.
+ */
+
+/*
+ * Adds functionality to livepatch-shadow-mod's in-flight data
+ * structures through a shadow variable.  The livepatch patches a
+ * routine that periodically inspects data structures, incrementing a
+ * per-data-structure counter, creating the counter if needed.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+
+/* Shadow variable enums */
+#define SV_LEAK		1
+#define SV_COUNTER	2
+
+struct dummy {
+	struct list_head list;
+	unsigned long jiffies_expire;
+};
+
+bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies)
+{
+	int *shadow_count;
+	int count;
+
+	/*
+	 *  Patch: handle in-flight dummy structures, if they do not
+	 * already have a SV_COUNTER shadow variable, then attach a
+	 * new one.
+	 */
+	count = 0;
+	shadow_count = klp_shadow_get_or_attach(d, SV_COUNTER,
+						&count, sizeof(count),
+						GFP_NOWAIT);
+	if (shadow_count)
+		*shadow_count += 1;
+
+	return time_after(jiffies, d->jiffies_expire);
+}
+
+void livepatch_fix2_dummy_free(struct dummy *d)
+{
+	void **shadow_leak;
+	int *shadow_count;
+
+	/* Patch: copy the memory leak patch from the fix1 module. */
+	shadow_leak = klp_shadow_get(d, SV_LEAK);
+	if (shadow_leak) {
+		pr_info("%s: dummy @ %p, prevented leak @ %p\n",
+			 __func__, d, *shadow_leak);
+		klp_shadow_detach(d, SV_LEAK);
+		kfree(*shadow_leak);
+	} else {
+		pr_info("%s: dummy @ %p leaked!\n", __func__, d);
+	}
+
+	/*
+	 * Patch: fetch the SV_COUNTER shadow variable and display
+	 * the final count.  Detach the shadow variable.
+	 */
+	shadow_count = klp_shadow_get(d, SV_COUNTER);
+	if (shadow_count) {
+		pr_info("%s: dummy @ %p, check counter = %d\n",
+			__func__, d, *shadow_count);
+		klp_shadow_detach(d, SV_COUNTER);
+	}
+
+	kfree(d);
+}
+
+static struct klp_func funcs[] = {
+	{
+		.old_name = "dummy_check",
+		.new_func = livepatch_fix2_dummy_check,
+	},
+	{
+		.old_name = "dummy_free",
+		.new_func = livepatch_fix2_dummy_free,
+	}, { }
+};
+
+static struct klp_object objs[] = {
+	{
+		.name = "livepatch_shadow_mod",
+		.funcs = funcs,
+	}, { }
+};
+
+static struct klp_patch patch = {
+	.mod = THIS_MODULE,
+	.objs = objs,
+};
+
+static int livepatch_shadow_fix2_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_shadow_fix2_exit(void)
+{
+	/* Cleanup any existing SV_COUNTER shadow variables */
+	klp_shadow_detach_all(SV_COUNTER);
+
+	WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(livepatch_shadow_fix2_init);
+module_exit(livepatch_shadow_fix2_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/samples/livepatch/livepatch-shadow-mod.c b/samples/livepatch/livepatch-shadow-mod.c
new file mode 100644
index 000000000000..423f4b7b0adb
--- /dev/null
+++ b/samples/livepatch/livepatch-shadow-mod.c
@@ -0,0 +1,353 @@
+/*
+ * livepatch-shadow-mod.c - Shadow variables, buggy module 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/>.
+ */
+
+/* Creates two running threads:
+ *
+ *   T1 - allocs a new dummy structure, sets a jiffie expiration time
+ *        in the future, adds the new structure to a list
+ *
+ *   T2 - cleans up expired dummies on the list
+ *
+ * For the purposes of demonstrating a livepatch shadow variable fix,
+ * the creation thread also allocates additional memory, but doesn't
+ * save a pointer to it in the dummy structure.  The cleanup thread
+ * then leaks the extra memory when it frees (only) the dummy
+ * structure.
+ *
+ *
+ * Usage
+ * -----
+ *
+ * Load the buggy demonstration module:
+ * $ insmod samples/livepatch/livepatch-shadow-mod.ko
+ *
+ * T1 allocator thread periodically wakes up and creates new dummy
+ * structures allocating extra memory and set to expire some jiffie time
+ * in the future:
+ *
+ * [  529.503048] dummy_alloc: dummy @ ffff880112921e38, expires @ 10003af60
+ * [  530.527051] dummy_alloc: dummy @ ffff8801129219e8, expires @ 10003b360
+ * [  531.551049] dummy_alloc: dummy @ ffff880112920458, expires @ 10003b760
+ *
+ * T2 cleanup thread wakes up, but doesn't find any expired dummies to
+ * cleanup yet:
+ *
+ * [  531.551212] cleanup_thread:  jiffies = 100038880
+ * [  532.575045] dummy_alloc: dummy @ ffff880112921708, expires @ 10003bb60
+ * [  533.599051] dummy_alloc: dummy @ ffff880112920a18, expires @ 10003bf60
+ * [  534.623052] dummy_alloc: dummy @ ffff8801129205c8, expires @ 10003c360
+ * [  535.647056] dummy_alloc: dummy @ ffff880112921598, expires @ 10003c760
+ * [  536.671032] dummy_alloc: dummy @ ffff880112920fd8, expires @ 10003cb60
+ * [  537.567089] cleanup_thread:  jiffies = 10003a000
+ * [  537.695043] dummy_alloc: dummy @ ffff880112920008, expires @ 10003cf60
+ * [  538.719047] dummy_alloc: dummy @ ffff880112921878, expires @ 10003d360
+ * [  539.743061] dummy_alloc: dummy @ ffff880112920cf8, expires @ 10003d760
+ * [  540.767041] dummy_alloc: dummy @ ffff880116567148, expires @ 10003db60
+ * [  541.791045] dummy_alloc: dummy @ ffff880116567598, expires @ 10003df60
+ * [  542.815046] dummy_alloc: dummy @ ffff880116567cc8, expires @ 10003e360
+ *
+ * T2 cleanup thread eventually finds a few expired dummies, frees them,
+ * and in the process leaks memory!
+ *
+ * [  543.711058] cleanup_thread:  jiffies = 10003b800
+ * [  543.711366] dummy_free: dummy @ ffff880112920458, expired = 10003b760
+ * [  543.711853] dummy_free: dummy @ ffff8801129219e8, expired = 10003b360
+ * [  543.712294] dummy_free: dummy @ ffff880112921e38, expired = 10003af60
+ * [  543.839046] dummy_alloc: dummy @ ffff8801165672b8, expires @ 10003e760
+ * [  544.863054] dummy_alloc: dummy @ ffff8801165665c8, expires @ 10003eb60
+ * [  545.887066] dummy_alloc: dummy @ ffff880119c42008, expires @ 10003ef60
+ * [  546.911046] dummy_alloc: dummy @ ffff88011aa05b58, expires @ 10003f360
+ * [  547.935048] dummy_alloc: dummy @ ffff8801150005c8, expires @ 10003f760
+ * [  548.959062] dummy_alloc: dummy @ ffff880113ee9e38, expires @ 10003fb60
+ *
+ *
+ * Fix the memory leak
+ * -------------------
+ *
+ * One way to fix this memory leak is to attach a shadow variable
+ * pointer to each dummy structure at its allocation point.  This
+ * use-case demonstrates a livepatch/shadow variable fix for short-lived
+ * data structures.
+ *
+ * In this example, existing dummy structures will unfortunately
+ * continue to leak memory, however once all of the dummies that were
+ * allocated before the live patch are retired, the memory leak will be
+ * closed.
+ *
+ * Load the livepatch fix1:
+ * $ insmod samples/livepatch/livepatch-shadow-fix1.ko
+ *
+ * T1 alloc thread wakes up and now calls a patched dummy_alloc() which
+ * saves the extra memory into a shadow variable:
+ *
+ * [  564.147027] livepatch: enabling patch 'livepatch_shadow_fix1'
+ * [  564.153922] livepatch: 'livepatch_shadow_fix1': patching...
+ * [  564.319052] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af88a8, expires @ 100043760
+ * [  565.343060] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af9708, expires @ 100043b60
+ * [  565.727039] livepatch: 'livepatch_shadow_fix1': patching complete
+ * [  566.367050] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af8fd8, expires @ 100043f60
+ * [  567.391047] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af9878, expires @ 100044360
+ *
+ * T2 cleanup thread calls a patched dummy_free() routine which retrives
+ * the shadow variable that saved the memory pointer.
+ *
+ * Note: Initially, memory will still be leaked as no shadow variables
+ * are found for dummy structures already created:
+ *
+ * [  568.287070] cleanup_thread:  jiffies = 100041800
+ * [  568.287492] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee8738 leaked!
+ * [  568.288062] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee9598 leaked!
+ * [  568.288644] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee8178 leaked!
+ * [  568.289178] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee8b88 leaked!
+ * [  568.289724] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee9b58 leaked!
+ * [  568.290258] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880113ee82e8 leaked!
+ * [  568.415046] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af8a18, expires @ 100044760
+ * [  569.439050] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af85c8, expires @ 100044b60
+ * [  570.463049] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af9148, expires @ 100044f60
+ * [  571.487022] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af8458, expires @ 100045360
+ * [  572.511049] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff880112af99e8, expires @ 100045760
+ * [  573.535036] livepatch_shadow_fix1: livepatch_fix1_dummy_alloc: dummy @ ffff88011650e008, expires @ 100045b60
+ *
+ * T2 cleanup thread will eventually begin to reap dummy structures that
+ * do have an associated shadow variable.  Over time, this memory leak
+ * will be closed completely as all dummy structures will have a
+ * corresponding shadow variable tracking the extra allocated memory:
+ *
+ * [  580.575028] cleanup_thread:  jiffies = 100044800
+ * [  580.575675] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af8a18, prevented leak @ ffff880112b7c568
+ * [  580.576607] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af9878, prevented leak @ ffff880112b7d990
+ * [  580.577545] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af8fd8, prevented leak @ ffff880112b7c410
+ * [  580.578483] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af9708, prevented leak @ ffff880112b7dae8
+ * [  580.579327] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af88a8, prevented leak @ ffff880112b7c2b8
+ * [  580.580324] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff8801151bf148 leaked!
+ * ...
+ * [  586.719022] cleanup_thread:  jiffies = 100046000
+ * [  586.719648] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff88011650e738, prevented leak @ ffff880112b7c970
+ * [  586.720948] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff88011650e008, prevented leak @ ffff880112b7d588
+ * [  586.722215] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af99e8, prevented leak @ ffff880112b7c818
+ * [  586.723496] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af8458, prevented leak @ ffff880112b7d6e0
+ * [  586.724824] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af9148, prevented leak @ ffff880112b7c6c0
+ * [  586.726146] livepatch_shadow_fix1: livepatch_fix1_dummy_free: dummy @ ffff880112af85c8, prevented leak @ ffff880112b7d838
+ *
+ *
+ * Extend functionality
+ * --------------------
+ *
+ * Shadow variables can also be attached to in-flight dummy structures.
+ * In the second livepatch, use a shadow variable counter to keep track
+ * of the number of times a given dummy structure is inspected for
+ * expiration.
+ *
+ * Load the livepatch fix2 (on top of fix1):
+ * $ insmod samples/livepatch/livepatch-shadow-fix2.ko
+ *
+ * [  592.303611] livepatch: enabling patch 'livepatch_shadow_fix2'
+ * [  592.307458] livepatch: 'livepatch_shadow_fix2': patching...
+ *
+ * T2 cleanup thread calls a patched dummy_check(), which keeps a shadow
+ * variable counter of the number times it inspects a given dummy
+ * structure.  The final count is reported by a patched dummy_free().
+ *
+ * Note: initially the final count will be 1, as soon-to-expire dummies
+ * will only have had a shadow variable counter for a single pass
+ * through the alloc - check - cleanup cycle.  Overtime, newer dummies
+ * will have increased count values:
+ *
+ * [  592.863035] cleanup_thread:  jiffies = 100047800
+ * [  592.863355] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880129d89b58, prevented leak @ ffff880112b7c2b8
+ * [  592.864100] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880129d89b58, check counter = 1
+ * ...
+ * [  599.007047] cleanup_thread:  jiffies = 100049000
+ * [  599.007368] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880112920cf8, prevented leak @ ffff880112b7d838
+ * [  599.008167] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880112920cf8, check counter = 2
+ * ...
+ * [  605.151049] cleanup_thread:  jiffies = 10004a800
+ * [  605.151464] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880113ee9b58, prevented leak @ ffff88011643c818
+ * [  605.152146] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880113ee9b58, check counter = 2
+ * [  605.152783] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880112920fd8, prevented leak @ ffff880112b7c970
+ * [  605.153490] livepatch_shadow_fix2: livepatch_fix2_dummy_free: dummy @ ffff880112920fd8, check counter = 3
+ * ...
+ *
+ *
+ * Cleanup
+ * -------
+ *
+ * $ echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled
+ * $ echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled
+ * $ rmmod livepatch-shadow-fix2
+ * $ rmmod livepatch-shadow-fix1
+ * $ rmmod livepatch-shadow-mod
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/workqueue.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joe Lawrence <joe.lawrence@...hat.com>");
+MODULE_DESCRIPTION("Buggy module for shadow variable demo");
+
+#define T1_PERIOD 1			/* allocator thread */
+#define T2_PERIOD (3 * T1_PERIOD)	/* cleanup thread */
+
+LIST_HEAD(dummy_list);
+DEFINE_MUTEX(dummy_list_mutex);
+
+struct dummy {
+	struct list_head list;
+	unsigned long jiffies_expire;
+};
+
+noinline struct dummy *dummy_alloc(void)
+{
+	struct dummy *d;
+	void *leak;
+
+	d = kzalloc(sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return NULL;
+
+	/* Dummies live long enough to see a few t2 instances */
+	d->jiffies_expire = jiffies + 1000 * 4 * T2_PERIOD;
+
+	/* Oops, forgot to save leak! */
+	leak = kzalloc(sizeof(int), GFP_KERNEL);
+
+	pr_info("%s: dummy @ %p, expires @ %lx\n",
+		__func__, d, d->jiffies_expire);
+
+	return d;
+}
+
+noinline void dummy_free(struct dummy *d)
+{
+	pr_info("%s: dummy @ %p, expired = %lx\n",
+		__func__, d, d->jiffies_expire);
+
+	kfree(d);
+}
+
+noinline bool dummy_check(struct dummy *d, unsigned long jiffies)
+{
+	return time_after(jiffies, d->jiffies_expire);
+}
+
+/*
+ * T1: alloc_thread allocates new dummy structures, allocates additional
+ *     memory, aptly named "leak", but doesn't keep permanent record of it.
+ */
+struct workqueue_struct *alloc_wq;
+struct delayed_work alloc_dwork;
+static void alloc_thread(struct work_struct *work)
+{
+	struct dummy *d;
+
+	d = dummy_alloc();
+	if (!d)
+		return;
+
+	mutex_lock(&dummy_list_mutex);
+	list_add(&d->list, &dummy_list);
+	mutex_unlock(&dummy_list_mutex);
+
+	queue_delayed_work(alloc_wq, &alloc_dwork,
+		msecs_to_jiffies(1000 * T1_PERIOD));
+}
+
+/*
+ * T2: cleanup_thread frees dummy structures.  Without knownledge of "leak",
+ *     it leaks the additional memory that alloc_thread created.
+ */
+struct workqueue_struct *cleanup_wq;
+struct delayed_work cleanup_dwork;
+static void cleanup_thread(struct work_struct *work)
+{
+	struct dummy *d, *tmp;
+	unsigned long j;
+
+	j = jiffies;
+	pr_info("%s: jiffies = %lx\n", __func__, j);
+
+	mutex_lock(&dummy_list_mutex);
+	list_for_each_entry_safe(d, tmp, &dummy_list, list) {
+
+		/* Kick out and free any expired dummies */
+		if (dummy_check(d, j)) {
+			list_del(&d->list);
+			dummy_free(d);
+		}
+	}
+	mutex_unlock(&dummy_list_mutex);
+
+	queue_delayed_work(cleanup_wq, &cleanup_dwork,
+		msecs_to_jiffies(1000 * 2 * T2_PERIOD));
+}
+
+static int livepatch_shadow_mod_init(void)
+{
+	alloc_wq = create_singlethread_workqueue("klp_demo_alloc_wq");
+	if (!alloc_wq)
+		return -1;
+
+	cleanup_wq = create_singlethread_workqueue("klp_demo_cleanup_wq");
+	if (!cleanup_wq)
+		goto exit_free_alloc;
+
+	INIT_DELAYED_WORK(&alloc_dwork, alloc_thread);
+	queue_delayed_work(alloc_wq, &alloc_dwork, 1000 * T1_PERIOD);
+
+	INIT_DELAYED_WORK(&cleanup_dwork, cleanup_thread);
+	queue_delayed_work(cleanup_wq, &cleanup_dwork,
+		msecs_to_jiffies(1000 * T2_PERIOD));
+
+	return 0;
+
+exit_free_alloc:
+	destroy_workqueue(alloc_wq);
+
+	return -1;
+}
+
+static void livepatch_shadow_mod_exit(void)
+{
+	struct dummy *d, *tmp;
+
+	/* Cleanup T1 */
+	if (!cancel_delayed_work(&alloc_dwork))
+		flush_workqueue(alloc_wq);
+	destroy_workqueue(alloc_wq);
+
+	/* Cleanup T2 */
+	if (!cancel_delayed_work(&cleanup_dwork))
+		flush_workqueue(cleanup_wq);
+	destroy_workqueue(cleanup_wq);
+
+	/* Cleanup residual dummies */
+	list_for_each_entry_safe(d, tmp, &dummy_list, list) {
+		list_del(&d->list);
+		dummy_free(d);
+	}
+}
+
+module_init(livepatch_shadow_mod_init);
+module_exit(livepatch_shadow_mod_exit);
-- 
1.8.3.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ