[<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