diff options
| author | Seth Jennings <sjenning@redhat.com> | 2014-12-16 12:58:19 -0500 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2014-12-22 09:40:49 -0500 |
| commit | b700e7f03df5d92f85fa5247fe1f557528d3363d (patch) | |
| tree | d6da8186d1bd9c42bbd5db9f23deeb1e47bb6dec /kernel/livepatch | |
| parent | c5f4546593e9911800f0926c1090959b58bc5c93 (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/Kconfig | 18 | ||||
| -rw-r--r-- | kernel/livepatch/Makefile | 3 | ||||
| -rw-r--r-- | kernel/livepatch/core.c | 930 |
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 @@ | |||
| 1 | config ARCH_HAVE_LIVE_PATCHING | ||
| 2 | boolean | ||
| 3 | help | ||
| 4 | Arch supports kernel live patching | ||
| 5 | |||
| 6 | config 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 @@ | |||
| 1 | obj-$(CONFIG_LIVE_PATCHING) += livepatch.o | ||
| 2 | |||
| 3 | livepatch-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 | |||
| 38 | static DEFINE_MUTEX(klp_mutex); | ||
| 39 | static LIST_HEAD(klp_patches); | ||
| 40 | |||
| 41 | static struct kobject *klp_root_kobj; | ||
| 42 | |||
| 43 | static bool klp_is_module(struct klp_object *obj) | ||
| 44 | { | ||
| 45 | return obj->name; | ||
| 46 | } | ||
| 47 | |||
| 48 | static 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 */ | ||
| 54 | static 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 */ | ||
| 70 | static 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 | |||
| 81 | static bool klp_initialized(void) | ||
| 82 | { | ||
| 83 | return klp_root_kobj; | ||
| 84 | } | ||
| 85 | |||
| 86 | struct 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 | |||
| 99 | static 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 | |||
| 124 | static 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 | |||
| 150 | struct klp_verify_args { | ||
| 151 | const char *name; | ||
| 152 | const unsigned long addr; | ||
| 153 | }; | ||
| 154 | |||
| 155 | static 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 | |||
