aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/livepatch
diff options
context:
space:
mode:
authorSeth Jennings <sjenning@redhat.com>2014-12-16 12:58:19 -0500
committerJiri Kosina <jkosina@suse.cz>2014-12-22 09:40:49 -0500
commitb700e7f03df5d92f85fa5247fe1f557528d3363d (patch)
treed6da8186d1bd9c42bbd5db9f23deeb1e47bb6dec /kernel/livepatch
parentc5f4546593e9911800f0926c1090959b58bc5c93 (diff)
livepatch: kernel: add support for live patching
This commit introduces code for the live patching core. It implements an ftrace-based mechanism and kernel interface for doing live patching of kernel and kernel module functions. It represents the greatest common functionality set between kpatch and kgraft and can accept patches built using either method. This first version does not implement any consistency mechanism that ensures that old and new code do not run together. In practice, ~90% of CVEs are safe to apply in this way, since they simply add a conditional check. However, any function change that can not execute safely with the old version of the function can _not_ be safely applied in this version. [ jkosina@suse.cz: due to the number of contributions that got folded into this original patch from Seth Jennings, add SUSE's copyright as well, as discussed via e-mail ] Signed-off-by: Seth Jennings <sjenning@redhat.com> Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Reviewed-by: Miroslav Benes <mbenes@suse.cz> Reviewed-by: Petr Mladek <pmladek@suse.cz> Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Miroslav Benes <mbenes@suse.cz> Signed-off-by: Petr Mladek <pmladek@suse.cz> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'kernel/livepatch')
-rw-r--r--kernel/livepatch/Kconfig18
-rw-r--r--kernel/livepatch/Makefile3
-rw-r--r--kernel/livepatch/core.c930
3 files changed, 951 insertions, 0 deletions
diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig
new file mode 100644
index 000000000000..96da00fbc120
--- /dev/null
+++ b/kernel/livepatch/Kconfig
@@ -0,0 +1,18 @@
1config ARCH_HAVE_LIVE_PATCHING
2 boolean
3 help
4 Arch supports kernel live patching
5
6config LIVE_PATCHING
7 boolean "Kernel Live Patching"
8 depends on DYNAMIC_FTRACE_WITH_REGS
9 depends on MODULES
10 depends on SYSFS
11 depends on KALLSYMS_ALL
12 depends on ARCH_HAVE_LIVE_PATCHING
13 help
14 Say Y here if you want to support kernel live patching.
15 This option has no runtime impact until a kernel "patch"
16 module uses the interface provided by this option to register
17 a patch, causing calls to patched functions to be redirected
18 to new function code contained in the patch module.
diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile
new file mode 100644
index 000000000000..7c1f00861428
--- /dev/null
+++ b/kernel/livepatch/Makefile
@@ -0,0 +1,3 @@
1obj-$(CONFIG_LIVE_PATCHING) += livepatch.o
2
3livepatch-objs := core.o
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
new file mode 100644
index 000000000000..f99fe189d596
--- /dev/null
+++ b/kernel/livepatch/core.c
@@ -0,0 +1,930 @@
1/*
2 * core.c - Kernel Live Patching Core
3 *
4 * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
5 * Copyright (C) 2014 SUSE
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22
23#include <linux/module.h>
24#include <linux/kernel.h>
25#include <linux/mutex.h>
26#include <linux/slab.h>
27#include <linux/ftrace.h>
28#include <linux/list.h>
29#include <linux/kallsyms.h>
30#include <linux/livepatch.h>
31
32/*
33 * The klp_mutex protects the klp_patches list and state transitions of any
34 * structure reachable from the patches list. References to any structure must
35 * be obtained under mutex protection.
36 */
37
38static DEFINE_MUTEX(klp_mutex);
39static LIST_HEAD(klp_patches);
40
41static struct kobject *klp_root_kobj;
42
43static bool klp_is_module(struct klp_object *obj)
44{
45 return obj->name;
46}
47
48static bool klp_is_object_loaded(struct klp_object *obj)
49{
50 return !obj->name || obj->mod;
51}
52
53/* sets obj->mod if object is not vmlinux and module is found */
54static void klp_find_object_module(struct klp_object *obj)
55{
56 if (!klp_is_module(obj))
57 return;
58
59 mutex_lock(&module_mutex);
60 /*
61 * We don't need to take a reference on the module here because we have
62 * the klp_mutex, which is also taken by the module notifier. This
63 * prevents any module from unloading until we release the klp_mutex.
64 */
65 obj->mod = find_module(obj->name);
66 mutex_unlock(&module_mutex);
67}
68
69/* klp_mutex must be held by caller */
70static bool klp_is_patch_registered(struct klp_patch *patch)
71{
72 struct klp_patch *mypatch;
73
74 list_for_each_entry(mypatch, &klp_patches, list)
75 if (mypatch == patch)
76 return true;
77
78 return false;
79}
80
81static bool klp_initialized(void)
82{
83 return klp_root_kobj;
84}
85
86struct klp_find_arg {
87 const char *objname;
88 const char *name;
89 unsigned long addr;
90 /*
91 * If count == 0, the symbol was not found. If count == 1, a unique
92 * match was found and addr is set. If count > 1, there is
93 * unresolvable ambiguity among "count" number of symbols with the same
94 * name in the same object.
95 */
96 unsigned long count;
97};
98
99static int klp_find_callback(void *data, const char *name,
100 struct module *mod, unsigned long addr)
101{
102 struct klp_find_arg *args = data;
103
104 if ((mod && !args->objname) || (!mod && args->objname))
105 return 0;
106
107 if (strcmp(args->name, name))
108 return 0;
109
110 if (args->objname && strcmp(args->objname, mod->name))
111 return 0;
112
113 /*
114 * args->addr might be overwritten if another match is found
115 * but klp_find_object_symbol() handles this and only returns the
116 * addr if count == 1.
117 */
118 args->addr = addr;
119 args->count++;
120
121 return 0;
122}
123
124static int klp_find_object_symbol(const char *objname, const char *name,
125 unsigned long *addr)
126{
127 struct klp_find_arg args = {
128 .objname = objname,
129 .name = name,
130 .addr = 0,
131 .count = 0
132 };
133
134 kallsyms_on_each_symbol(klp_find_callback, &args);
135
136 if (args.count == 0)
137 pr_err("symbol '%s' not found in symbol table\n", name);
138 else if (args.count > 1)
139 pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n",
140 args.count, name, objname);
141 else {
142 *addr = args.addr;
143 return 0;
144 }
145
146 *addr = 0;
147 return -EINVAL;
148}
149
150struct klp_verify_args {
151 const char *name;
152 const unsigned long addr;
153};
154
155static int klp_verify_callback(void *data, const char *name,
156 struct module *mod, unsigned long addr)
157{
158 struct klp_verify_args *args = data;
159
160 if (!mod &&
161 !strcmp(args->name, name) &&
162 args->addr == addr)
163 return 1;
164
165 return 0;
166}
167
168