2014-11-17 01:30:32

by Seth Jennings

[permalink] [raw]
Subject: [PATCHv2 0/3] Kernel Live Patching

Changelog:

Thanks for all the feedback!

changes in v2:
- rebase to next-20141113
- add copyright/license block to livepatch.h
- add _LINUX prefix to header defines
- replace semaphore with mutex
- add LPC_ prefix to state enum
- convert BUGs to WARNs and handle properly
- change Kconfig default to n
- remove [old|new] attrs from function sysfs dir (KASLR leak, no use)
- disregard user provided old_addr if kernel uses KASLR
- s/out/err for error path labels
- s/unregister/disable for uniform terminology
- s/lp/lpc for module notifier elements
- replace module ref'ing with unload notifier + mutex protection
- adjust notifier priority to run before ftrace
- make LIVE_PATCHING boolean (about to depend on arch stuff)
- move x86-specific reloc code to arch/x86
- s/dynrela/reloc/
- add live patching sysfs documentation
- add API function kernel-doc
- TODO: kernel-doc for API structs once agreed upon

Summary:

This patchset 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 [1] and kGraft [2] and can
accept patches built using either method. This solution was discussed in the
Live Patching Mini-conference at LPC 2014 [3].

The model consists of a live patching "core" that provides an interface for
other "patch" kernel modules to register patches with the core.

Patch modules contain the new function code and create an lp_patch structure
containing the required data about what functions to patch, where the new code
for each patched function resides, and in which kernel object (vmlinux or
module) the function to be patch resides. The patch module then invokes the
lp_register_patch() function to register with the core, then lp_enable_patch()
to have the core redirect the execution paths using ftrace.

An example patch module can be found here:
https://github.com/spartacus06/livepatch/blob/master/patch/patch.c

The live patching core creates a sysfs hierarchy for user-level access to live
patching information. The hierarchy is structured like this:

/sys/kernel/livepatch
/sys/kernel/livepatch/<patch>
/sys/kernel/livepatch/<patch>/enabled
/sys/kernel/livepatch/<patch>/<object>
/sys/kernel/livepatch/<patch>/<object>/<func>

The old function is located using one of two methods: it is either provided by
the patch module (only possible for a function in vmlinux) or kallsyms lookup.
Symbol ambiguity results in a failure.

The core takes a reference on the patch module itself to keep it from
unloading. This is because, without a mechanism to ensure that no thread is
currently executing in the patched function, we can not determine whether it is
safe to unload the patch module. For this reason, unloading patch modules is
currently not allowed.

Disabling patches can be done using the "enabled" attribute of the patch:

echo 0 > /sys/kernel/livepatch/<patch>/enabled

If a patch module contains a patch for a module that is not currently loaded,
there is nothing to patch so the core does nothing for that patch object.
However, the core registers a module notifier that looks for COMING events so
that if the module is ever loaded, it is immediately patched. If a module with
patch code is removed, the notifier looks for GOING events and disables any
patched functions for that object before it unloads. The notifier has a higher
priority than that of the ftrace notifier so that it runs before the ftrace
notifier for GOING events and we can cleanly unregister from ftrace.

kpatch and kGraft each have their own mechanisms for ensuring system
consistency during the patching process. 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
for now.

[1] https://github.com/dynup/kpatch
[2] https://git.kernel.org/cgit/linux/kernel/git/jirislaby/kgraft.git/
[3] https://etherpad.fr/p/LPC2014_LivePatching

Seth Jennings (3):
kernel: add TAINT_LIVEPATCH
kernel: add support for live patching
kernel: add sysfs documentation for live patching

Documentation/ABI/testing/sysfs-kernel-livepatch | 44 +
Documentation/oops-tracing.txt | 2 +
Documentation/sysctl/kernel.txt | 1 +
MAINTAINERS | 13 +
arch/x86/Kconfig | 2 +
arch/x86/include/asm/livepatch.h | 38 +
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/livepatch.c | 83 ++
include/linux/kernel.h | 1 +
include/linux/livepatch.h | 68 ++
kernel/Makefile | 1 +
kernel/livepatch/Kconfig | 9 +
kernel/livepatch/Makefile | 3 +
kernel/livepatch/core.c | 999 +++++++++++++++++++++++
kernel/panic.c | 2 +
15 files changed, 1267 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-kernel-livepatch
create mode 100644 arch/x86/include/asm/livepatch.h
create mode 100644 arch/x86/kernel/livepatch.c
create mode 100644 include/linux/livepatch.h
create mode 100644 kernel/livepatch/Kconfig
create mode 100644 kernel/livepatch/Makefile
create mode 100644 kernel/livepatch/core.c

--
1.9.3


2014-11-17 01:30:40

by Seth Jennings

[permalink] [raw]
Subject: [PATCHv2 2/3] 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.

Signed-off-by: Seth Jennings <[email protected]>
---
MAINTAINERS | 12 +
arch/x86/Kconfig | 2 +
arch/x86/include/asm/livepatch.h | 38 ++
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/livepatch.c | 83 ++++
include/linux/livepatch.h | 68 +++
kernel/Makefile | 1 +
kernel/livepatch/Kconfig | 9 +
kernel/livepatch/Makefile | 3 +
kernel/livepatch/core.c | 999 +++++++++++++++++++++++++++++++++++++++
10 files changed, 1216 insertions(+)
create mode 100644 arch/x86/include/asm/livepatch.h
create mode 100644 arch/x86/kernel/livepatch.c
create mode 100644 include/linux/livepatch.h
create mode 100644 kernel/livepatch/Kconfig
create mode 100644 kernel/livepatch/Makefile
create mode 100644 kernel/livepatch/core.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 4861577..c7f49ae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5715,6 +5715,18 @@ F: Documentation/misc-devices/lis3lv02d
F: drivers/misc/lis3lv02d/
F: drivers/platform/x86/hp_accel.c

+LIVE PATCHING
+M: Josh Poimboeuf <[email protected]>
+M: Seth Jennings <[email protected]>
+M: Jiri Kosina <[email protected]>
+M: Vojtech Pavlik <[email protected]>
+S: Maintained
+F: kernel/livepatch/
+F: include/linux/livepatch.h
+F: arch/x86/include/asm/livepatch.h
+F: arch/x86/kernel/livepatch.c
+L: [email protected]
+
LLC (802.2)
M: Arnaldo Carvalho de Melo <[email protected]>
S: Maintained
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index ec21dfd..0cf27e8 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1991,6 +1991,8 @@ config CMDLINE_OVERRIDE
This is used to work around broken boot loaders. This should
be set to 'N' under normal conditions.

+source "kernel/livepatch/Kconfig"
+
endmenu

config ARCH_ENABLE_MEMORY_HOTPLUG
diff --git a/arch/x86/include/asm/livepatch.h b/arch/x86/include/asm/livepatch.h
new file mode 100644
index 0000000..c5fab45
--- /dev/null
+++ b/arch/x86/include/asm/livepatch.h
@@ -0,0 +1,38 @@
+/*
+ * livepatch.h - x86-specific Live Kernel Patching Core
+ *
+ * Copyright (C) 2014 Seth Jennings <[email protected]>
+ *
+ * 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/>.
+ */
+
+#ifndef _ASM_X86_LIVEPATCH_H
+#define _ASM_X86_LIVEPATCH_H
+
+#include <linux/module.h>
+
+#ifdef CONFIG_LIVE_PATCHING
+extern int lpc_write_module_reloc(struct module *mod, unsigned long type,
+ unsigned long loc, unsigned long value);
+
+#else
+static int lpc_write_module_reloc(struct module *mod, unsigned long type,
+ unsigned long loc, unsigned long value);
+{
+ pr_err("Kernel does not support live patching\n");
+ return -ENOSYS;
+}
+#endif
+
+#endif /* _ASM_X86_LIVEPATCH_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 5d4502c..316b34e 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_X86_MPPARSE) += mpparse.o
obj-y += apic/
obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+obj-$(CONFIG_LIVE_PATCHING) += livepatch.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_X86_TSC) += trace_clock.o
diff --git a/arch/x86/kernel/livepatch.c b/arch/x86/kernel/livepatch.c
new file mode 100644
index 0000000..56813ae
--- /dev/null
+++ b/arch/x86/kernel/livepatch.c
@@ -0,0 +1,83 @@
+/*
+ * livepatch.c - x86-specific Live Kernel Patching Core
+ *
+ * Copyright (C) 2014 Seth Jennings <[email protected]>
+ *
+ * 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/>.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/page_types.h>
+
+#ifdef CONFIG_X86_32
+int lpc_write_module_reloc(struct module *mod, unsigned long type,
+ unsigned long loc, unsigned long value)
+{
+ pr_err("Live patching not supported on 32-bit x86\n");
+ return -ENOSYS;
+}
+#else
+int lpc_write_module_reloc(struct module *mod, unsigned long type,
+ unsigned long loc, unsigned long value)
+{
+ int ret, readonly, numpages, size = 4;
+ unsigned long val;
+ unsigned long core = (unsigned long)mod->module_core;
+ unsigned long core_ro_size = mod->core_ro_size;
+ unsigned long core_size = mod->core_size;
+
+ switch (type) {
+ case R_X86_64_NONE:
+ return 0;
+ case R_X86_64_64:
+ val = value;
+ size = 8;
+ break;
+ case R_X86_64_32:
+ val = (u32)value;
+ break;
+ case R_X86_64_32S:
+ val = (s32)value;
+ break;
+ case R_X86_64_PC32:
+ val = (u32)(value - loc);
+ break;
+ default:
+ /* unsupported relocation type */
+ return -EINVAL;
+ }
+
+ if (loc >= core && loc < core + core_ro_size)
+ readonly = 1;
+ else if (loc >= core + core_ro_size && loc < core + core_size)
+ readonly = 0;
+ else
+ /* loc not in expected range */
+ return -EINVAL;
+
+ numpages = ((loc & PAGE_MASK) == ((loc + size) & PAGE_MASK)) ? 1 : 2;
+
+ if (readonly)
+ set_memory_rw(loc & PAGE_MASK, numpages);
+
+ ret = probe_kernel_write((void *)loc, &val, size);
+
+ if (readonly)
+ set_memory_ro(loc & PAGE_MASK, numpages);
+
+ return ret;
+}
+#endif
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
new file mode 100644
index 0000000..8b68fef
--- /dev/null
+++ b/include/linux/livepatch.h
@@ -0,0 +1,68 @@
+/*
+ * livepatch.h - Live Kernel Patching Core
+ *
+ * Copyright (C) 2014 Seth Jennings <[email protected]>
+ *
+ * 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/>.
+ */
+
+#ifndef _LINUX_LIVEPATCH_H_
+#define _LINUX_LIVEPATCH_H_
+
+#include <linux/module.h>
+
+/* TODO: add kernel-doc for structures once agreed upon */
+
+struct lp_func {
+ const char *old_name; /* function to be patched */
+ void *new_func; /* replacement function in patch module */
+ /*
+ * The old_addr field is optional and can be used to resolve
+ * duplicate symbol names in the vmlinux object. If this
+ * information is not present, the symbol is located by name
+ * with kallsyms. If the name is not unique and old_addr is
+ * not provided, the patch application fails as there is no
+ * way to resolve the ambiguity.
+ */
+ unsigned long old_addr;
+};
+
+struct lp_reloc {
+ unsigned long dest;
+ unsigned long src;
+ unsigned long type;
+ const char *name;
+ int addend;
+ int external;
+};
+
+struct lp_object {
+ const char *name; /* "vmlinux" or module name */
+ struct lp_func *funcs;
+ struct lp_reloc *relocs;
+};
+
+struct lp_patch {
+ struct module *mod; /* module containing the patch */
+ struct lp_object *objs;
+};
+
+int lp_register_patch(struct lp_patch *);
+int lp_unregister_patch(struct lp_patch *);
+int lp_enable_patch(struct lp_patch *);
+int lp_disable_patch(struct lp_patch *);
+
+#include <asm/livepatch.h>
+
+#endif /* _LINUX_LIVEPATCH_H_ */
diff --git a/kernel/Makefile b/kernel/Makefile
index a59481a..616994f 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -26,6 +26,7 @@ obj-y += power/
obj-y += printk/
obj-y += irq/
obj-y += rcu/
+obj-y += livepatch/

obj-$(CONFIG_CHECKPOINT_RESTORE) += kcmp.o
obj-$(CONFIG_FREEZER) += freezer.o
diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig
new file mode 100644
index 0000000..206bb63
--- /dev/null
+++ b/kernel/livepatch/Kconfig
@@ -0,0 +1,9 @@
+config LIVE_PATCHING
+ boolean "Live Kernel Patching"
+ depends on DYNAMIC_FTRACE_WITH_REGS && MODULES && SYSFS && KALLSYMS_ALL
+ help
+ Say Y here if you want to support live kernel patching.
+ This option has no runtime impact until a kernel "patch"
+ module uses the interface provided by this option to register
+ a patch, causing calls to patched functions to be redirected
+ to new function code contained in the patch module.
diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile
new file mode 100644
index 0000000..7c1f008
--- /dev/null
+++ b/kernel/livepatch/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LIVE_PATCHING) += livepatch.o
+
+livepatch-objs := core.o
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
new file mode 100644
index 0000000..e67c176
--- /dev/null
+++ b/kernel/livepatch/core.c
@@ -0,0 +1,999 @@
+/*
+ * core.c - Live Kernel Patching Core
+ *
+ * Copyright (C) 2014 Seth Jennings <[email protected]>
+ *
+ * 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/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/ftrace.h>
+#include <linux/list.h>
+#include <linux/kallsyms.h>
+#include <linux/livepatch.h>
+
+/*************************************
+ * Core structures
+ ************************************/
+
+/*
+ * lp_ structs vs lpc_ structs
+ *
+ * For each element (patch, object, func) in the live-patching code,
+ * there are two types with two different prefixes: lp_ and lpc_.
+ *
+ * Structures used by the live-patch modules to register with this core module
+ * are prefixed with lp_ (live patching). These structures are part of the
+ * registration API and are defined in livepatch.h. The structures used
+ * internally by this core module are prefixed with lpc_ (live patching core).
+ */
+
+static DEFINE_MUTEX(lpc_mutex);
+static LIST_HEAD(lpc_patches);
+
+enum lpc_state {
+ LPC_DISABLED,
+ LPC_ENABLED
+};
+
+struct lpc_func {
+ struct list_head list;
+ struct kobject kobj;
+ struct ftrace_ops fops;
+ enum lpc_state state;
+
+ const char *old_name;
+ unsigned long new_addr;
+ unsigned long old_addr;
+};
+
+struct lpc_object {
+ struct list_head list;
+ struct kobject kobj;
+ struct module *mod; /* module associated with object */
+ enum lpc_state state;
+
+ const char *name;
+ struct list_head funcs;
+ struct lp_reloc *relocs;
+};
+
+struct lpc_patch {
+ struct list_head list;
+ struct kobject kobj;
+ struct lp_patch *userpatch; /* for correlation during unregister */
+ enum lpc_state state;
+
+ struct module *mod;
+ struct list_head objs;
+};
+
+/*******************************************
+ * Helpers
+ *******************************************/
+
+/* sets obj->mod if object is not vmlinux and module is found */
+static bool lpc_find_object_module(struct lpc_object *obj)
+{
+ if (!strcmp(obj->name, "vmlinux"))
+ return 1;
+
+ mutex_lock(&module_mutex);
+ /*
+ * We don't need to take a reference on the module here because we have
+ * the lpc_mutex, which is also taken by the module notifier. This
+ * prevents any module from unloading until we release the lpc_mutex.
+ */
+ obj->mod = find_module(obj->name);
+ mutex_unlock(&module_mutex);
+
+ return !!obj->mod;
+}
+
+/************************************
+ * kallsyms
+ ***********************************/
+
+struct lpc_find_arg {
+ const char *objname;
+ const char *name;
+ unsigned long addr;
+ /*
+ * If count == 0, the symbol was not found. If count == 1, a unique
+ * match was found and addr is set. If count > 1, there is
+ * unresolvable ambiguity among "count" number of symbols with the same
+ * name in the same object.
+ */
+ unsigned long count;
+};
+
+static int lpc_find_callback(void *data, const char *name,
+ struct module *mod, unsigned long addr)
+{
+ struct lpc_find_arg *args = data;
+
+ if ((mod && !args->objname) || (!mod && args->objname))
+ return 0;
+
+ if (strcmp(args->name, name))
+ return 0;
+
+ if (args->objname && strcmp(args->objname, mod->name))
+ return 0;
+
+ /*
+ * args->addr might be overwritten if another match is found
+ * but lpc_find_symbol() handles this and only returns the
+ * addr if count == 1.
+ */
+ args->addr = addr;
+ args->count++;
+
+ return 0;
+}
+
+static int lpc_find_symbol(const char *objname, const char *name,
+ unsigned long *addr)
+{
+ struct lpc_find_arg args = {
+ .objname = objname,
+ .name = name,
+ .addr = 0,
+ .count = 0
+ };
+
+ if (objname && !strcmp(objname, "vmlinux"))
+ args.objname = NULL;
+
+ kallsyms_on_each_symbol(lpc_find_callback, &args);
+
+ if (args.count == 0)
+ pr_err("symbol '%s' not found in symbol table\n", name);
+ else if (args.count > 1)
+ pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n",
+ args.count, name, objname);
+ else {
+ *addr = args.addr;
+ return 0;
+ }
+
+ *addr = 0;
+ return -EINVAL;
+}
+
+struct lpc_verify_args {
+ const char *name;
+ const unsigned long addr;
+};
+
+static int lpc_verify_callback(void *data, const char *name,
+ struct module *mod, unsigned long addr)
+{
+ struct lpc_verify_args *args = data;
+
+ if (!mod &&
+ !strcmp(args->name, name) &&
+ args->addr == addr)
+ return 1;
+ return 0;
+}
+
+static int lpc_verify_vmlinux_symbol(const char *name, unsigned long addr)
+{
+ struct lpc_verify_args args = {
+ .name = name,
+ .addr = addr,
+ };
+
+ if (kallsyms_on_each_symbol(lpc_verify_callback, &args))
+ return 0;
+ pr_err("symbol '%s' not found at specified address 0x%016lx, kernel mismatch?",
+ name, addr);
+ return -EINVAL;
+}
+
+static int lpc_find_verify_func_addr(struct lpc_func *func, const char *objname)
+{
+ int ret;
+
+#if defined(CONFIG_RANDOMIZE_BASE)
+ /* KASLR is enabled, disregard old_addr from user */
+ func->old_addr = 0;
+#endif
+
+ if (func->old_addr && strcmp(objname, "vmlinux")) {
+ pr_err("old address specified for module symbol\n");
+ return -EINVAL;
+ }
+
+ if (func->old_addr)
+ ret = lpc_verify_vmlinux_symbol(func->old_name,
+ func->old_addr);
+ else
+ ret = lpc_find_symbol(objname, func->old_name,
+ &func->old_addr);
+
+ return ret;
+}
+
+/****************************************
+ * dynamic relocations (load-time linker)
+ ****************************************/
+
+/*
+ * external symbols are located outside the parent object (where the parent
+ * object is either vmlinux or the kmod being patched).
+ */
+static int lpc_find_external_symbol(struct module *pmod, const char *name,
+ unsigned long *addr)
+{
+ const struct kernel_symbol *sym;
+
+ /* first, check if it's an exported symbol */
+ preempt_disable();
+ sym = find_symbol(name, NULL, NULL, true, true);
+ preempt_enable();
+ if (sym) {
+ *addr = sym->value;
+ return 0;
+ }
+
+ /* otherwise check if it's in another .o within the patch module */
+ return lpc_find_symbol(pmod->name, name, addr);
+}
+
+static int lpc_write_object_relocations(struct module *pmod,
+ struct lpc_object *obj)
+{
+ int ret;
+ struct lp_reloc *reloc;
+
+ for (reloc = obj->relocs; reloc->name; reloc++) {
+ if (!strcmp(obj->name, "vmlinux")) {
+ ret = lpc_verify_vmlinux_symbol(reloc->name,
+ reloc->src);
+ if (ret)
+ return ret;
+ } else {
+ /* module, reloc->src needs to be discovered */
+ if (reloc->external)
+ ret = lpc_find_external_symbol(pmod,
+ reloc->name,
+ &reloc->src);
+ else
+ ret = lpc_find_symbol(obj->mod->name,
+ reloc->name,
+ &reloc->src);
+ if (ret)
+ return ret;
+ }
+ ret = lpc_write_module_reloc(pmod, reloc->type, reloc->dest,
+ reloc->src + reloc->addend);
+ if (ret) {
+ pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n",
+ reloc->name, reloc->src, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/***********************************
+ * ftrace registration
+ **********************************/
+
+static void lpc_ftrace_handler(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *ops, struct pt_regs *regs)
+{
+ struct lpc_func *func = ops->private;
+
+ regs->ip = func->new_addr;
+}
+
+static int lpc_enable_func(struct lpc_func *func)
+{
+ int ret;
+
+ if (WARN_ON(!func->old_addr || func->state != LPC_DISABLED))
+ return -EINVAL;
+
+ ret = ftrace_set_filter_ip(&func->fops, func->old_addr, 0, 0);
+ if (ret) {
+ pr_err("failed to set ftrace filter for function '%s' (%d)\n",
+ func->old_name, ret);
+ return ret;
+ }
+ ret = register_ftrace_function(&func->fops);
+ if (ret) {
+ pr_err("failed to register ftrace handler for function '%s' (%d)\n",
+ func->old_name, ret);
+ ftrace_set_filter_ip(&func->fops, func->old_addr, 1, 0);
+ } else
+ func->state = LPC_ENABLED;
+
+ return ret;
+}
+
+static int lpc_disable_func(struct lpc_func *func)
+{
+ int ret;
+
+ if (WARN_ON(func->state != LPC_ENABLED))
+ return -EINVAL;
+
+ if (!func->old_addr)
+ /* parent object is not loaded */
+ return 0;
+ ret = unregister_ftrace_function(&func->fops);
+ if (ret) {
+ pr_err("failed to unregister ftrace handler for function '%s' (%d)\n",
+ func->old_name, ret);
+ return ret;
+ }
+ ret = ftrace_set_filter_ip(&func->fops, func->old_addr, 1, 0);
+ if (ret)
+ pr_warn("function unregister succeeded but failed to clear the filter\n");
+ func->state = LPC_DISABLED;
+
+ return 0;
+}
+
+static int lpc_disable_object(struct lpc_object *obj)
+{
+ struct lpc_func *func;
+ int ret;
+
+ list_for_each_entry(func, &obj->funcs, list) {
+ if (func->state != LPC_ENABLED)
+ continue;
+ ret = lpc_disable_func(func);
+ if (ret)
+ return ret;
+ if (strcmp(obj->name, "vmlinux"))
+ func->old_addr = 0;
+ }
+ obj->state = LPC_DISABLED;
+
+ return 0;
+}
+
+static int lpc_enable_object(struct module *pmod, struct lpc_object *obj)
+{
+ struct lpc_func *func;
+ int ret;
+
+ if (WARN_ON(!obj->mod && strcmp(obj->name, "vmlinux")))
+ return -EINVAL;
+
+ if (obj->relocs) {
+ ret = lpc_write_object_relocations(pmod, obj);
+ if (ret)
+ goto unregister;
+ }
+ list_for_each_entry(func, &obj->funcs, list) {
+ ret = lpc_find_verify_func_addr(func, obj->name);
+ if (ret)
+ goto unregister;
+
+ ret = lpc_enable_func(func);
+ if (ret)
+ goto unregister;
+ }
+ obj->state = LPC_ENABLED;
+
+ return 0;
+unregister:
+ WARN_ON(lpc_disable_object(obj));
+ return ret;
+}
+
+/******************************
+ * enable/disable
+ ******************************/
+
+static struct lpc_patch *lpc_find_patch(struct lp_patch *userpatch)
+{
+ struct lpc_patch *patch;
+
+ list_for_each_entry(patch, &lpc_patches, list)
+ if (patch->userpatch == userpatch)
+ return patch;
+
+ return NULL;
+}
+
+static int lpc_disable_patch(struct lpc_patch *patch)
+{
+ struct lpc_object *obj;
+ int ret;
+
+ pr_notice("disabling patch '%s'\n", patch->mod->name);
+
+ list_for_each_entry(obj, &patch->objs, list) {
+ if (obj->state != LPC_ENABLED)
+ continue;
+ ret = lpc_disable_object(obj);
+ if (ret)
+ return ret;
+ }
+ patch->state = LPC_DISABLED;
+
+ return 0;
+}
+
+/**
+ * lp_disable_patch() - disables a registered patch
+ * @userpatch: The registered, enabled patch to be disabled
+ *
+ * Unregisters the patched functions from ftrace.
+ *
+ * Return: 0 on success, otherwise error
+ */
+int lp_disable_patch(struct lp_patch *userpatch)
+{
+ struct lpc_patch *patch;
+ int ret;
+
+ mutex_lock(&lpc_mutex);
+ patch = lpc_find_patch(userpatch);
+ if (!patch) {
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = lpc_disable_patch(patch);
+out:
+ mutex_unlock(&lpc_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lp_disable_patch);
+
+static int lpc_enable_patch(struct lpc_patch *patch)
+{
+ struct lpc_object *obj;
+ int ret;
+
+ if (WARN_ON(patch->state != LPC_DISABLED))
+ return -EINVAL;
+
+ pr_notice_once("tainting kernel with TAINT_LIVEPATCH\n");
+ add_taint(TAINT_LIVEPATCH, LOCKDEP_STILL_OK);
+
+ pr_notice("enabling patch '%s'\n", patch->mod->name);
+
+ list_for_each_entry(obj, &patch->objs, list) {
+ if (!lpc_find_object_module(obj))
+ continue;
+ ret = lpc_enable_object(patch->mod, obj);
+ if (ret)
+ goto unregister;
+ }
+ patch->state = LPC_ENABLED;
+ return 0;
+
+unregister:
+ WARN_ON(lpc_disable_patch(patch));
+ return ret;
+}
+
+/**
+ * lp_enable_patch() - enables a registered patch
+ * @userpatch: The registered, disabled patch to be enabled
+ *
+ * Performs the needed symbol lookups and code relocations,
+ * then registers the patched functions with ftrace.
+ *
+ * Return: 0 on success, otherwise error
+ */
+int lp_enable_patch(struct lp_patch *userpatch)
+{
+ struct lpc_patch *patch;
+ int ret;
+
+ mutex_lock(&lpc_mutex);
+ patch = lpc_find_patch(userpatch);
+ if (!patch) {
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = lpc_enable_patch(patch);
+out:
+ mutex_unlock(&lpc_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lp_enable_patch);
+
+/******************************
+ * module notifier
+ *****************************/
+
+static void lpc_module_notify_coming(struct module *pmod,
+ struct lpc_object *obj)
+{
+ struct module *mod = obj->mod;
+ int ret;
+
+ pr_notice("applying patch '%s' to loading module '%s'\n",
+ mod->name, pmod->name);
+ obj->mod = mod;
+ ret = lpc_enable_object(pmod, obj);
+ if (ret)
+ pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
+ pmod->name, mod->name, ret);
+}
+
+static void lpc_module_notify_going(struct module *pmod,
+ struct lpc_object *obj)
+{
+ struct module *mod = obj->mod;
+ int ret;
+
+ pr_notice("reverting patch '%s' on unloading module '%s'\n",
+ pmod->name, mod->name);
+ ret = lpc_disable_object(obj);
+ if (ret)
+ pr_warn("failed to revert patch '%s' on module '%s' (%d)\n",
+ pmod->name, mod->name, ret);
+ obj->mod = NULL;
+}
+
+static int lpc_module_notify(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct module *mod = data;
+ struct lpc_patch *patch;
+ struct lpc_object *obj;
+
+ mutex_lock(&lpc_mutex);
+
+ if (action != MODULE_STATE_COMING && action != MODULE_STATE_GOING)
+ goto out;
+
+ list_for_each_entry(patch, &lpc_patches, list) {
+ if (patch->state == LPC_DISABLED)
+ continue;
+ list_for_each_entry(obj, &patch->objs, list) {
+ if (strcmp(obj->name, mod->name))
+ continue;
+ if (action == MODULE_STATE_COMING) {
+ obj->mod = mod;
+ lpc_module_notify_coming(patch->mod, obj);
+ } else /* MODULE_STATE_GOING */
+ lpc_module_notify_going(patch->mod, obj);
+ break;
+ }
+ }
+out:
+ mutex_unlock(&lpc_mutex);
+ return 0;
+}
+
+static struct notifier_block lpc_module_nb = {
+ .notifier_call = lpc_module_notify,
+ .priority = INT_MIN+1, /* called late but before ftrace notifier */
+};
+
+/********************************************
+ * Sysfs Interface
+ *******************************************/
+/*
+ * /sys/kernel/livepatch
+ * /sys/kernel/livepatch/<patch>
+ * /sys/kernel/livepatch/<patch>/enabled
+ * /sys/kernel/livepatch/<patch>/<object>
+ * /sys/kernel/livepatch/<patch>/<object>/<func>
+ */
+
+static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lpc_patch *patch;
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return -EINVAL;
+
+ if (val != LPC_DISABLED && val != LPC_ENABLED)
+ return -EINVAL;
+
+ patch = container_of(kobj, struct lpc_patch, kobj);
+
+ mutex_lock(&lpc_mutex);
+ if (val == patch->state) {
+ /* already in requested state */
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (val == LPC_ENABLED) {
+ ret = lpc_enable_patch(patch);
+ if (ret)
+ goto err;
+ } else {
+ ret = lpc_disable_patch(patch);
+ if (ret)
+ goto err;
+ }
+ mutex_unlock(&lpc_mutex);
+ return count;
+err:
+ mutex_unlock(&lpc_mutex);
+ return ret;
+}
+
+static ssize_t enabled_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct lpc_patch *patch;
+
+ patch = container_of(kobj, struct lpc_patch, kobj);
+ return snprintf(buf, PAGE_SIZE-1, "%d\n", patch->state);
+}
+
+static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled);
+static struct attribute *lpc_patch_attrs[] = {
+ &enabled_kobj_attr.attr,
+ NULL
+};
+
+static struct kobject *lpc_root_kobj;
+
+static int lpc_create_root_kobj(void)
+{
+ lpc_root_kobj =
+ kobject_create_and_add("livepatch", kernel_kobj);
+ if (!lpc_root_kobj)
+ return -ENOMEM;
+ return 0;
+}
+
+static void lpc_remove_root_kobj(void)
+{
+ kobject_put(lpc_root_kobj);
+}
+
+static void lpc_kobj_release_patch(struct kobject *kobj)
+{
+ struct lpc_patch *patch;
+
+ patch = container_of(kobj, struct lpc_patch, kobj);
+ if (!list_empty(&patch->list))
+ list_del(&patch->list);
+ kfree(patch);
+}
+
+static struct kobj_type lpc_ktype_patch = {
+ .release = lpc_kobj_release_patch,
+ .sysfs_ops = &kobj_sysfs_ops,
+ .default_attrs = lpc_patch_attrs
+};
+
+static void lpc_kobj_release_object(struct kobject *kobj)
+{
+ struct lpc_object *obj;
+
+ obj = container_of(kobj, struct lpc_object, kobj);
+ if (!list_empty(&obj->list))
+ list_del(&obj->list);
+ kfree(obj);
+}
+
+static struct kobj_type lpc_ktype_object = {
+ .release = lpc_kobj_release_object,
+ .sysfs_ops = &kobj_sysfs_ops,
+};
+
+static void lpc_kobj_release_func(struct kobject *kobj)
+{
+ struct lpc_func *func;
+
+ func = container_of(kobj, struct lpc_func, kobj);
+ if (!list_empty(&func->list))
+ list_del(&func->list);
+ kfree(func);
+}
+
+static struct kobj_type lpc_ktype_func = {
+ .release = lpc_kobj_release_func,
+ .sysfs_ops = &kobj_sysfs_ops,
+};
+
+/*********************************
+ * structure allocation
+ ********************************/
+
+static void lpc_free_funcs(struct lpc_object *obj)
+{
+ struct lpc_func *func, *funcsafe;
+
+ list_for_each_entry_safe(func, funcsafe, &obj->funcs, list)
+ kobject_put(&func->kobj);
+}
+
+static void lpc_free_objects(struct lpc_patch *patch)
+{
+ struct lpc_object *obj, *objsafe;
+
+ list_for_each_entry_safe(obj, objsafe, &patch->objs, list) {
+ lpc_free_funcs(obj);
+ kobject_put(&obj->kobj);
+ }
+}
+
+static void lpc_free_patch(struct lpc_patch *patch)
+{
+ lpc_free_objects(patch);
+ kobject_put(&patch->kobj);
+}
+
+static struct lpc_func *lpc_create_func(struct kobject *root,
+ struct lp_func *userfunc)
+{
+ struct lpc_func *func;
+ struct ftrace_ops *ops;
+ int ret;
+
+ /* alloc */
+ func = kzalloc(sizeof(*func), GFP_KERNEL);
+ if (!func)
+ return NULL;
+
+ /* init */
+ INIT_LIST_HEAD(&func->list);
+ func->old_name = userfunc->old_name;
+ func->new_addr = (unsigned long)userfunc->new_func;
+ func->old_addr = userfunc->old_addr;
+ func->state = LPC_DISABLED;
+ ops = &func->fops;
+ ops->private = func;
+ ops->func = lpc_ftrace_handler;
+ ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;
+
+ /* sysfs */
+ ret = kobject_init_and_add(&func->kobj, &lpc_ktype_func,
+ root, func->old_name);
+ if (ret) {
+ kfree(func);
+ return NULL;
+ }
+
+ return func;
+}
+
+static int lpc_create_funcs(struct lpc_object *obj,
+ struct lp_func *userfuncs)
+{
+ struct lp_func *userfunc;
+ struct lpc_func *func;
+
+ if (!userfuncs)
+ return -EINVAL;
+
+ for (userfunc = userfuncs; userfunc->old_name; userfunc++) {
+ func = lpc_create_func(&obj->kobj, userfunc);
+ if (!func)
+ goto free;
+ list_add(&func->list, &obj->funcs);
+ }
+ return 0;
+free:
+ lpc_free_funcs(obj);
+ return -ENOMEM;
+}
+
+static struct lpc_object *lpc_create_object(struct kobject *root,
+ struct lp_object *userobj)
+{
+ struct lpc_object *obj;
+ int ret;
+
+ /* alloc */
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return NULL;
+
+ /* init */
+ INIT_LIST_HEAD(&obj->list);
+ obj->name = userobj->name;
+ obj->relocs = userobj->relocs;
+ obj->state = LPC_DISABLED;
+ /* obj->mod set by lpc_object_module_get() */
+ INIT_LIST_HEAD(&obj->funcs);
+
+ /* sysfs */
+ ret = kobject_init_and_add(&obj->kobj, &lpc_ktype_object,
+ root, obj->name);
+ if (ret) {
+ kfree(obj);
+ return NULL;
+ }
+
+ /* create functions */
+ ret = lpc_create_funcs(obj, userobj->funcs);
+ if (ret) {
+ kobject_put(&obj->kobj);
+ return NULL;
+ }
+
+ return obj;
+}
+
+static int lpc_create_objects(struct lpc_patch *patch,
+ struct lp_object *userobjs)
+{
+ struct lp_object *userobj;
+ struct lpc_object *obj;
+
+ if (!userobjs)
+ return -EINVAL;
+
+ for (userobj = userobjs; userobj->name; userobj++) {
+ obj = lpc_create_object(&patch->kobj, userobj);
+ if (!obj)
+ goto free;
+ list_add(&obj->list, &patch->objs);
+ }
+ return 0;
+free:
+ lpc_free_objects(patch);
+ return -ENOMEM;
+}
+
+static int lpc_create_patch(struct lp_patch *userpatch)
+{
+ struct lpc_patch *patch;
+ int ret;
+
+ /* alloc */
+ patch = kzalloc(sizeof(*patch), GFP_KERNEL);
+ if (!patch)
+ return -ENOMEM;
+
+ /* init */
+ INIT_LIST_HEAD(&patch->list);
+ patch->userpatch = userpatch;
+ patch->mod = userpatch->mod;
+ patch->state = LPC_DISABLED;
+ INIT_LIST_HEAD(&patch->objs);
+
+ /* sysfs */
+ ret = kobject_init_and_add(&patch->kobj, &lpc_ktype_patch,
+ lpc_root_kobj, patch->mod->name);
+ if (ret) {
+ kfree(patch);
+ return ret;
+ }
+
+ /* create objects */
+ ret = lpc_create_objects(patch, userpatch->objs);
+ if (ret) {
+ kobject_put(&patch->kobj);
+ return ret;
+ }
+
+ /* add to global list of patches */
+ mutex_lock(&lpc_mutex);
+ list_add(&patch->list, &lpc_patches);
+ mutex_unlock(&lpc_mutex);
+
+ return 0;
+}
+
+/************************************
+ * register/unregister
+ ***********************************/
+
+/**
+ * lp_register_patch() - registers a patch
+ * @userpatch: Patch to be registered
+ *
+ * Allocates the data structure associated with the patch and
+ * creates the sysfs interface.
+ *
+ * Return: 0 on success, otherwise error
+ */
+int lp_register_patch(struct lp_patch *userpatch)
+{
+ int ret;
+
+ if (!userpatch || !userpatch->mod || !userpatch->objs)
+ return -EINVAL;
+
+ /*
+ * A reference is taken on the patch module to prevent it from being
+ * unloaded. Right now, we don't allow patch modules to unload since
+ * there is currently no method to determine if a thread is still
+ * running in the patched code contained in the patch module once
+ * the ftrace registration is successful.
+ */
+ if (!try_module_get(userpatch->mod))
+ return -ENODEV;
+
+ ret = lpc_create_patch(userpatch);
+ if (ret)
+ module_put(userpatch->mod);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lp_register_patch);
+
+/**
+ * lp_unregister_patch() - unregisters a patch
+ * @userpatch: Disabled patch to be unregistered
+ *
+ * Frees the data structures and removes the sysfs interface.
+ *
+ * Return: 0 on success, otherwise error
+ */
+int lp_unregister_patch(struct lp_patch *userpatch)
+{
+ struct lpc_patch *patch;
+ int ret = 0;
+
+ mutex_lock(&lpc_mutex);
+ patch = lpc_find_patch(userpatch);
+ if (!patch) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (patch->state == LPC_ENABLED) {
+ ret = -EINVAL;
+ goto out;
+ }
+ lpc_free_patch(patch);
+out:
+ mutex_unlock(&lpc_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(lp_unregister_patch);
+
+/************************************
+ * entry/exit
+ ************************************/
+
+static int lpc_init(void)
+{
+ int ret;
+
+ ret = register_module_notifier(&lpc_module_nb);
+ if (ret)
+ return ret;
+
+ ret = lpc_create_root_kobj();
+ if (ret)
+ goto unregister;
+
+ return 0;
+unregister:
+ unregister_module_notifier(&lpc_module_nb);
+ return ret;
+}
+
+static void lpc_exit(void)
+{
+ lpc_remove_root_kobj();
+ unregister_module_notifier(&lpc_module_nb);
+}
+
+module_init(lpc_init);
+module_exit(lpc_exit);
+MODULE_DESCRIPTION("Live Kernel Patching Core");
+MODULE_LICENSE("GPL");
--
1.9.3

2014-11-17 01:30:31

by Seth Jennings

[permalink] [raw]
Subject: [PATCHv2 1/3] kernel: add TAINT_LIVEPATCH

This adds a new taint flag to indicate when the kernel or a kernel
module has been live patched. This will provide a clean indication in
bug reports that live patching was used.

Additionally, if the crash occurs in a live patched function, the live
patch module will appear beside the patched function in the backtrace.

Signed-off-by: Seth Jennings <[email protected]>
---
Documentation/oops-tracing.txt | 2 ++
Documentation/sysctl/kernel.txt | 1 +
include/linux/kernel.h | 1 +
kernel/panic.c | 2 ++
4 files changed, 6 insertions(+)

diff --git a/Documentation/oops-tracing.txt b/Documentation/oops-tracing.txt
index beefb9f..f3ac05c 100644
--- a/Documentation/oops-tracing.txt
+++ b/Documentation/oops-tracing.txt
@@ -270,6 +270,8 @@ characters, each representing a particular tainted value.

15: 'L' if a soft lockup has previously occurred on the system.

+ 16: 'K' if the kernel has been live patched.
+
The primary reason for the 'Tainted: ' string is to tell kernel
debuggers if this is a clean kernel or if anything unusual has
occurred. Tainting is permanent: even if an offending module is
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 75511ef..83ab256 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -843,6 +843,7 @@ can be ORed together:
8192 - An unsigned module has been loaded in a kernel supporting module
signature.
16384 - A soft lockup has previously occurred on the system.
+32768 - The kernel has been live patched.

==============================================================

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 5449d2f..d03e3de 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -471,6 +471,7 @@ extern enum system_states {
#define TAINT_OOT_MODULE 12
#define TAINT_UNSIGNED_MODULE 13
#define TAINT_SOFTLOCKUP 14
+#define TAINT_LIVEPATCH 15

extern const char hex_asc[];
#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
diff --git a/kernel/panic.c b/kernel/panic.c
index 4d8d6f9..8136ad7 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -226,6 +226,7 @@ static const struct tnt tnts[] = {
{ TAINT_OOT_MODULE, 'O', ' ' },
{ TAINT_UNSIGNED_MODULE, 'E', ' ' },
{ TAINT_SOFTLOCKUP, 'L', ' ' },
+ { TAINT_LIVEPATCH, 'K', ' ' },
};

/**
@@ -246,6 +247,7 @@ static const struct tnt tnts[] = {
* 'O' - Out-of-tree module has been loaded.
* 'E' - Unsigned module has been loaded.
* 'L' - A soft lockup has previously occurred.
+ * 'K' - Kernel has been live patched.
*
* The string is overwritten by the next call to print_tainted().
*/
--
1.9.3

2014-11-17 01:31:07

by Seth Jennings

[permalink] [raw]
Subject: [PATCHv2 3/3] kernel: add sysfs documentation for live patching

Adds sysfs interface documentation to Documentation/ABI/testing/

Signed-off-by: Seth Jennings <[email protected]>
---
Documentation/ABI/testing/sysfs-kernel-livepatch | 44 ++++++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 45 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-kernel-livepatch

diff --git a/Documentation/ABI/testing/sysfs-kernel-livepatch b/Documentation/ABI/testing/sysfs-kernel-livepatch
new file mode 100644
index 0000000..5bf42a8
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-livepatch
@@ -0,0 +1,44 @@
+What: /sys/kernel/livepatch
+Date: Nov 2014
+KernelVersion: 3.19.0
+Contact: [email protected]
+Description:
+ Interface for kernel live patching
+
+ The /sys/kernel/livepatch directory contains subdirectories for
+ each loaded live patch module.
+
+What: /sys/kernel/livepatch/<patch>
+Date: Nov 2014
+KernelVersion: 3.19.0
+Contact: [email protected]
+Description:
+ The patch directory contains subdirectories for each kernel
+ object (vmlinux or a module) in which it patched functions.
+
+What: /sys/kernel/livepatch/<patch>/enabled
+Date: Nov 2014
+KernelVersion: 3.19.0
+Contact: [email protected]
+Description:
+ A writable attribute that indicates whether the patched
+ code is currently applied. Writing 0 will disable the patch
+ while writing 1 will re-enable the patch.
+
+What: /sys/kernel/livepatch/<patch>/<object>
+Date: Nov 2014
+KernelVersion: 3.19.0
+Contact: [email protected]
+Description:
+ The object directory contains subdirectories for each function
+ that is patched within the object.
+
+What: /sys/kernel/livepatch/<patch>/<object>/<function>
+Date: Nov 2014
+KernelVersion: 3.19.0
+Contact: [email protected]
+Description:
+ The function directory contains attributes regarding the
+ properties and state of the patched function.
+
+ There are currently no such attributes.
diff --git a/MAINTAINERS b/MAINTAINERS
index c7f49ae..7985293 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5725,6 +5725,7 @@ F: kernel/livepatch/
F: include/linux/livepatch.h
F: arch/x86/include/asm/livepatch.h
F: arch/x86/kernel/livepatch.c
+F: Documentation/ABI/testing/sysfs-kernel-livepatch
L: [email protected]

LLC (802.2)
--
1.9.3

Subject: Re: [PATCHv2 0/3] Kernel Live Patching

Hi Seth,

(2014/11/17 10:29), Seth Jennings wrote:
> Changelog:
>
> Thanks for all the feedback!
>
> changes in v2:
> - rebase to next-20141113
> - add copyright/license block to livepatch.h
> - add _LINUX prefix to header defines
> - replace semaphore with mutex
> - add LPC_ prefix to state enum
> - convert BUGs to WARNs and handle properly
> - change Kconfig default to n
> - remove [old|new] attrs from function sysfs dir (KASLR leak, no use)
> - disregard user provided old_addr if kernel uses KASLR
> - s/out/err for error path labels
> - s/unregister/disable for uniform terminology
> - s/lp/lpc for module notifier elements

Hmm, btw, "LP" and "LPC" remind me line-printer and LPC bus :(
Can we use LKP (Live Kernel Patching) or KLP (Kernel Live Patching) instead ?

> - replace module ref'ing with unload notifier + mutex protection
> - adjust notifier priority to run before ftrace
> - make LIVE_PATCHING boolean (about to depend on arch stuff)

For better handling x86-32, we'd better introduce ARCH_HAVE_LIVE_PATCHING and
avoid enabling LIVE_PATCHING on x86_32, then we can simplify arch/x86/kernel/livepatch.c.

Thank you,

> - move x86-specific reloc code to arch/x86
> - s/dynrela/reloc/
> - add live patching sysfs documentation
> - add API function kernel-doc
> - TODO: kernel-doc for API structs once agreed upon
>
> Summary:
>
> This patchset 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 [1] and kGraft [2] and can
> accept patches built using either method. This solution was discussed in the
> Live Patching Mini-conference at LPC 2014 [3].
>
> The model consists of a live patching "core" that provides an interface for
> other "patch" kernel modules to register patches with the core.
>
> Patch modules contain the new function code and create an lp_patch structure
> containing the required data about what functions to patch, where the new code
> for each patched function resides, and in which kernel object (vmlinux or
> module) the function to be patch resides. The patch module then invokes the
> lp_register_patch() function to register with the core, then lp_enable_patch()
> to have the core redirect the execution paths using ftrace.
>
> An example patch module can be found here:
> https://github.com/spartacus06/livepatch/blob/master/patch/patch.c
>
> The live patching core creates a sysfs hierarchy for user-level access to live
> patching information. The hierarchy is structured like this:
>
> /sys/kernel/livepatch
> /sys/kernel/livepatch/<patch>
> /sys/kernel/livepatch/<patch>/enabled
> /sys/kernel/livepatch/<patch>/<object>
> /sys/kernel/livepatch/<patch>/<object>/<func>
>
> The old function is located using one of two methods: it is either provided by
> the patch module (only possible for a function in vmlinux) or kallsyms lookup.
> Symbol ambiguity results in a failure.
>
> The core takes a reference on the patch module itself to keep it from
> unloading. This is because, without a mechanism to ensure that no thread is
> currently executing in the patched function, we can not determine whether it is
> safe to unload the patch module. For this reason, unloading patch modules is
> currently not allowed.
>
> Disabling patches can be done using the "enabled" attribute of the patch:
>
> echo 0 > /sys/kernel/livepatch/<patch>/enabled
>
> If a patch module contains a patch for a module that is not currently loaded,
> there is nothing to patch so the core does nothing for that patch object.
> However, the core registers a module notifier that looks for COMING events so
> that if the module is ever loaded, it is immediately patched. If a module with
> patch code is removed, the notifier looks for GOING events and disables any
> patched functions for that object before it unloads. The notifier has a higher
> priority than that of the ftrace notifier so that it runs before the ftrace
> notifier for GOING events and we can cleanly unregister from ftrace.
>
> kpatch and kGraft each have their own mechanisms for ensuring system
> consistency during the patching process. 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
> for now.
>
> [1] https://github.com/dynup/kpatch
> [2] https://git.kernel.org/cgit/linux/kernel/git/jirislaby/kgraft.git/
> [3] https://etherpad.fr/p/LPC2014_LivePatching
>
> Seth Jennings (3):
> kernel: add TAINT_LIVEPATCH
> kernel: add support for live patching
> kernel: add sysfs documentation for live patching
>
> Documentation/ABI/testing/sysfs-kernel-livepatch | 44 +
> Documentation/oops-tracing.txt | 2 +
> Documentation/sysctl/kernel.txt | 1 +
> MAINTAINERS | 13 +
> arch/x86/Kconfig | 2 +
> arch/x86/include/asm/livepatch.h | 38 +
> arch/x86/kernel/Makefile | 1 +
> arch/x86/kernel/livepatch.c | 83 ++
> include/linux/kernel.h | 1 +
> include/linux/livepatch.h | 68 ++
> kernel/Makefile | 1 +
> kernel/livepatch/Kconfig | 9 +
> kernel/livepatch/Makefile | 3 +
> kernel/livepatch/core.c | 999 +++++++++++++++++++++++
> kernel/panic.c | 2 +
> 15 files changed, 1267 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-kernel-livepatch
> create mode 100644 arch/x86/include/asm/livepatch.h
> create mode 100644 arch/x86/kernel/livepatch.c
> create mode 100644 include/linux/livepatch.h
> create mode 100644 kernel/livepatch/Kconfig
> create mode 100644 kernel/livepatch/Makefile
> create mode 100644 kernel/livepatch/core.c
>


--
Masami HIRAMATSU
Software Platform Research Dept. Linux Technology Research Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: [email protected]

2014-11-17 13:17:10

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCHv2 0/3] Kernel Live Patching

On Mon, 17 Nov 2014 14:33:02 +0900
Masami Hiramatsu <[email protected]> wrote:


> > - s/lp/lpc for module notifier elements
>
> Hmm, btw, "LP" and "LPC" remind me line-printer and LPC bus :(

LPC and LP remind me of "Linux Plumbers Conference" J

-- Steve

2014-11-17 14:55:30

by Seth Jennings

[permalink] [raw]
Subject: Re: [PATCHv2 0/3] Kernel Live Patching

On Mon, Nov 17, 2014 at 02:33:02PM +0900, Masami Hiramatsu wrote:
> Hi Seth,
>
> (2014/11/17 10:29), Seth Jennings wrote:
> > Changelog:
> >
> > Thanks for all the feedback!
> >
> > changes in v2:
> > - rebase to next-20141113
> > - add copyright/license block to livepatch.h
> > - add _LINUX prefix to header defines
> > - replace semaphore with mutex
> > - add LPC_ prefix to state enum
> > - convert BUGs to WARNs and handle properly
> > - change Kconfig default to n
> > - remove [old|new] attrs from function sysfs dir (KASLR leak, no use)
> > - disregard user provided old_addr if kernel uses KASLR
> > - s/out/err for error path labels
> > - s/unregister/disable for uniform terminology
> > - s/lp/lpc for module notifier elements
>
> Hmm, btw, "LP" and "LPC" remind me line-printer and LPC bus :(
> Can we use LKP (Live Kernel Patching) or KLP (Kernel Live Patching) instead ?

Jiri S also mentioned this so I guess it is a common sentiment :) He
suggested "lip" but I think I like "klp" better? Jiri S sound good?

>
> > - replace module ref'ing with unload notifier + mutex protection
> > - adjust notifier priority to run before ftrace
> > - make LIVE_PATCHING boolean (about to depend on arch stuff)
>
> For better handling x86-32, we'd better introduce ARCH_HAVE_LIVE_PATCHING and
> avoid enabling LIVE_PATCHING on x86_32, then we can simplify arch/x86/kernel/livepatch.c.

Will do.

Thanks for the review!

Seth

>
> Thank you,
>
> > - move x86-specific reloc code to arch/x86
> > - s/dynrela/reloc/
> > - add live patching sysfs documentation
> > - add API function kernel-doc
> > - TODO: kernel-doc for API structs once agreed upon
> >
> > Summary:
> >
> > This patchset 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 [1] and kGraft [2] and can
> > accept patches built using either method. This solution was discussed in the
> > Live Patching Mini-conference at LPC 2014 [3].
> >
> > The model consists of a live patching "core" that provides an interface for
> > other "patch" kernel modules to register patches with the core.
> >
> > Patch modules contain the new function code and create an lp_patch structure
> > containing the required data about what functions to patch, where the new code
> > for each patched function resides, and in which kernel object (vmlinux or
> > module) the function to be patch resides. The patch module then invokes the
> > lp_register_patch() function to register with the core, then lp_enable_patch()
> > to have the core redirect the execution paths using ftrace.
> >
> > An example patch module can be found here:
> > https://github.com/spartacus06/livepatch/blob/master/patch/patch.c
> >
> > The live patching core creates a sysfs hierarchy for user-level access to live
> > patching information. The hierarchy is structured like this:
> >
> > /sys/kernel/livepatch
> > /sys/kernel/livepatch/<patch>
> > /sys/kernel/livepatch/<patch>/enabled
> > /sys/kernel/livepatch/<patch>/<object>
> > /sys/kernel/livepatch/<patch>/<object>/<func>
> >
> > The old function is located using one of two methods: it is either provided by
> > the patch module (only possible for a function in vmlinux) or kallsyms lookup.
> > Symbol ambiguity results in a failure.
> >
> > The core takes a reference on the patch module itself to keep it from
> > unloading. This is because, without a mechanism to ensure that no thread is
> > currently executing in the patched function, we can not determine whether it is
> > safe to unload the patch module. For this reason, unloading patch modules is
> > currently not allowed.
> >
> > Disabling patches can be done using the "enabled" attribute of the patch:
> >
> > echo 0 > /sys/kernel/livepatch/<patch>/enabled
> >
> > If a patch module contains a patch for a module that is not currently loaded,
> > there is nothing to patch so the core does nothing for that patch object.
> > However, the core registers a module notifier that looks for COMING events so
> > that if the module is ever loaded, it is immediately patched. If a module with
> > patch code is removed, the notifier looks for GOING events and disables any
> > patched functions for that object before it unloads. The notifier has a higher
> > priority than that of the ftrace notifier so that it runs before the ftrace
> > notifier for GOING events and we can cleanly unregister from ftrace.
> >
> > kpatch and kGraft each have their own mechanisms for ensuring system
> > consistency during the patching process. 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
> > for now.
> >
> > [1] https://github.com/dynup/kpatch
> > [2] https://git.kernel.org/cgit/linux/kernel/git/jirislaby/kgraft.git/
> > [3] https://etherpad.fr/p/LPC2014_LivePatching
> >
> > Seth Jennings (3):
> > kernel: add TAINT_LIVEPATCH
> > kernel: add support for live patching
> > kernel: add sysfs documentation for live patching
> >
> > Documentation/ABI/testing/sysfs-kernel-livepatch | 44 +
> > Documentation/oops-tracing.txt | 2 +
> > Documentation/sysctl/kernel.txt | 1 +
> > MAINTAINERS | 13 +
> > arch/x86/Kconfig | 2 +
> > arch/x86/include/asm/livepatch.h | 38 +
> > arch/x86/kernel/Makefile | 1 +
> > arch/x86/kernel/livepatch.c | 83 ++
> > include/linux/kernel.h | 1 +
> > include/linux/livepatch.h | 68 ++
> > kernel/Makefile | 1 +
> > kernel/livepatch/Kconfig | 9 +
> > kernel/livepatch/Makefile | 3 +
> > kernel/livepatch/core.c | 999 +++++++++++++++++++++++
> > kernel/panic.c | 2 +
> > 15 files changed, 1267 insertions(+)
> > create mode 100644 Documentation/ABI/testing/sysfs-kernel-livepatch
> > create mode 100644 arch/x86/include/asm/livepatch.h
> > create mode 100644 arch/x86/kernel/livepatch.c
> > create mode 100644 include/linux/livepatch.h
> > create mode 100644 kernel/livepatch/Kconfig
> > create mode 100644 kernel/livepatch/Makefile
> > create mode 100644 kernel/livepatch/core.c
> >
>
>
> --
> Masami HIRAMATSU
> Software Platform Research Dept. Linux Technology Research Center
> Hitachi, Ltd., Yokohama Research Laboratory
> E-mail: [email protected]
>
>

2014-11-17 18:46:00

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Sun, Nov 16, 2014 at 07:29:23PM -0600, Seth Jennings wrote:
> +#ifdef CONFIG_X86_32
> +int lpc_write_module_reloc(struct module *mod, unsigned long type,
> + unsigned long loc, unsigned long value)
> +{
> + pr_err("Live patching not supported on 32-bit x86\n");
> + return -ENOSYS;
> +}

Why not just prevent the code from being built on x86_32 instead of
putting this in the file?

thanks,

greg k-h

2014-11-17 18:50:27

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCHv2 3/3] kernel: add sysfs documentation for live patching

On Sun, Nov 16, 2014 at 07:29:24PM -0600, Seth Jennings wrote:
> Adds sysfs interface documentation to Documentation/ABI/testing/
>
> Signed-off-by: Seth Jennings <[email protected]>
> ---
> Documentation/ABI/testing/sysfs-kernel-livepatch | 44 ++++++++++++++++++++++++
> MAINTAINERS | 1 +
> 2 files changed, 45 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-kernel-livepatch

Looks good, thanks for adding the documentation.

greg k-h

2014-11-17 19:13:44

by Seth Jennings

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Mon, Nov 17, 2014 at 10:45:58AM -0800, Greg KH wrote:
> On Sun, Nov 16, 2014 at 07:29:23PM -0600, Seth Jennings wrote:
> > +#ifdef CONFIG_X86_32
> > +int lpc_write_module_reloc(struct module *mod, unsigned long type,
> > + unsigned long loc, unsigned long value)
> > +{
> > + pr_err("Live patching not supported on 32-bit x86\n");
> > + return -ENOSYS;
> > +}
>
> Why not just prevent the code from being built on x86_32 instead of
> putting this in the file?

Yep. Masami saw this too and recommended a ARCH_HAVE_LIVE_PATCHING flag
set by the archs that support it. I'll make the change.

Thanks for the review!

Seth

>
> thanks,
>
> greg k-h

2014-11-18 14:11:41

by Miroslav Benes

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching


Hi,

thank you for the revision. I'll rebase our patches on top of that. Anyway
there is a small bug in a header file. See below.

On Sun, 16 Nov 2014, Seth Jennings wrote:

[...]

> diff --git a/arch/x86/include/asm/livepatch.h
b/arch/x86/include/asm/livepatch.h
> new file mode 100644
> index 0000000..c5fab45
> --- /dev/null
> +++ b/arch/x86/include/asm/livepatch.h
> @@ -0,0 +1,38 @@
> +/*
> + * livepatch.h - x86-specific Live Kernel Patching Core
> + *
> + * Copyright (C) 2014 Seth Jennings <[email protected]>
> + *
> + * 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/>.
> + */
> +
> +#ifndef _ASM_X86_LIVEPATCH_H
> +#define _ASM_X86_LIVEPATCH_H
> +
> +#include <linux/module.h>
> +
> +#ifdef CONFIG_LIVE_PATCHING
> +extern int lpc_write_module_reloc(struct module *mod, unsigned long
type,
> + unsigned long loc, unsigned long value);
> +
> +#else
> +static int lpc_write_module_reloc(struct module *mod, unsigned long
type,
> + unsigned long loc, unsigned long value);
> +{
> + pr_err("Kernel does not support live patching\n");
> + return -ENOSYS;
> +}
> +#endif
> +
> +#endif /* _ASM_X86_LIVEPATCH_H */

There should not be a semicolon at the end of the function header in #else
branch.

Regards,

---
Miroslav Benes
SUSE Labs

2014-11-18 14:23:55

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCHv2 0/3] Kernel Live Patching

On 11/17/2014, 03:54 PM, Seth Jennings wrote:
> On Mon, Nov 17, 2014 at 02:33:02PM +0900, Masami Hiramatsu wrote:
>> Hmm, btw, "LP" and "LPC" remind me line-printer and LPC bus :(
>> Can we use LKP (Live Kernel Patching) or KLP (Kernel Live Patching) instead ?
>
> Jiri S also mentioned this so I guess it is a common sentiment :) He
> suggested "lip" but I think I like "klp" better? Jiri S sound good?

Definitely, I like both lkp and klp.

--
js
suse labs

2014-11-18 14:27:10

by Seth Jennings

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Tue, Nov 18, 2014 at 03:11:43PM +0100, Miroslav Benes wrote:
>
> Hi,
>
> thank you for the revision. I'll rebase our patches on top of that. Anyway
> there is a small bug in a header file. See below.
>
> On Sun, 16 Nov 2014, Seth Jennings wrote:
>
> [...]
>
> > diff --git a/arch/x86/include/asm/livepatch.h
> b/arch/x86/include/asm/livepatch.h
> > new file mode 100644
> > index 0000000..c5fab45
> > --- /dev/null
> > +++ b/arch/x86/include/asm/livepatch.h
> > @@ -0,0 +1,38 @@
> > +/*
> > + * livepatch.h - x86-specific Live Kernel Patching Core
> > + *
> > + * Copyright (C) 2014 Seth Jennings <[email protected]>
> > + *
> > + * 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/>.
> > + */
> > +
> > +#ifndef _ASM_X86_LIVEPATCH_H
> > +#define _ASM_X86_LIVEPATCH_H
> > +
> > +#include <linux/module.h>
> > +
> > +#ifdef CONFIG_LIVE_PATCHING
> > +extern int lpc_write_module_reloc(struct module *mod, unsigned long
> type,
> > + unsigned long loc, unsigned long value);
> > +
> > +#else
> > +static int lpc_write_module_reloc(struct module *mod, unsigned long
> type,
> > + unsigned long loc, unsigned long value);
> > +{
> > + pr_err("Kernel does not support live patching\n");
> > + return -ENOSYS;
> > +}
> > +#endif
> > +
> > +#endif /* _ASM_X86_LIVEPATCH_H */
>
> There should not be a semicolon at the end of the function header in #else
> branch.

Good catch.

Also, I'm adding "build with LIVE_PATCHING=n" as a test step :-/

Thanks,
Seth

>
> Regards,
>
> ---
> Miroslav Benes
> SUSE Labs

2014-11-18 14:43:12

by Seth Jennings

[permalink] [raw]
Subject: Re: [PATCHv2 0/3] Kernel Live Patching

On Tue, Nov 18, 2014 at 03:23:49PM +0100, Jiri Slaby wrote:
> On 11/17/2014, 03:54 PM, Seth Jennings wrote:
> > On Mon, Nov 17, 2014 at 02:33:02PM +0900, Masami Hiramatsu wrote:
> >> Hmm, btw, "LP" and "LPC" remind me line-printer and LPC bus :(
> >> Can we use LKP (Live Kernel Patching) or KLP (Kernel Live Patching) instead ?
> >
> > Jiri S also mentioned this so I guess it is a common sentiment :) He
> > suggested "lip" but I think I like "klp" better? Jiri S sound good?
>
> Definitely, I like both lkp and klp.

Great. I'll make the change.

Thanks,
Seth

>
> --
> js
> suse labs
> --
> To unsubscribe from this list: send the line "unsubscribe live-patching" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-11-18 14:45:23

by Miroslav Benes

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching


On Sun, 16 Nov 2014, Seth Jennings wrote:

[...]

> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> new file mode 100644
> index 0000000..8b68fef
> --- /dev/null
> +++ b/include/linux/livepatch.h
> @@ -0,0 +1,68 @@
> +/*
> + * livepatch.h - Live Kernel Patching Core
> + *
> + * Copyright (C) 2014 Seth Jennings <[email protected]>
> + *
> + * 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/>.
> + */
> +
> +#ifndef _LINUX_LIVEPATCH_H_
> +#define _LINUX_LIVEPATCH_H_
> +
> +#include <linux/module.h>
> +

I think we need something like

#if IS_ENABLED(CONFIG_LIVE_PATCHING)

here. Otherwise kernel module with live patch itself would be built
even with live patching support disabled (as the structures and needed
functions are declared).

> +/* TODO: add kernel-doc for structures once agreed upon */
> +
> +struct lp_func {
> + const char *old_name; /* function to be patched */
> + void *new_func; /* replacement function in patch module */
> + /*
> + * The old_addr field is optional and can be used to resolve
> + * duplicate symbol names in the vmlinux object. If this
> + * information is not present, the symbol is located by name
> + * with kallsyms. If the name is not unique and old_addr is
> + * not provided, the patch application fails as there is no
> + * way to resolve the ambiguity.
> + */
> + unsigned long old_addr;
> +};
> +
> +struct lp_reloc {
> + unsigned long dest;
> + unsigned long src;
> + unsigned long type;
> + const char *name;
> + int addend;
> + int external;
> +};
> +
> +struct lp_object {
> + const char *name; /* "vmlinux" or module name */
> + struct lp_func *funcs;
> + struct lp_reloc *relocs;
> +};
> +
> +struct lp_patch {
> + struct module *mod; /* module containing the patch */
> + struct lp_object *objs;
> +};
> +
> +int lp_register_patch(struct lp_patch *);
> +int lp_unregister_patch(struct lp_patch *);
> +int lp_enable_patch(struct lp_patch *);
> +int lp_disable_patch(struct lp_patch *);
> +
> +#include <asm/livepatch.h>

and #endif for CONFIG_LIVE_PATCHING here.

> +
> +#endif /* _LINUX_LIVEPATCH_H_ */

Thanks,
--
Miroslav Benes
SUSE Labs

2014-11-19 15:27:36

by Miroslav Benes

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching


Hi,

during rewriting our code I came across few more things. See below.

On Sun, 16 Nov 2014, Seth Jennings wrote:

[...]

> +/******************************
> + * module notifier
> + *****************************/
> +
> +static void lpc_module_notify_coming(struct module *pmod,
> + struct lpc_object *obj)
> +{
> + struct module *mod = obj->mod;
> + int ret;
> +
> + pr_notice("applying patch '%s' to loading module '%s'\n",
> + mod->name, pmod->name);

This looks strange. I guess the arguments should be swapped.

> + obj->mod = mod;

And this is redundant.

> + ret = lpc_enable_object(pmod, obj);
> + if (ret)
> + pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
> + pmod->name, mod->name, ret);
> +}
> +
> +static void lpc_module_notify_going(struct module *pmod,
> + struct lpc_object *obj)
> +{
> + struct module *mod = obj->mod;
> + int ret;
> +
> + pr_notice("reverting patch '%s' on unloading module '%s'\n",
> + pmod->name, mod->name);
> + ret = lpc_disable_object(obj);
> + if (ret)
> + pr_warn("failed to revert patch '%s' on module '%s' (%d)\n",
> + pmod->name, mod->name, ret);
> + obj->mod = NULL;
> +}
> +
> +static int lpc_module_notify(struct notifier_block *nb, unsigned long action,
> + void *data)
> +{
> + struct module *mod = data;
> + struct lpc_patch *patch;
> + struct lpc_object *obj;
> +
> + mutex_lock(&lpc_mutex);
> +
> + if (action != MODULE_STATE_COMING && action != MODULE_STATE_GOING)
> + goto out;
> +
> + list_for_each_entry(patch, &lpc_patches, list) {
> + if (patch->state == LPC_DISABLED)
> + continue;
> + list_for_each_entry(obj, &patch->objs, list) {
> + if (strcmp(obj->name, mod->name))
> + continue;
> + if (action == MODULE_STATE_COMING) {
> + obj->mod = mod;
> + lpc_module_notify_coming(patch->mod, obj);
> + } else /* MODULE_STATE_GOING */
> + lpc_module_notify_going(patch->mod, obj);
> + break;
> + }
> + }
> +out:
> + mutex_unlock(&lpc_mutex);
> + return 0;
> +}

[...]

> +static struct lpc_object *lpc_create_object(struct kobject *root,
> + struct lp_object *userobj)
> +{
> + struct lpc_object *obj;
> + int ret;
> +
> + /* alloc */
> + obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> + if (!obj)
> + return NULL;
> +
> + /* init */
> + INIT_LIST_HEAD(&obj->list);
> + obj->name = userobj->name;
> + obj->relocs = userobj->relocs;
> + obj->state = LPC_DISABLED;
> + /* obj->mod set by lpc_object_module_get() */
> + INIT_LIST_HEAD(&obj->funcs);

There is nothing like lpc_object_module_get() in the code. Did you mean
lpc_find_object_module()?

Thank you,
--
Miroslav Benes
SUSE Labs

2014-11-19 16:06:22

by Seth Jennings

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Wed, Nov 19, 2014 at 04:27:39PM +0100, Miroslav Benes wrote:
>
> Hi,
>
> during rewriting our code I came across few more things. See below.
>
> On Sun, 16 Nov 2014, Seth Jennings wrote:
>
> [...]
>
> > +/******************************
> > + * module notifier
> > + *****************************/
> > +
> > +static void lpc_module_notify_coming(struct module *pmod,
> > + struct lpc_object *obj)
> > +{
> > + struct module *mod = obj->mod;
> > + int ret;
> > +
> > + pr_notice("applying patch '%s' to loading module '%s'\n",
> > + mod->name, pmod->name);
>
> This looks strange. I guess the arguments should be swapped.

Indeed, you are correct :)

>
> > + obj->mod = mod;
>
> And this is redundant.

True again!

>
> > + ret = lpc_enable_object(pmod, obj);
> > + if (ret)
> > + pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
> > + pmod->name, mod->name, ret);
> > +}
> > +
> > +static void lpc_module_notify_going(struct module *pmod,
> > + struct lpc_object *obj)
> > +{
> > + struct module *mod = obj->mod;
> > + int ret;
> > +
> > + pr_notice("reverting patch '%s' on unloading module '%s'\n",
> > + pmod->name, mod->name);
> > + ret = lpc_disable_object(obj);
> > + if (ret)
> > + pr_warn("failed to revert patch '%s' on module '%s' (%d)\n",
> > + pmod->name, mod->name, ret);
> > + obj->mod = NULL;
> > +}
> > +
> > +static int lpc_module_notify(struct notifier_block *nb, unsigned long action,
> > + void *data)
> > +{
> > + struct module *mod = data;
> > + struct lpc_patch *patch;
> > + struct lpc_object *obj;
> > +
> > + mutex_lock(&lpc_mutex);
> > +
> > + if (action != MODULE_STATE_COMING && action != MODULE_STATE_GOING)
> > + goto out;
> > +
> > + list_for_each_entry(patch, &lpc_patches, list) {
> > + if (patch->state == LPC_DISABLED)
> > + continue;
> > + list_for_each_entry(obj, &patch->objs, list) {
> > + if (strcmp(obj->name, mod->name))
> > + continue;
> > + if (action == MODULE_STATE_COMING) {
> > + obj->mod = mod;
> > + lpc_module_notify_coming(patch->mod, obj);
> > + } else /* MODULE_STATE_GOING */
> > + lpc_module_notify_going(patch->mod, obj);
> > + break;
> > + }
> > + }
> > +out:
> > + mutex_unlock(&lpc_mutex);
> > + return 0;
> > +}
>
> [...]
>
> > +static struct lpc_object *lpc_create_object(struct kobject *root,
> > + struct lp_object *userobj)
> > +{
> > + struct lpc_object *obj;
> > + int ret;
> > +
> > + /* alloc */
> > + obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> > + if (!obj)
> > + return NULL;
> > +
> > + /* init */
> > + INIT_LIST_HEAD(&obj->list);
> > + obj->name = userobj->name;
> > + obj->relocs = userobj->relocs;
> > + obj->state = LPC_DISABLED;
> > + /* obj->mod set by lpc_object_module_get() */
> > + INIT_LIST_HEAD(&obj->funcs);
>
> There is nothing like lpc_object_module_get() in the code. Did you mean
> lpc_find_object_module()?

Yes, this comment should be removed or updated.

Thanks,
Seth

>
> Thank you,
> --
> Miroslav Benes
> SUSE Labs

2014-11-19 20:35:08

by Seth Jennings

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Tue, Nov 18, 2014 at 03:45:22PM +0100, Miroslav Benes wrote:
>
> On Sun, 16 Nov 2014, Seth Jennings wrote:
>
> [...]
>
> > diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> > new file mode 100644
> > index 0000000..8b68fef
> > --- /dev/null
> > +++ b/include/linux/livepatch.h
> > @@ -0,0 +1,68 @@
> > +/*
> > + * livepatch.h - Live Kernel Patching Core
> > + *
> > + * Copyright (C) 2014 Seth Jennings <[email protected]>
> > + *
> > + * 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/>.
> > + */
> > +
> > +#ifndef _LINUX_LIVEPATCH_H_
> > +#define _LINUX_LIVEPATCH_H_
> > +
> > +#include <linux/module.h>
> > +
>
> I think we need something like
>
> #if IS_ENABLED(CONFIG_LIVE_PATCHING)
>
> here. Otherwise kernel module with live patch itself would be built
> even with live patching support disabled (as the structures and needed
> functions are declared).

What do you think of this (already includes s/lp/klp/ change)?

====
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 0143b73..a9821f3 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -21,6 +21,7 @@
#define _LINUX_LIVEPATCH_H_

#include <linux/module.h>
+#include <asm/livepatch.h>

/* TODO: add kernel-doc for structures once agreed upon */

@@ -58,11 +59,20 @@ struct klp_patch {
struct klp_object *objs;
};

-int klp_register_patch(struct klp_patch *);
-int klp_unregister_patch(struct klp_patch *);
-int klp_enable_patch(struct klp_patch *);
-int klp_disable_patch(struct klp_patch *);
+#ifdef CONFIG_LIVE_PATCHING

-#include <asm/livepatch.h>
+extern int klp_register_patch(struct klp_patch *);
+extern int klp_unregister_patch(struct klp_patch *);
+extern int klp_enable_patch(struct klp_patch *);
+extern int klp_disable_patch(struct klp_patch *);
+
+#else /* !CONFIG_LIVE_PATCHING */
+
+static int klp_register_patch(struct klp_patch *k) { return -ENOSYS; }
+static int klp_unregister_patch(struct klp_patch *k) { return -ENOSYS; }
+static int klp_enable_patch(struct klp_patch *k) { return -ENOSYS; }
+static int klp_disable_patch(struct klp_patch *k) { return -ENOSYS; }
+
+#endif
====

This seems to be the way many headers handle this. Patch modules built
against a kernel that doesn't support live patching will build cleanly,
but will always fail to load.

Seth

>
> > +/* TODO: add kernel-doc for structures once agreed upon */
> > +
> > +struct lp_func {
> > + const char *old_name; /* function to be patched */
> > + void *new_func; /* replacement function in patch module */
> > + /*
> > + * The old_addr field is optional and can be used to resolve
> > + * duplicate symbol names in the vmlinux object. If this
> > + * information is not present, the symbol is located by name
> > + * with kallsyms. If the name is not unique and old_addr is
> > + * not provided, the patch application fails as there is no
> > + * way to resolve the ambiguity.
> > + */
> > + unsigned long old_addr;
> > +};
> > +
> > +struct lp_reloc {
> > + unsigned long dest;
> > + unsigned long src;
> > + unsigned long type;
> > + const char *name;
> > + int addend;
> > + int external;
> > +};
> > +
> > +struct lp_object {
> > + const char *name; /* "vmlinux" or module name */
> > + struct lp_func *funcs;
> > + struct lp_reloc *relocs;
> > +};
> > +
> > +struct lp_patch {
> > + struct module *mod; /* module containing the patch */
> > + struct lp_object *objs;
> > +};
> > +
> > +int lp_register_patch(struct lp_patch *);
> > +int lp_unregister_patch(struct lp_patch *);
> > +int lp_enable_patch(struct lp_patch *);
> > +int lp_disable_patch(struct lp_patch *);
> > +
> > +#include <asm/livepatch.h>
>
> and #endif for CONFIG_LIVE_PATCHING here.
>
> > +
> > +#endif /* _LINUX_LIVEPATCH_H_ */
>
> Thanks,
> --
> Miroslav Benes
> SUSE Labs

2014-11-20 13:10:31

by Miroslav Benes

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching


On Sun, 16 Nov 2014, Seth Jennings wrote:

> 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.
>
> Signed-off-by: Seth Jennings <[email protected]>

Hi,

below is the patch which merges the internal and external data structures
(so it is only one part of our original patch for version 1). Apart from
that I tried to make minimal changes to the code. Only unnecessary
kobjects were removed and I renamed lpc_create_* functions to lpc_init_*
as it made more sense in this approach, I think.

I hope this clearly shows our point of view stated previously. What do
you say?

Next, I'll look at the three level hierarchy and sysfs directory and see
if we can make it simpler yet keep its advantages.

Regards,

Miroslav Benes
SUSE Labs

-- >8 --
>From aba839eb6b3292b193843715bfce7834969c0c17 Mon Sep 17 00:00:00 2001
From: Miroslav Benes <[email protected]>
Date: Wed, 19 Nov 2014 16:06:35 +0100
Subject: [PATCH] Remove the data duplication in internal and public structures

The split of internal and external structures is cleaner and makes the API more
stable. But it makes the code more complicated. It requires more space and data
copying. Also the one letter difference of the names (lp_ vs. lpc_ prefix)
causes confusion.

The API is not a real issue for live patching. We take care neither of backward
nor forward compatibility. The dependency between a patch and kernel is even
more strict than by version. They have to use the same configuration and the
same build environment.

This patch merge the external and internal structures into one. The structures
are initialized using ".item = value" syntax. Therefore the API is basically as
stable as it was before. We could later even hide it under some helper macros
if requested.

For the purpose if this patch, we used the prefix "lpc". It allows to make as
less changes as possible and show the real effect. If the patch is accepted, it
would make sense to merge it into the original patch and even use another
common prefix, for example the proposed "klp".

Signed-off-by: Miroslav Benes <[email protected]>
---
include/linux/livepatch.h | 47 +++++--
kernel/livepatch/core.c | 338 ++++++++++++----------------------------------
2 files changed, 121 insertions(+), 264 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 8b68fef..f16de32 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -21,10 +21,23 @@
#define _LINUX_LIVEPATCH_H_

#include <linux/module.h>
+#include <linux/ftrace.h>

/* TODO: add kernel-doc for structures once agreed upon */

-struct lp_func {
+enum lpc_state {
+ LPC_DISABLED,
+ LPC_ENABLED
+};
+
+struct lpc_func {
+ /* internal */
+ struct kobject *kobj;
+ struct ftrace_ops fops;
+ enum lpc_state state;
+ unsigned long new_addr;
+
+ /* external */
const char *old_name; /* function to be patched */
void *new_func; /* replacement function in patch module */
/*
@@ -38,7 +51,7 @@ struct lp_func {
unsigned long old_addr;
};

-struct lp_reloc {
+struct lpc_reloc {
unsigned long dest;
unsigned long src;
unsigned long type;
@@ -47,21 +60,33 @@ struct lp_reloc {
int external;
};

-struct lp_object {
+struct lpc_object {
+ /* internal */
+ struct kobject *kobj;
+ struct module *mod; /* module associated with object */
+ enum lpc_state state;
+
+ /* external */
const char *name; /* "vmlinux" or module name */
- struct lp_func *funcs;
- struct lp_reloc *relocs;
+ struct lpc_reloc *relocs;
+ struct lpc_func *funcs;
};

-struct lp_patch {
+struct lpc_patch {
+ /* internal */
+ struct list_head list;
+ struct kobject kobj;
+ enum lpc_state state;
+
+ /* external */
struct module *mod; /* module containing the patch */
- struct lp_object *objs;
+ struct lpc_object *objs;
};

-int lp_register_patch(struct lp_patch *);
-int lp_unregister_patch(struct lp_patch *);
-int lp_enable_patch(struct lp_patch *);
-int lp_disable_patch(struct lp_patch *);
+extern int lpc_register_patch(struct lpc_patch *);
+extern int lpc_unregister_patch(struct lpc_patch *);
+extern int lpc_enable_patch(struct lpc_patch *);
+extern int lpc_disable_patch(struct lpc_patch *);

#include <asm/livepatch.h>

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index e67c176..6586959 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -32,58 +32,9 @@
* Core structures
************************************/

-/*
- * lp_ structs vs lpc_ structs
- *
- * For each element (patch, object, func) in the live-patching code,
- * there are two types with two different prefixes: lp_ and lpc_.
- *
- * Structures used by the live-patch modules to register with this core module
- * are prefixed with lp_ (live patching). These structures are part of the
- * registration API and are defined in livepatch.h. The structures used
- * internally by this core module are prefixed with lpc_ (live patching core).
- */
-
static DEFINE_MUTEX(lpc_mutex);
static LIST_HEAD(lpc_patches);

-enum lpc_state {
- LPC_DISABLED,
- LPC_ENABLED
-};
-
-struct lpc_func {
- struct list_head list;
- struct kobject kobj;
- struct ftrace_ops fops;
- enum lpc_state state;
-
- const char *old_name;
- unsigned long new_addr;
- unsigned long old_addr;
-};
-
-struct lpc_object {
- struct list_head list;
- struct kobject kobj;
- struct module *mod; /* module associated with object */
- enum lpc_state state;
-
- const char *name;
- struct list_head funcs;
- struct lp_reloc *relocs;
-};
-
-struct lpc_patch {
- struct list_head list;
- struct kobject kobj;
- struct lp_patch *userpatch; /* for correlation during unregister */
- enum lpc_state state;
-
- struct module *mod;
- struct list_head objs;
-};
-
/*******************************************
* Helpers
*******************************************/
@@ -262,7 +213,7 @@ static int lpc_write_object_relocations(struct module *pmod,
struct lpc_object *obj)
{
int ret;
- struct lp_reloc *reloc;
+ struct lpc_reloc *reloc;

for (reloc = obj->relocs; reloc->name; reloc++) {
if (!strcmp(obj->name, "vmlinux")) {
@@ -360,7 +311,7 @@ static int lpc_disable_object(struct lpc_object *obj)
struct lpc_func *func;
int ret;

- list_for_each_entry(func, &obj->funcs, list) {
+ for (func = obj->funcs; func->old_name; func++) {
if (func->state != LPC_ENABLED)
continue;
ret = lpc_disable_func(func);
@@ -387,7 +338,8 @@ static int lpc_enable_object(struct module *pmod, struct lpc_object *obj)
if (ret)
goto unregister;
}
- list_for_each_entry(func, &obj->funcs, list) {
+
+ for (func = obj->funcs; func->old_name; func++) {
ret = lpc_find_verify_func_addr(func, obj->name);
if (ret)
goto unregister;
@@ -408,25 +360,14 @@ unregister:
* enable/disable
******************************/

-static struct lpc_patch *lpc_find_patch(struct lp_patch *userpatch)
-{
- struct lpc_patch *patch;
-
- list_for_each_entry(patch, &lpc_patches, list)
- if (patch->userpatch == userpatch)
- return patch;
-
- return NULL;
-}
-
-static int lpc_disable_patch(struct lpc_patch *patch)
+static int __lpc_disable_patch(struct lpc_patch *patch)
{
struct lpc_object *obj;
int ret;

pr_notice("disabling patch '%s'\n", patch->mod->name);

- list_for_each_entry(obj, &patch->objs, list) {
+ for (obj = patch->objs; obj->name; obj++) {
if (obj->state != LPC_ENABLED)
continue;
ret = lpc_disable_object(obj);
@@ -439,32 +380,26 @@ static int lpc_disable_patch(struct lpc_patch *patch)
}

/**
- * lp_disable_patch() - disables a registered patch
+ * lpc_disable_patch() - disables a registered patch
* @userpatch: The registered, enabled patch to be disabled
*
* Unregisters the patched functions from ftrace.
*
* Return: 0 on success, otherwise error
*/
-int lp_disable_patch(struct lp_patch *userpatch)
+int lpc_disable_patch(struct lpc_patch *patch)
{
- struct lpc_patch *patch;
int ret;

mutex_lock(&lpc_mutex);
- patch = lpc_find_patch(userpatch);
- if (!patch) {
- ret = -ENODEV;
- goto out;
- }
- ret = lpc_disable_patch(patch);
-out:
+ ret = __lpc_disable_patch(patch);
mutex_unlock(&lpc_mutex);
+
return ret;
}
-EXPORT_SYMBOL_GPL(lp_disable_patch);
+EXPORT_SYMBOL_GPL(lpc_disable_patch);

-static int lpc_enable_patch(struct lpc_patch *patch)
+static int __lpc_enable_patch(struct lpc_patch *patch)
{
struct lpc_object *obj;
int ret;
@@ -477,7 +412,7 @@ static int lpc_enable_patch(struct lpc_patch *patch)

pr_notice("enabling patch '%s'\n", patch->mod->name);

- list_for_each_entry(obj, &patch->objs, list) {
+ for (obj = patch->objs; obj->name; obj++) {
if (!lpc_find_object_module(obj))
continue;
ret = lpc_enable_object(patch->mod, obj);
@@ -493,7 +428,7 @@ unregister:
}

/**
- * lp_enable_patch() - enables a registered patch
+ * lpc_enable_patch() - enables a registered patch
* @userpatch: The registered, disabled patch to be enabled
*
* Performs the needed symbol lookups and code relocations,
@@ -501,23 +436,17 @@ unregister:
*
* Return: 0 on success, otherwise error
*/
-int lp_enable_patch(struct lp_patch *userpatch)
+int lpc_enable_patch(struct lpc_patch *patch)
{
- struct lpc_patch *patch;
int ret;

mutex_lock(&lpc_mutex);
- patch = lpc_find_patch(userpatch);
- if (!patch) {
- ret = -ENODEV;
- goto out;
- }
- ret = lpc_enable_patch(patch);
-out:
+ ret = __lpc_enable_patch(patch);
mutex_unlock(&lpc_mutex);
+
return ret;
}
-EXPORT_SYMBOL_GPL(lp_enable_patch);
+EXPORT_SYMBOL_GPL(lpc_enable_patch);

/******************************
* module notifier
@@ -568,7 +497,7 @@ static int lpc_module_notify(struct notifier_block *nb, unsigned long action,
list_for_each_entry(patch, &lpc_patches, list) {
if (patch->state == LPC_DISABLED)
continue;
- list_for_each_entry(obj, &patch->objs, list) {
+ for (obj = patch->objs; obj->name; obj++) {
if (strcmp(obj->name, mod->name))
continue;
if (action == MODULE_STATE_COMING) {
@@ -624,11 +553,11 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
}

if (val == LPC_ENABLED) {
- ret = lpc_enable_patch(patch);
+ ret = __lpc_enable_patch(patch);
if (ret)
goto err;
} else {
- ret = lpc_disable_patch(patch);
+ ret = __lpc_disable_patch(patch);
if (ret)
goto err;
}
@@ -677,7 +606,6 @@ static void lpc_kobj_release_patch(struct kobject *kobj)
patch = container_of(kobj, struct lpc_patch, kobj);
if (!list_empty(&patch->list))
list_del(&patch->list);
- kfree(patch);
}

static struct kobj_type lpc_ktype_patch = {
@@ -686,212 +614,122 @@ static struct kobj_type lpc_ktype_patch = {
.default_attrs = lpc_patch_attrs
};

-static void lpc_kobj_release_object(struct kobject *kobj)
-{
- struct lpc_object *obj;
-
- obj = container_of(kobj, struct lpc_object, kobj);
- if (!list_empty(&obj->list))
- list_del(&obj->list);
- kfree(obj);
-}
-
-static struct kobj_type lpc_ktype_object = {
- .release = lpc_kobj_release_object,
- .sysfs_ops = &kobj_sysfs_ops,
-};
-
-static void lpc_kobj_release_func(struct kobject *kobj)
-{
- struct lpc_func *func;
-
- func = container_of(kobj, struct lpc_func, kobj);
- if (!list_empty(&func->list))
- list_del(&func->list);
- kfree(func);
-}
-
-static struct kobj_type lpc_ktype_func = {
- .release = lpc_kobj_release_func,
- .sysfs_ops = &kobj_sysfs_ops,
-};
-
/*********************************
* structure allocation
********************************/

-static void lpc_free_funcs(struct lpc_object *obj)
+/*
+ * Free all functions' kobjects in the array up to some limit. When limit is
+ * NULL, all kobjects are freed.
+ */
+static void lpc_free_funcs_limited(struct lpc_object *obj,
+ struct lpc_func *limit)
{
- struct lpc_func *func, *funcsafe;
+ struct lpc_func *func;

- list_for_each_entry_safe(func, funcsafe, &obj->funcs, list)
- kobject_put(&func->kobj);
+ for (func = obj->funcs; func->old_name && func != limit; func++)
+ kobject_put(func->kobj);
}

-static void lpc_free_objects(struct lpc_patch *patch)
+/*
+ * Free all objects' kobjects in the array up to some limit. When limit is
+ * NULL, all kobjects are freed.
+ */
+static void lpc_free_objects_limited(struct lpc_patch *patch,
+ struct lpc_object *limit)
{
- struct lpc_object *obj, *objsafe;
+ struct lpc_object *obj;

- list_for_each_entry_safe(obj, objsafe, &patch->objs, list) {
- lpc_free_funcs(obj);
- kobject_put(&obj->kobj);
+ for (obj = patch->objs; obj->name && obj != limit; obj++) {
+ lpc_free_funcs_limited(obj, NULL);
+ kobject_put(obj->kobj);
}
}

static void lpc_free_patch(struct lpc_patch *patch)
{
- lpc_free_objects(patch);
+ lpc_free_objects_limited(patch, NULL);
kobject_put(&patch->kobj);
}

-static struct lpc_func *lpc_create_func(struct kobject *root,
- struct lp_func *userfunc)
+static int lpc_init_funcs(struct lpc_object *obj)
{
struct lpc_func *func;
struct ftrace_ops *ops;
- int ret;
-
- /* alloc */
- func = kzalloc(sizeof(*func), GFP_KERNEL);
- if (!func)
- return NULL;
-
- /* init */
- INIT_LIST_HEAD(&func->list);
- func->old_name = userfunc->old_name;
- func->new_addr = (unsigned long)userfunc->new_func;
- func->old_addr = userfunc->old_addr;
- func->state = LPC_DISABLED;
- ops = &func->fops;
- ops->private = func;
- ops->func = lpc_ftrace_handler;
- ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;
-
- /* sysfs */
- ret = kobject_init_and_add(&func->kobj, &lpc_ktype_func,
- root, func->old_name);
- if (ret) {
- kfree(func);
- return NULL;
- }
-
- return func;
-}
-
-static int lpc_create_funcs(struct lpc_object *obj,
- struct lp_func *userfuncs)
-{
- struct lp_func *userfunc;
- struct lpc_func *func;

- if (!userfuncs)
- return -EINVAL;
-
- for (userfunc = userfuncs; userfunc->old_name; userfunc++) {
- func = lpc_create_func(&obj->kobj, userfunc);
- if (!func)
+ for (func = obj->funcs; func->old_name; func++) {
+ func->new_addr = (unsigned long)func->new_func;
+ func->state = LPC_DISABLED;
+ ops = &func->fops;
+ ops->private = func;
+ ops->func = lpc_ftrace_handler;
+ ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;
+
+ /* sysfs */
+ func->kobj = kobject_create_and_add(func->old_name, obj->kobj);
+ if (!func->kobj)
goto free;
- list_add(&func->list, &obj->funcs);
}
+
return 0;
free:
- lpc_free_funcs(obj);
+ lpc_free_funcs_limited(obj, func);
return -ENOMEM;
}

-static struct lpc_object *lpc_create_object(struct kobject *root,
- struct lp_object *userobj)
+static int lpc_init_objects(struct lpc_patch *patch)
{
struct lpc_object *obj;
int ret;

- /* alloc */
- obj = kzalloc(sizeof(*obj), GFP_KERNEL);
- if (!obj)
- return NULL;
+ for (obj = patch->objs; obj->name; obj++) {
+ /* obj->mod set by lpc_object_module_get() */
+ obj->state = LPC_DISABLED;

- /* init */
- INIT_LIST_HEAD(&obj->list);
- obj->name = userobj->name;
- obj->relocs = userobj->relocs;
- obj->state = LPC_DISABLED;
- /* obj->mod set by lpc_object_module_get() */
- INIT_LIST_HEAD(&obj->funcs);
-
- /* sysfs */
- ret = kobject_init_and_add(&obj->kobj, &lpc_ktype_object,
- root, obj->name);
- if (ret) {
- kfree(obj);
- return NULL;
- }
-
- /* create functions */
- ret = lpc_create_funcs(obj, userobj->funcs);
- if (ret) {
- kobject_put(&obj->kobj);
- return NULL;
- }
-
- return obj;
-}
-
-static int lpc_create_objects(struct lpc_patch *patch,
- struct lp_object *userobjs)
-{
- struct lp_object *userobj;
- struct lpc_object *obj;
-
- if (!userobjs)
- return -EINVAL;
+ /* sysfs */
+ obj->kobj = kobject_create_and_add(obj->name, &patch->kobj);
+ if (!obj->kobj)
+ goto free;

- for (userobj = userobjs; userobj->name; userobj++) {
- obj = lpc_create_object(&patch->kobj, userobj);
- if (!obj)
+ /* init functions */
+ ret = lpc_init_funcs(obj);
+ if (ret) {
+ kobject_put(obj->kobj);
goto free;
- list_add(&obj->list, &patch->objs);
+ }
}
+
return 0;
free:
- lpc_free_objects(patch);
+ lpc_free_objects_limited(patch, obj);
return -ENOMEM;
}

-static int lpc_create_patch(struct lp_patch *userpatch)
+static int lpc_init_patch(struct lpc_patch *patch)
{
- struct lpc_patch *patch;
int ret;

- /* alloc */
- patch = kzalloc(sizeof(*patch), GFP_KERNEL);
- if (!patch)
- return -ENOMEM;
+ mutex_lock(&lpc_mutex);

/* init */
- INIT_LIST_HEAD(&patch->list);
- patch->userpatch = userpatch;
- patch->mod = userpatch->mod;
patch->state = LPC_DISABLED;
- INIT_LIST_HEAD(&patch->objs);

/* sysfs */
ret = kobject_init_and_add(&patch->kobj, &lpc_ktype_patch,
lpc_root_kobj, patch->mod->name);
- if (ret) {
- kfree(patch);
+ if (ret)
return ret;
- }

/* create objects */
- ret = lpc_create_objects(patch, userpatch->objs);
+ ret = lpc_init_objects(patch);
if (ret) {
kobject_put(&patch->kobj);
return ret;
}

/* add to global list of patches */
- mutex_lock(&lpc_mutex);
list_add(&patch->list, &lpc_patches);
+
mutex_unlock(&lpc_mutex);

return 0;
@@ -902,7 +740,7 @@ static int lpc_create_patch(struct lp_patch *userpatch)
***********************************/

/**
- * lp_register_patch() - registers a patch
+ * lpc_register_patch() - registers a patch
* @userpatch: Patch to be registered
*
* Allocates the data structure associated with the patch and
@@ -910,11 +748,11 @@ static int lpc_create_patch(struct lp_patch *userpatch)
*
* Return: 0 on success, otherwise error
*/
-int lp_register_patch(struct lp_patch *userpatch)
+int lpc_register_patch(struct lpc_patch *userpatch)
{
int ret;

- if (!userpatch || !userpatch->mod || !userpatch->objs)
+ if (!userpatch || !userpatch->mod)
return -EINVAL;

/*
@@ -927,43 +765,37 @@ int lp_register_patch(struct lp_patch *userpatch)
if (!try_module_get(userpatch->mod))
return -ENODEV;

- ret = lpc_create_patch(userpatch);
+ ret = lpc_init_patch(userpatch);
if (ret)
module_put(userpatch->mod);

return ret;
}
-EXPORT_SYMBOL_GPL(lp_register_patch);
+EXPORT_SYMBOL_GPL(lpc_register_patch);

/**
- * lp_unregister_patch() - unregisters a patch
+ * lpc_unregister_patch() - unregisters a patch
* @userpatch: Disabled patch to be unregistered
*
* Frees the data structures and removes the sysfs interface.
*
* Return: 0 on success, otherwise error
*/
-int lp_unregister_patch(struct lp_patch *userpatch)
+int lpc_unregister_patch(struct lpc_patch *userpatch)
{
- struct lpc_patch *patch;
int ret = 0;

mutex_lock(&lpc_mutex);
- patch = lpc_find_patch(userpatch);
- if (!patch) {
- ret = -ENODEV;
- goto out;
- }
- if (patch->state == LPC_ENABLED) {
+ if (userpatch->state == LPC_ENABLED) {
ret = -EINVAL;
goto out;
}
- lpc_free_patch(patch);
+ lpc_free_patch(userpatch);
out:
mutex_unlock(&lpc_mutex);
return ret;
}
-EXPORT_SYMBOL_GPL(lp_unregister_patch);
+EXPORT_SYMBOL_GPL(lpc_unregister_patch);

/************************************
* entry/exit
--
2.1.2

2014-11-20 13:22:34

by Miroslav Benes

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Wed, 19 Nov 2014, Seth Jennings wrote:

> On Tue, Nov 18, 2014 at 03:45:22PM +0100, Miroslav Benes wrote:
> >
> > On Sun, 16 Nov 2014, Seth Jennings wrote:
> >
> > [...]
> >
> > > diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> > > new file mode 100644
> > > index 0000000..8b68fef
> > > --- /dev/null
> > > +++ b/include/linux/livepatch.h
> > > @@ -0,0 +1,68 @@
> > > +/*
> > > + * livepatch.h - Live Kernel Patching Core
> > > + *
> > > + * Copyright (C) 2014 Seth Jennings <[email protected]>
> > > + *
> > > + * 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/>.
> > > + */
> > > +
> > > +#ifndef _LINUX_LIVEPATCH_H_
> > > +#define _LINUX_LIVEPATCH_H_
> > > +
> > > +#include <linux/module.h>
> > > +
> >
> > I think we need something like
> >
> > #if IS_ENABLED(CONFIG_LIVE_PATCHING)
> >
> > here. Otherwise kernel module with live patch itself would be built
> > even with live patching support disabled (as the structures and needed
> > functions are declared).
>
> What do you think of this (already includes s/lp/klp/ change)?
>
> ====
> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> index 0143b73..a9821f3 100644
> --- a/include/linux/livepatch.h
> +++ b/include/linux/livepatch.h
> @@ -21,6 +21,7 @@
> #define _LINUX_LIVEPATCH_H_
>
> #include <linux/module.h>
> +#include <asm/livepatch.h>
>
> /* TODO: add kernel-doc for structures once agreed upon */
>
> @@ -58,11 +59,20 @@ struct klp_patch {
> struct klp_object *objs;
> };
>
> -int klp_register_patch(struct klp_patch *);
> -int klp_unregister_patch(struct klp_patch *);
> -int klp_enable_patch(struct klp_patch *);
> -int klp_disable_patch(struct klp_patch *);
> +#ifdef CONFIG_LIVE_PATCHING
>
> -#include <asm/livepatch.h>
> +extern int klp_register_patch(struct klp_patch *);
> +extern int klp_unregister_patch(struct klp_patch *);
> +extern int klp_enable_patch(struct klp_patch *);
> +extern int klp_disable_patch(struct klp_patch *);
> +
> +#else /* !CONFIG_LIVE_PATCHING */
> +
> +static int klp_register_patch(struct klp_patch *k) { return -ENOSYS; }
> +static int klp_unregister_patch(struct klp_patch *k) { return -ENOSYS; }
> +static int klp_enable_patch(struct klp_patch *k) { return -ENOSYS; }
> +static int klp_disable_patch(struct klp_patch *k) { return -ENOSYS; }
> +
> +#endif
> ====
>
> This seems to be the way many headers handle this. Patch modules built
> against a kernel that doesn't support live patching will build cleanly,
> but will always fail to load.
>
> Seth

Hm, I would still vote for build failure. I think it doesn't make sense to
build patch module against a kernel that doesn't support live patching and
it is better to let the user know (and not potentially someone else who
would load it and fail). Afaik the other headers handle it your way
because otherwise the code would be spoiled by #ifdefs in .c files.
However I think that our case is a bit different.

Anyway it is better to use #if (IS_ENABLED(CONFIG_LIVE_PATCHING)) than
simple #ifdef (see Documentation/CodingStyle) and make the functions
static inlined for !CONFIG_LIVE_PATCHING case.

Mira

> >
> > > +/* TODO: add kernel-doc for structures once agreed upon */
> > > +
> > > +struct lp_func {
> > > + const char *old_name; /* function to be patched */
> > > + void *new_func; /* replacement function in patch module */
> > > + /*
> > > + * The old_addr field is optional and can be used to resolve
> > > + * duplicate symbol names in the vmlinux object. If this
> > > + * information is not present, the symbol is located by name
> > > + * with kallsyms. If the name is not unique and old_addr is
> > > + * not provided, the patch application fails as there is no
> > > + * way to resolve the ambiguity.
> > > + */
> > > + unsigned long old_addr;
> > > +};
> > > +
> > > +struct lp_reloc {
> > > + unsigned long dest;
> > > + unsigned long src;
> > > + unsigned long type;
> > > + const char *name;
> > > + int addend;
> > > + int external;
> > > +};
> > > +
> > > +struct lp_object {
> > > + const char *name; /* "vmlinux" or module name */
> > > + struct lp_func *funcs;
> > > + struct lp_reloc *relocs;
> > > +};
> > > +
> > > +struct lp_patch {
> > > + struct module *mod; /* module containing the patch */
> > > + struct lp_object *objs;
> > > +};
> > > +
> > > +int lp_register_patch(struct lp_patch *);
> > > +int lp_unregister_patch(struct lp_patch *);
> > > +int lp_enable_patch(struct lp_patch *);
> > > +int lp_disable_patch(struct lp_patch *);
> > > +
> > > +#include <asm/livepatch.h>
> >
> > and #endif for CONFIG_LIVE_PATCHING here.
> >
> > > +
> > > +#endif /* _LINUX_LIVEPATCH_H_ */
> >
> > Thanks,
> > --
> > Miroslav Benes
> > SUSE Labs
>

2014-11-20 15:20:32

by Josh Poimboeuf

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Sun, Nov 16, 2014 at 07:29:23PM -0600, Seth Jennings wrote:
> +static int lpc_module_notify(struct notifier_block *nb, unsigned long action,
> + void *data)
> +{
> + struct module *mod = data;
> + struct lpc_patch *patch;
> + struct lpc_object *obj;
> +
> + mutex_lock(&lpc_mutex);
> +
> + if (action != MODULE_STATE_COMING && action != MODULE_STATE_GOING)
> + goto out;

I think we can get the mutex here instead of above so it doesn't block
other module actions (and then you can also get rid of the "out" label).

> +
> + list_for_each_entry(patch, &lpc_patches, list) {
> + if (patch->state == LPC_DISABLED)
> + continue;
> + list_for_each_entry(obj, &patch->objs, list) {
> + if (strcmp(obj->name, mod->name))
> + continue;
> + if (action == MODULE_STATE_COMING) {
> + obj->mod = mod;
> + lpc_module_notify_coming(patch->mod, obj);
> + } else /* MODULE_STATE_GOING */
> + lpc_module_notify_going(patch->mod, obj);
> + break;
> + }
> + }
> +out:
> + mutex_unlock(&lpc_mutex);
> + return 0;
> +}

--
Josh

2014-11-20 16:49:30

by Seth Jennings

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Thu, Nov 20, 2014 at 09:19:54AM -0600, Josh Poimboeuf wrote:
> On Sun, Nov 16, 2014 at 07:29:23PM -0600, Seth Jennings wrote:
> > +static int lpc_module_notify(struct notifier_block *nb, unsigned long action,
> > + void *data)
> > +{
> > + struct module *mod = data;
> > + struct lpc_patch *patch;
> > + struct lpc_object *obj;
> > +
> > + mutex_lock(&lpc_mutex);
> > +
> > + if (action != MODULE_STATE_COMING && action != MODULE_STATE_GOING)
> > + goto out;
>
> I think we can get the mutex here instead of above so it doesn't block
> other module actions (and then you can also get rid of the "out" label).

Sure.

Thanks,
Seth

>
> > +
> > + list_for_each_entry(patch, &lpc_patches, list) {
> > + if (patch->state == LPC_DISABLED)
> > + continue;
> > + list_for_each_entry(obj, &patch->objs, list) {
> > + if (strcmp(obj->name, mod->name))
> > + continue;
> > + if (action == MODULE_STATE_COMING) {
> > + obj->mod = mod;
> > + lpc_module_notify_coming(patch->mod, obj);
> > + } else /* MODULE_STATE_GOING */
> > + lpc_module_notify_going(patch->mod, obj);
> > + break;
> > + }
> > + }
> > +out:
> > + mutex_unlock(&lpc_mutex);
> > + return 0;
> > +}
>
> --
> Josh
> --
> To unsubscribe from this list: send the line "unsubscribe live-patching" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-11-20 17:36:31

by Josh Poimboeuf

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Thu, Nov 20, 2014 at 02:10:33PM +0100, Miroslav Benes wrote:
>
> On Sun, 16 Nov 2014, Seth Jennings wrote:
>
> > 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.
> >
> > Signed-off-by: Seth Jennings <[email protected]>
>
> Hi,
>
> below is the patch which merges the internal and external data structures
> (so it is only one part of our original patch for version 1). Apart from
> that I tried to make minimal changes to the code. Only unnecessary
> kobjects were removed and I renamed lpc_create_* functions to lpc_init_*
> as it made more sense in this approach, I think.
>
> I hope this clearly shows our point of view stated previously. What do
> you say?

Thanks for rebasing to v2 and splitting up the patches! Personally I'm
ok with this patch (though I do have a few comments below).

> Next, I'll look at the three level hierarchy and sysfs directory and see
> if we can make it simpler yet keep its advantages.
>
> Regards,
>
> Miroslav Benes
> SUSE Labs
>
> -- >8 --
> From aba839eb6b3292b193843715bfce7834969c0c17 Mon Sep 17 00:00:00 2001
> From: Miroslav Benes <[email protected]>
> Date: Wed, 19 Nov 2014 16:06:35 +0100
> Subject: [PATCH] Remove the data duplication in internal and public structures
>
> The split of internal and external structures is cleaner and makes the API more
> stable. But it makes the code more complicated. It requires more space and data
> copying. Also the one letter difference of the names (lp_ vs. lpc_ prefix)
> causes confusion.
>
> The API is not a real issue for live patching. We take care neither of backward
> nor forward compatibility. The dependency between a patch and kernel is even
> more strict than by version. They have to use the same configuration and the
> same build environment.
>
> This patch merge the external and internal structures into one. The structures
> are initialized using ".item = value" syntax. Therefore the API is basically as
> stable as it was before. We could later even hide it under some helper macros
> if requested.
>
> For the purpose if this patch, we used the prefix "lpc". It allows to make as
> less changes as possible and show the real effect. If the patch is accepted, it
> would make sense to merge it into the original patch and even use another
> common prefix, for example the proposed "klp".
>
> Signed-off-by: Miroslav Benes <[email protected]>
> ---
> include/linux/livepatch.h | 47 +++++--
> kernel/livepatch/core.c | 338 ++++++++++++----------------------------------
> 2 files changed, 121 insertions(+), 264 deletions(-)
>
> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> index 8b68fef..f16de32 100644
> --- a/include/linux/livepatch.h
> +++ b/include/linux/livepatch.h
> @@ -21,10 +21,23 @@
> #define _LINUX_LIVEPATCH_H_
>
> #include <linux/module.h>
> +#include <linux/ftrace.h>
>
> /* TODO: add kernel-doc for structures once agreed upon */
>
> -struct lp_func {
> +enum lpc_state {
> + LPC_DISABLED,
> + LPC_ENABLED
> +};
> +
> +struct lpc_func {
> + /* internal */

Would it be a little clearer to list the external fields first?

> + struct kobject *kobj;
> + struct ftrace_ops fops;
> + enum lpc_state state;
> + unsigned long new_addr;

I think this internal new_addr field isn't really needed since we
already have the external new_func field.

> +
> + /* external */
> const char *old_name; /* function to be patched */
> void *new_func; /* replacement function in patch module */
> /*
> @@ -38,7 +51,7 @@ struct lp_func {
> unsigned long old_addr;
> };
>
> -struct lp_reloc {
> +struct lpc_reloc {
> unsigned long dest;
> unsigned long src;
> unsigned long type;
> @@ -47,21 +60,33 @@ struct lp_reloc {
> int external;
> };
>
> -struct lp_object {
> +struct lpc_object {
> + /* internal */
> + struct kobject *kobj;
> + struct module *mod; /* module associated with object */
> + enum lpc_state state;
> +
> + /* external */
> const char *name; /* "vmlinux" or module name */
> - struct lp_func *funcs;
> - struct lp_reloc *relocs;
> + struct lpc_reloc *relocs;
> + struct lpc_func *funcs;
> };
>
> -struct lp_patch {
> +struct lpc_patch {
> + /* internal */
> + struct list_head list;
> + struct kobject kobj;
> + enum lpc_state state;
> +
> + /* external */
> struct module *mod; /* module containing the patch */
> - struct lp_object *objs;
> + struct lpc_object *objs;
> };
>
> -int lp_register_patch(struct lp_patch *);
> -int lp_unregister_patch(struct lp_patch *);
> -int lp_enable_patch(struct lp_patch *);
> -int lp_disable_patch(struct lp_patch *);
> +extern int lpc_register_patch(struct lpc_patch *);
> +extern int lpc_unregister_patch(struct lpc_patch *);
> +extern int lpc_enable_patch(struct lpc_patch *);
> +extern int lpc_disable_patch(struct lpc_patch *);
>
> #include <asm/livepatch.h>
>
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index e67c176..6586959 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -32,58 +32,9 @@
> * Core structures
> ************************************/
>
> -/*
> - * lp_ structs vs lpc_ structs
> - *
> - * For each element (patch, object, func) in the live-patching code,
> - * there are two types with two different prefixes: lp_ and lpc_.
> - *
> - * Structures used by the live-patch modules to register with this core module
> - * are prefixed with lp_ (live patching). These structures are part of the
> - * registration API and are defined in livepatch.h. The structures used
> - * internally by this core module are prefixed with lpc_ (live patching core).
> - */
> -
> static DEFINE_MUTEX(lpc_mutex);
> static LIST_HEAD(lpc_patches);
>
> -enum lpc_state {
> - LPC_DISABLED,
> - LPC_ENABLED
> -};
> -
> -struct lpc_func {
> - struct list_head list;
> - struct kobject kobj;
> - struct ftrace_ops fops;
> - enum lpc_state state;
> -
> - const char *old_name;
> - unsigned long new_addr;
> - unsigned long old_addr;
> -};
> -
> -struct lpc_object {
> - struct list_head list;
> - struct kobject kobj;
> - struct module *mod; /* module associated with object */
> - enum lpc_state state;
> -
> - const char *name;
> - struct list_head funcs;
> - struct lp_reloc *relocs;
> -};
> -
> -struct lpc_patch {
> - struct list_head list;
> - struct kobject kobj;
> - struct lp_patch *userpatch; /* for correlation during unregister */
> - enum lpc_state state;
> -
> - struct module *mod;
> - struct list_head objs;
> -};
> -
> /*******************************************
> * Helpers
> *******************************************/
> @@ -262,7 +213,7 @@ static int lpc_write_object_relocations(struct module *pmod,
> struct lpc_object *obj)
> {
> int ret;
> - struct lp_reloc *reloc;
> + struct lpc_reloc *reloc;
>
> for (reloc = obj->relocs; reloc->name; reloc++) {
> if (!strcmp(obj->name, "vmlinux")) {
> @@ -360,7 +311,7 @@ static int lpc_disable_object(struct lpc_object *obj)
> struct lpc_func *func;
> int ret;
>
> - list_for_each_entry(func, &obj->funcs, list) {
> + for (func = obj->funcs; func->old_name; func++) {
> if (func->state != LPC_ENABLED)
> continue;
> ret = lpc_disable_func(func);
> @@ -387,7 +338,8 @@ static int lpc_enable_object(struct module *pmod, struct lpc_object *obj)
> if (ret)
> goto unregister;
> }
> - list_for_each_entry(func, &obj->funcs, list) {
> +
> + for (func = obj->funcs; func->old_name; func++) {
> ret = lpc_find_verify_func_addr(func, obj->name);
> if (ret)
> goto unregister;
> @@ -408,25 +360,14 @@ unregister:
> * enable/disable
> ******************************/
>
> -static struct lpc_patch *lpc_find_patch(struct lp_patch *userpatch)
> -{
> - struct lpc_patch *patch;
> -
> - list_for_each_entry(patch, &lpc_patches, list)
> - if (patch->userpatch == userpatch)
> - return patch;
> -
> - return NULL;
> -}
> -
> -static int lpc_disable_patch(struct lpc_patch *patch)
> +static int __lpc_disable_patch(struct lpc_patch *patch)
> {
> struct lpc_object *obj;
> int ret;
>
> pr_notice("disabling patch '%s'\n", patch->mod->name);
>
> - list_for_each_entry(obj, &patch->objs, list) {
> + for (obj = patch->objs; obj->name; obj++) {
> if (obj->state != LPC_ENABLED)
> continue;
> ret = lpc_disable_object(obj);
> @@ -439,32 +380,26 @@ static int lpc_disable_patch(struct lpc_patch *patch)
> }
>
> /**
> - * lp_disable_patch() - disables a registered patch
> + * lpc_disable_patch() - disables a registered patch
> * @userpatch: The registered, enabled patch to be disabled
> *
> * Unregisters the patched functions from ftrace.
> *
> * Return: 0 on success, otherwise error
> */
> -int lp_disable_patch(struct lp_patch *userpatch)
> +int lpc_disable_patch(struct lpc_patch *patch)
> {
> - struct lpc_patch *patch;
> int ret;
>
> mutex_lock(&lpc_mutex);
> - patch = lpc_find_patch(userpatch);
> - if (!patch) {
> - ret = -ENODEV;
> - goto out;
> - }
> - ret = lpc_disable_patch(patch);
> -out:
> + ret = __lpc_disable_patch(patch);
> mutex_unlock(&lpc_mutex);
> +
> return ret;
> }
> -EXPORT_SYMBOL_GPL(lp_disable_patch);
> +EXPORT_SYMBOL_GPL(lpc_disable_patch);
>
> -static int lpc_enable_patch(struct lpc_patch *patch)
> +static int __lpc_enable_patch(struct lpc_patch *patch)
> {
> struct lpc_object *obj;
> int ret;
> @@ -477,7 +412,7 @@ static int lpc_enable_patch(struct lpc_patch *patch)
>
> pr_notice("enabling patch '%s'\n", patch->mod->name);
>
> - list_for_each_entry(obj, &patch->objs, list) {
> + for (obj = patch->objs; obj->name; obj++) {
> if (!lpc_find_object_module(obj))
> continue;
> ret = lpc_enable_object(patch->mod, obj);
> @@ -493,7 +428,7 @@ unregister:
> }
>
> /**
> - * lp_enable_patch() - enables a registered patch
> + * lpc_enable_patch() - enables a registered patch
> * @userpatch: The registered, disabled patch to be enabled
> *
> * Performs the needed symbol lookups and code relocations,
> @@ -501,23 +436,17 @@ unregister:
> *
> * Return: 0 on success, otherwise error
> */
> -int lp_enable_patch(struct lp_patch *userpatch)
> +int lpc_enable_patch(struct lpc_patch *patch)
> {
> - struct lpc_patch *patch;
> int ret;
>
> mutex_lock(&lpc_mutex);
> - patch = lpc_find_patch(userpatch);
> - if (!patch) {
> - ret = -ENODEV;
> - goto out;
> - }
> - ret = lpc_enable_patch(patch);
> -out:
> + ret = __lpc_enable_patch(patch);
> mutex_unlock(&lpc_mutex);
> +
> return ret;
> }
> -EXPORT_SYMBOL_GPL(lp_enable_patch);
> +EXPORT_SYMBOL_GPL(lpc_enable_patch);
>
> /******************************
> * module notifier
> @@ -568,7 +497,7 @@ static int lpc_module_notify(struct notifier_block *nb, unsigned long action,
> list_for_each_entry(patch, &lpc_patches, list) {
> if (patch->state == LPC_DISABLED)
> continue;
> - list_for_each_entry(obj, &patch->objs, list) {
> + for (obj = patch->objs; obj->name; obj++) {
> if (strcmp(obj->name, mod->name))
> continue;
> if (action == MODULE_STATE_COMING) {
> @@ -624,11 +553,11 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
> }
>
> if (val == LPC_ENABLED) {
> - ret = lpc_enable_patch(patch);
> + ret = __lpc_enable_patch(patch);
> if (ret)
> goto err;
> } else {
> - ret = lpc_disable_patch(patch);
> + ret = __lpc_disable_patch(patch);
> if (ret)
> goto err;
> }
> @@ -677,7 +606,6 @@ static void lpc_kobj_release_patch(struct kobject *kobj)
> patch = container_of(kobj, struct lpc_patch, kobj);
> if (!list_empty(&patch->list))
> list_del(&patch->list);
> - kfree(patch);
> }
>
> static struct kobj_type lpc_ktype_patch = {
> @@ -686,212 +614,122 @@ static struct kobj_type lpc_ktype_patch = {
> .default_attrs = lpc_patch_attrs
> };
>
> -static void lpc_kobj_release_object(struct kobject *kobj)
> -{
> - struct lpc_object *obj;
> -
> - obj = container_of(kobj, struct lpc_object, kobj);
> - if (!list_empty(&obj->list))
> - list_del(&obj->list);
> - kfree(obj);
> -}
> -
> -static struct kobj_type lpc_ktype_object = {
> - .release = lpc_kobj_release_object,
> - .sysfs_ops = &kobj_sysfs_ops,
> -};
> -
> -static void lpc_kobj_release_func(struct kobject *kobj)
> -{
> - struct lpc_func *func;
> -
> - func = container_of(kobj, struct lpc_func, kobj);
> - if (!list_empty(&func->list))
> - list_del(&func->list);
> - kfree(func);
> -}
> -
> -static struct kobj_type lpc_ktype_func = {
> - .release = lpc_kobj_release_func,
> - .sysfs_ops = &kobj_sysfs_ops,
> -};
> -
> /*********************************
> * structure allocation
> ********************************/
>
> -static void lpc_free_funcs(struct lpc_object *obj)
> +/*
> + * Free all functions' kobjects in the array up to some limit. When limit is
> + * NULL, all kobjects are freed.
> + */
> +static void lpc_free_funcs_limited(struct lpc_object *obj,
> + struct lpc_func *limit)
> {
> - struct lpc_func *func, *funcsafe;
> + struct lpc_func *func;
>
> - list_for_each_entry_safe(func, funcsafe, &obj->funcs, list)
> - kobject_put(&func->kobj);
> + for (func = obj->funcs; func->old_name && func != limit; func++)
> + kobject_put(func->kobj);
> }
>
> -static void lpc_free_objects(struct lpc_patch *patch)
> +/*
> + * Free all objects' kobjects in the array up to some limit. When limit is
> + * NULL, all kobjects are freed.
> + */
> +static void lpc_free_objects_limited(struct lpc_patch *patch,
> + struct lpc_object *limit)
> {
> - struct lpc_object *obj, *objsafe;
> + struct lpc_object *obj;
>
> - list_for_each_entry_safe(obj, objsafe, &patch->objs, list) {
> - lpc_free_funcs(obj);
> - kobject_put(&obj->kobj);
> + for (obj = patch->objs; obj->name && obj != limit; obj++) {
> + lpc_free_funcs_limited(obj, NULL);
> + kobject_put(obj->kobj);
> }
> }
>
> static void lpc_free_patch(struct lpc_patch *patch)
> {
> - lpc_free_objects(patch);
> + lpc_free_objects_limited(patch, NULL);
> kobject_put(&patch->kobj);
> }
>
> -static struct lpc_func *lpc_create_func(struct kobject *root,
> - struct lp_func *userfunc)
> +static int lpc_init_funcs(struct lpc_object *obj)
> {
> struct lpc_func *func;
> struct ftrace_ops *ops;
> - int ret;
> -
> - /* alloc */
> - func = kzalloc(sizeof(*func), GFP_KERNEL);
> - if (!func)
> - return NULL;
> -
> - /* init */
> - INIT_LIST_HEAD(&func->list);
> - func->old_name = userfunc->old_name;
> - func->new_addr = (unsigned long)userfunc->new_func;
> - func->old_addr = userfunc->old_addr;
> - func->state = LPC_DISABLED;
> - ops = &func->fops;
> - ops->private = func;
> - ops->func = lpc_ftrace_handler;
> - ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;

FTRACE_OPS_FL_DYNAMIC is probably no longer appropriate here, since I
think both kGraft and kpatch allocate the func struct statically.

However, we can't know for sure how the user allocated it, so maybe we
need to change funcs->fops to be a pointer, and allocate it here?

That may also require embedding kobj into the func struct so that ops
can be freed with lpc_kobj_release_func.

> -
> - /* sysfs */
> - ret = kobject_init_and_add(&func->kobj, &lpc_ktype_func,
> - root, func->old_name);
> - if (ret) {
> - kfree(func);
> - return NULL;
> - }
> -
> - return func;
> -}
> -
> -static int lpc_create_funcs(struct lpc_object *obj,
> - struct lp_func *userfuncs)
> -{
> - struct lp_func *userfunc;
> - struct lpc_func *func;
>
> - if (!userfuncs)
> - return -EINVAL;
> -
> - for (userfunc = userfuncs; userfunc->old_name; userfunc++) {
> - func = lpc_create_func(&obj->kobj, userfunc);
> - if (!func)
> + for (func = obj->funcs; func->old_name; func++) {
> + func->new_addr = (unsigned long)func->new_func;
> + func->state = LPC_DISABLED;
> + ops = &func->fops;
> + ops->private = func;
> + ops->func = lpc_ftrace_handler;
> + ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;
> +
> + /* sysfs */
> + func->kobj = kobject_create_and_add(func->old_name, obj->kobj);
> + if (!func->kobj)
> goto free;
> - list_add(&func->list, &obj->funcs);
> }
> +
> return 0;
> free:
> - lpc_free_funcs(obj);
> + lpc_free_funcs_limited(obj, func);
> return -ENOMEM;
> }
>
> -static struct lpc_object *lpc_create_object(struct kobject *root,
> - struct lp_object *userobj)
> +static int lpc_init_objects(struct lpc_patch *patch)
> {
> struct lpc_object *obj;
> int ret;
>
> - /* alloc */
> - obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> - if (!obj)
> - return NULL;
> + for (obj = patch->objs; obj->name; obj++) {
> + /* obj->mod set by lpc_object_module_get() */
> + obj->state = LPC_DISABLED;
>
> - /* init */
> - INIT_LIST_HEAD(&obj->list);
> - obj->name = userobj->name;
> - obj->relocs = userobj->relocs;
> - obj->state = LPC_DISABLED;
> - /* obj->mod set by lpc_object_module_get() */
> - INIT_LIST_HEAD(&obj->funcs);
> -
> - /* sysfs */
> - ret = kobject_init_and_add(&obj->kobj, &lpc_ktype_object,
> - root, obj->name);
> - if (ret) {
> - kfree(obj);
> - return NULL;
> - }
> -
> - /* create functions */
> - ret = lpc_create_funcs(obj, userobj->funcs);
> - if (ret) {
> - kobject_put(&obj->kobj);
> - return NULL;
> - }
> -
> - return obj;
> -}
> -
> -static int lpc_create_objects(struct lpc_patch *patch,
> - struct lp_object *userobjs)
> -{
> - struct lp_object *userobj;
> - struct lpc_object *obj;
> -
> - if (!userobjs)
> - return -EINVAL;
> + /* sysfs */
> + obj->kobj = kobject_create_and_add(obj->name, &patch->kobj);
> + if (!obj->kobj)
> + goto free;
>
> - for (userobj = userobjs; userobj->name; userobj++) {
> - obj = lpc_create_object(&patch->kobj, userobj);
> - if (!obj)
> + /* init functions */
> + ret = lpc_init_funcs(obj);
> + if (ret) {
> + kobject_put(obj->kobj);
> goto free;
> - list_add(&obj->list, &patch->objs);
> + }
> }
> +
> return 0;
> free:
> - lpc_free_objects(patch);
> + lpc_free_objects_limited(patch, obj);
> return -ENOMEM;
> }
>
> -static int lpc_create_patch(struct lp_patch *userpatch)
> +static int lpc_init_patch(struct lpc_patch *patch)
> {
> - struct lpc_patch *patch;
> int ret;
>
> - /* alloc */
> - patch = kzalloc(sizeof(*patch), GFP_KERNEL);
> - if (!patch)
> - return -ENOMEM;
> + mutex_lock(&lpc_mutex);
>
> /* init */
> - INIT_LIST_HEAD(&patch->list);
> - patch->userpatch = userpatch;
> - patch->mod = userpatch->mod;
> patch->state = LPC_DISABLED;

Would be nice if we could detect a double call to lpc_register() and
return -EINVAL if the patch is already enabled (but I'm not sure how to
do that with this data layout). Probably not a big deal.


> - INIT_LIST_HEAD(&patch->objs);
>
> /* sysfs */
> ret = kobject_init_and_add(&patch->kobj, &lpc_ktype_patch,
> lpc_root_kobj, patch->mod->name);
> - if (ret) {
> - kfree(patch);
> + if (ret)
> return ret;
> - }
>
> /* create objects */
> - ret = lpc_create_objects(patch, userpatch->objs);
> + ret = lpc_init_objects(patch);
> if (ret) {
> kobject_put(&patch->kobj);
> return ret;
> }
>
> /* add to global list of patches */
> - mutex_lock(&lpc_mutex);
> list_add(&patch->list, &lpc_patches);
> +
> mutex_unlock(&lpc_mutex);
>
> return 0;
> @@ -902,7 +740,7 @@ static int lpc_create_patch(struct lp_patch *userpatch)
> ***********************************/
>
> /**
> - * lp_register_patch() - registers a patch
> + * lpc_register_patch() - registers a patch
> * @userpatch: Patch to be registered
> *
> * Allocates the data structure associated with the patch and

s/Allocates/Initializes/

> @@ -910,11 +748,11 @@ static int lpc_create_patch(struct lp_patch *userpatch)
> *
> * Return: 0 on success, otherwise error
> */
> -int lp_register_patch(struct lp_patch *userpatch)
> +int lpc_register_patch(struct lpc_patch *userpatch)

We can probably s/userpatch/patch/g .

> {
> int ret;
>
> - if (!userpatch || !userpatch->mod || !userpatch->objs)
> + if (!userpatch || !userpatch->mod)

Why?

> return -EINVAL;
>
> /*
> @@ -927,43 +765,37 @@ int lp_register_patch(struct lp_patch *userpatch)
> if (!try_module_get(userpatch->mod))
> return -ENODEV;
>
> - ret = lpc_create_patch(userpatch);
> + ret = lpc_init_patch(userpatch);
> if (ret)
> module_put(userpatch->mod);
>
> return ret;
> }
> -EXPORT_SYMBOL_GPL(lp_register_patch);
> +EXPORT_SYMBOL_GPL(lpc_register_patch);
>
> /**
> - * lp_unregister_patch() - unregisters a patch
> + * lpc_unregister_patch() - unregisters a patch
> * @userpatch: Disabled patch to be unregistered
> *
> * Frees the data structures and removes the sysfs interface.
> *
> * Return: 0 on success, otherwise error
> */
> -int lp_unregister_patch(struct lp_patch *userpatch)
> +int lpc_unregister_patch(struct lpc_patch *userpatch)
> {
> - struct lpc_patch *patch;
> int ret = 0;
>
> mutex_lock(&lpc_mutex);
> - patch = lpc_find_patch(userpatch);
> - if (!patch) {
> - ret = -ENODEV;
> - goto out;
> - }
> - if (patch->state == LPC_ENABLED) {
> + if (userpatch->state == LPC_ENABLED) {
> ret = -EINVAL;
> goto out;
> }
> - lpc_free_patch(patch);
> + lpc_free_patch(userpatch);
> out:
> mutex_unlock(&lpc_mutex);
> return ret;
> }
> -EXPORT_SYMBOL_GPL(lp_unregister_patch);
> +EXPORT_SYMBOL_GPL(lpc_unregister_patch);
>
> /************************************
> * entry/exit
> --
> 2.1.2
>

--
Josh

2014-11-20 19:57:17

by Seth Jennings

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Thu, Nov 20, 2014 at 11:35:52AM -0600, Josh Poimboeuf wrote:
> On Thu, Nov 20, 2014 at 02:10:33PM +0100, Miroslav Benes wrote:
> >
> > On Sun, 16 Nov 2014, Seth Jennings wrote:
> >
> > > 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.
> > >
> > > Signed-off-by: Seth Jennings <[email protected]>
> >
> > Hi,
> >
> > below is the patch which merges the internal and external data structures
> > (so it is only one part of our original patch for version 1). Apart from
> > that I tried to make minimal changes to the code. Only unnecessary
> > kobjects were removed and I renamed lpc_create_* functions to lpc_init_*
> > as it made more sense in this approach, I think.
> >
> > I hope this clearly shows our point of view stated previously. What do
> > you say?
>
> Thanks for rebasing to v2 and splitting up the patches! Personally I'm
> ok with this patch (though I do have a few comments below).

Thanks Josh :)

Miroslav, before you send out a revision on this patch, I'm merging it
for v3 right now. I'll fixup any trivial fixes from this email.

I'm putting the finishing touches on v3 now. Hopefully it will make
everyone happy, or happier, with your changes merged. Should be getting
close...

Thanks,
Seth

>
> > Next, I'll look at the three level hierarchy and sysfs directory and see
> > if we can make it simpler yet keep its advantages.
> >
> > Regards,
> >
> > Miroslav Benes
> > SUSE Labs
> >
> > -- >8 --
> > From aba839eb6b3292b193843715bfce7834969c0c17 Mon Sep 17 00:00:00 2001
> > From: Miroslav Benes <[email protected]>
> > Date: Wed, 19 Nov 2014 16:06:35 +0100
> > Subject: [PATCH] Remove the data duplication in internal and public structures
> >
> > The split of internal and external structures is cleaner and makes the API more
> > stable. But it makes the code more complicated. It requires more space and data
> > copying. Also the one letter difference of the names (lp_ vs. lpc_ prefix)
> > causes confusion.
> >
> > The API is not a real issue for live patching. We take care neither of backward
> > nor forward compatibility. The dependency between a patch and kernel is even
> > more strict than by version. They have to use the same configuration and the
> > same build environment.
> >
> > This patch merge the external and internal structures into one. The structures
> > are initialized using ".item = value" syntax. Therefore the API is basically as
> > stable as it was before. We could later even hide it under some helper macros
> > if requested.
> >
> > For the purpose if this patch, we used the prefix "lpc". It allows to make as
> > less changes as possible and show the real effect. If the patch is accepted, it
> > would make sense to merge it into the original patch and even use another
> > common prefix, for example the proposed "klp".
> >
> > Signed-off-by: Miroslav Benes <[email protected]>
> > ---
> > include/linux/livepatch.h | 47 +++++--
> > kernel/livepatch/core.c | 338 ++++++++++++----------------------------------
> > 2 files changed, 121 insertions(+), 264 deletions(-)
> >
> > diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> > index 8b68fef..f16de32 100644
> > --- a/include/linux/livepatch.h
> > +++ b/include/linux/livepatch.h
> > @@ -21,10 +21,23 @@
> > #define _LINUX_LIVEPATCH_H_
> >
> > #include <linux/module.h>
> > +#include <linux/ftrace.h>
> >
> > /* TODO: add kernel-doc for structures once agreed upon */
> >
> > -struct lp_func {
> > +enum lpc_state {
> > + LPC_DISABLED,
> > + LPC_ENABLED
> > +};
> > +
> > +struct lpc_func {
> > + /* internal */
>
> Would it be a little clearer to list the external fields first?
>
> > + struct kobject *kobj;
> > + struct ftrace_ops fops;
> > + enum lpc_state state;
> > + unsigned long new_addr;
>
> I think this internal new_addr field isn't really needed since we
> already have the external new_func field.
>
> > +
> > + /* external */
> > const char *old_name; /* function to be patched */
> > void *new_func; /* replacement function in patch module */
> > /*
> > @@ -38,7 +51,7 @@ struct lp_func {
> > unsigned long old_addr;
> > };
> >
> > -struct lp_reloc {
> > +struct lpc_reloc {
> > unsigned long dest;
> > unsigned long src;
> > unsigned long type;
> > @@ -47,21 +60,33 @@ struct lp_reloc {
> > int external;
> > };
> >
> > -struct lp_object {
> > +struct lpc_object {
> > + /* internal */
> > + struct kobject *kobj;
> > + struct module *mod; /* module associated with object */
> > + enum lpc_state state;
> > +
> > + /* external */
> > const char *name; /* "vmlinux" or module name */
> > - struct lp_func *funcs;
> > - struct lp_reloc *relocs;
> > + struct lpc_reloc *relocs;
> > + struct lpc_func *funcs;
> > };
> >
> > -struct lp_patch {
> > +struct lpc_patch {
> > + /* internal */
> > + struct list_head list;
> > + struct kobject kobj;
> > + enum lpc_state state;
> > +
> > + /* external */
> > struct module *mod; /* module containing the patch */
> > - struct lp_object *objs;
> > + struct lpc_object *objs;
> > };
> >
> > -int lp_register_patch(struct lp_patch *);
> > -int lp_unregister_patch(struct lp_patch *);
> > -int lp_enable_patch(struct lp_patch *);
> > -int lp_disable_patch(struct lp_patch *);
> > +extern int lpc_register_patch(struct lpc_patch *);
> > +extern int lpc_unregister_patch(struct lpc_patch *);
> > +extern int lpc_enable_patch(struct lpc_patch *);
> > +extern int lpc_disable_patch(struct lpc_patch *);
> >
> > #include <asm/livepatch.h>
> >
> > diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> > index e67c176..6586959 100644
> > --- a/kernel/livepatch/core.c
> > +++ b/kernel/livepatch/core.c
> > @@ -32,58 +32,9 @@
> > * Core structures
> > ************************************/
> >
> > -/*
> > - * lp_ structs vs lpc_ structs
> > - *
> > - * For each element (patch, object, func) in the live-patching code,
> > - * there are two types with two different prefixes: lp_ and lpc_.
> > - *
> > - * Structures used by the live-patch modules to register with this core module
> > - * are prefixed with lp_ (live patching). These structures are part of the
> > - * registration API and are defined in livepatch.h. The structures used
> > - * internally by this core module are prefixed with lpc_ (live patching core).
> > - */
> > -
> > static DEFINE_MUTEX(lpc_mutex);
> > static LIST_HEAD(lpc_patches);
> >
> > -enum lpc_state {
> > - LPC_DISABLED,
> > - LPC_ENABLED
> > -};
> > -
> > -struct lpc_func {
> > - struct list_head list;
> > - struct kobject kobj;
> > - struct ftrace_ops fops;
> > - enum lpc_state state;
> > -
> > - const char *old_name;
> > - unsigned long new_addr;
> > - unsigned long old_addr;
> > -};
> > -
> > -struct lpc_object {
> > - struct list_head list;
> > - struct kobject kobj;
> > - struct module *mod; /* module associated with object */
> > - enum lpc_state state;
> > -
> > - const char *name;
> > - struct list_head funcs;
> > - struct lp_reloc *relocs;
> > -};
> > -
> > -struct lpc_patch {
> > - struct list_head list;
> > - struct kobject kobj;
> > - struct lp_patch *userpatch; /* for correlation during unregister */
> > - enum lpc_state state;
> > -
> > - struct module *mod;
> > - struct list_head objs;
> > -};
> > -
> > /*******************************************
> > * Helpers
> > *******************************************/
> > @@ -262,7 +213,7 @@ static int lpc_write_object_relocations(struct module *pmod,
> > struct lpc_object *obj)
> > {
> > int ret;
> > - struct lp_reloc *reloc;
> > + struct lpc_reloc *reloc;
> >
> > for (reloc = obj->relocs; reloc->name; reloc++) {
> > if (!strcmp(obj->name, "vmlinux")) {
> > @@ -360,7 +311,7 @@ static int lpc_disable_object(struct lpc_object *obj)
> > struct lpc_func *func;
> > int ret;
> >
> > - list_for_each_entry(func, &obj->funcs, list) {
> > + for (func = obj->funcs; func->old_name; func++) {
> > if (func->state != LPC_ENABLED)
> > continue;
> > ret = lpc_disable_func(func);
> > @@ -387,7 +338,8 @@ static int lpc_enable_object(struct module *pmod, struct lpc_object *obj)
> > if (ret)
> > goto unregister;
> > }
> > - list_for_each_entry(func, &obj->funcs, list) {
> > +
> > + for (func = obj->funcs; func->old_name; func++) {
> > ret = lpc_find_verify_func_addr(func, obj->name);
> > if (ret)
> > goto unregister;
> > @@ -408,25 +360,14 @@ unregister:
> > * enable/disable
> > ******************************/
> >
> > -static struct lpc_patch *lpc_find_patch(struct lp_patch *userpatch)
> > -{
> > - struct lpc_patch *patch;
> > -
> > - list_for_each_entry(patch, &lpc_patches, list)
> > - if (patch->userpatch == userpatch)
> > - return patch;
> > -
> > - return NULL;
> > -}
> > -
> > -static int lpc_disable_patch(struct lpc_patch *patch)
> > +static int __lpc_disable_patch(struct lpc_patch *patch)
> > {
> > struct lpc_object *obj;
> > int ret;
> >
> > pr_notice("disabling patch '%s'\n", patch->mod->name);
> >
> > - list_for_each_entry(obj, &patch->objs, list) {
> > + for (obj = patch->objs; obj->name; obj++) {
> > if (obj->state != LPC_ENABLED)
> > continue;
> > ret = lpc_disable_object(obj);
> > @@ -439,32 +380,26 @@ static int lpc_disable_patch(struct lpc_patch *patch)
> > }
> >
> > /**
> > - * lp_disable_patch() - disables a registered patch
> > + * lpc_disable_patch() - disables a registered patch
> > * @userpatch: The registered, enabled patch to be disabled
> > *
> > * Unregisters the patched functions from ftrace.
> > *
> > * Return: 0 on success, otherwise error
> > */
> > -int lp_disable_patch(struct lp_patch *userpatch)
> > +int lpc_disable_patch(struct lpc_patch *patch)
> > {
> > - struct lpc_patch *patch;
> > int ret;
> >
> > mutex_lock(&lpc_mutex);
> > - patch = lpc_find_patch(userpatch);
> > - if (!patch) {
> > - ret = -ENODEV;
> > - goto out;
> > - }
> > - ret = lpc_disable_patch(patch);
> > -out:
> > + ret = __lpc_disable_patch(patch);
> > mutex_unlock(&lpc_mutex);
> > +
> > return ret;
> > }
> > -EXPORT_SYMBOL_GPL(lp_disable_patch);
> > +EXPORT_SYMBOL_GPL(lpc_disable_patch);
> >
> > -static int lpc_enable_patch(struct lpc_patch *patch)
> > +static int __lpc_enable_patch(struct lpc_patch *patch)
> > {
> > struct lpc_object *obj;
> > int ret;
> > @@ -477,7 +412,7 @@ static int lpc_enable_patch(struct lpc_patch *patch)
> >
> > pr_notice("enabling patch '%s'\n", patch->mod->name);
> >
> > - list_for_each_entry(obj, &patch->objs, list) {
> > + for (obj = patch->objs; obj->name; obj++) {
> > if (!lpc_find_object_module(obj))
> > continue;
> > ret = lpc_enable_object(patch->mod, obj);
> > @@ -493,7 +428,7 @@ unregister:
> > }
> >
> > /**
> > - * lp_enable_patch() - enables a registered patch
> > + * lpc_enable_patch() - enables a registered patch
> > * @userpatch: The registered, disabled patch to be enabled
> > *
> > * Performs the needed symbol lookups and code relocations,
> > @@ -501,23 +436,17 @@ unregister:
> > *
> > * Return: 0 on success, otherwise error
> > */
> > -int lp_enable_patch(struct lp_patch *userpatch)
> > +int lpc_enable_patch(struct lpc_patch *patch)
> > {
> > - struct lpc_patch *patch;
> > int ret;
> >
> > mutex_lock(&lpc_mutex);
> > - patch = lpc_find_patch(userpatch);
> > - if (!patch) {
> > - ret = -ENODEV;
> > - goto out;
> > - }
> > - ret = lpc_enable_patch(patch);
> > -out:
> > + ret = __lpc_enable_patch(patch);
> > mutex_unlock(&lpc_mutex);
> > +
> > return ret;
> > }
> > -EXPORT_SYMBOL_GPL(lp_enable_patch);
> > +EXPORT_SYMBOL_GPL(lpc_enable_patch);
> >
> > /******************************
> > * module notifier
> > @@ -568,7 +497,7 @@ static int lpc_module_notify(struct notifier_block *nb, unsigned long action,
> > list_for_each_entry(patch, &lpc_patches, list) {
> > if (patch->state == LPC_DISABLED)
> > continue;
> > - list_for_each_entry(obj, &patch->objs, list) {
> > + for (obj = patch->objs; obj->name; obj++) {
> > if (strcmp(obj->name, mod->name))
> > continue;
> > if (action == MODULE_STATE_COMING) {
> > @@ -624,11 +553,11 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
> > }
> >
> > if (val == LPC_ENABLED) {
> > - ret = lpc_enable_patch(patch);
> > + ret = __lpc_enable_patch(patch);
> > if (ret)
> > goto err;
> > } else {
> > - ret = lpc_disable_patch(patch);
> > + ret = __lpc_disable_patch(patch);
> > if (ret)
> > goto err;
> > }
> > @@ -677,7 +606,6 @@ static void lpc_kobj_release_patch(struct kobject *kobj)
> > patch = container_of(kobj, struct lpc_patch, kobj);
> > if (!list_empty(&patch->list))
> > list_del(&patch->list);
> > - kfree(patch);
> > }
> >
> > static struct kobj_type lpc_ktype_patch = {
> > @@ -686,212 +614,122 @@ static struct kobj_type lpc_ktype_patch = {
> > .default_attrs = lpc_patch_attrs
> > };
> >
> > -static void lpc_kobj_release_object(struct kobject *kobj)
> > -{
> > - struct lpc_object *obj;
> > -
> > - obj = container_of(kobj, struct lpc_object, kobj);
> > - if (!list_empty(&obj->list))
> > - list_del(&obj->list);
> > - kfree(obj);
> > -}
> > -
> > -static struct kobj_type lpc_ktype_object = {
> > - .release = lpc_kobj_release_object,
> > - .sysfs_ops = &kobj_sysfs_ops,
> > -};
> > -
> > -static void lpc_kobj_release_func(struct kobject *kobj)
> > -{
> > - struct lpc_func *func;
> > -
> > - func = container_of(kobj, struct lpc_func, kobj);
> > - if (!list_empty(&func->list))
> > - list_del(&func->list);
> > - kfree(func);
> > -}
> > -
> > -static struct kobj_type lpc_ktype_func = {
> > - .release = lpc_kobj_release_func,
> > - .sysfs_ops = &kobj_sysfs_ops,
> > -};
> > -
> > /*********************************
> > * structure allocation
> > ********************************/
> >
> > -static void lpc_free_funcs(struct lpc_object *obj)
> > +/*
> > + * Free all functions' kobjects in the array up to some limit. When limit is
> > + * NULL, all kobjects are freed.
> > + */
> > +static void lpc_free_funcs_limited(struct lpc_object *obj,
> > + struct lpc_func *limit)
> > {
> > - struct lpc_func *func, *funcsafe;
> > + struct lpc_func *func;
> >
> > - list_for_each_entry_safe(func, funcsafe, &obj->funcs, list)
> > - kobject_put(&func->kobj);
> > + for (func = obj->funcs; func->old_name && func != limit; func++)
> > + kobject_put(func->kobj);
> > }
> >
> > -static void lpc_free_objects(struct lpc_patch *patch)
> > +/*
> > + * Free all objects' kobjects in the array up to some limit. When limit is
> > + * NULL, all kobjects are freed.
> > + */
> > +static void lpc_free_objects_limited(struct lpc_patch *patch,
> > + struct lpc_object *limit)
> > {
> > - struct lpc_object *obj, *objsafe;
> > + struct lpc_object *obj;
> >
> > - list_for_each_entry_safe(obj, objsafe, &patch->objs, list) {
> > - lpc_free_funcs(obj);
> > - kobject_put(&obj->kobj);
> > + for (obj = patch->objs; obj->name && obj != limit; obj++) {
> > + lpc_free_funcs_limited(obj, NULL);
> > + kobject_put(obj->kobj);
> > }
> > }
> >
> > static void lpc_free_patch(struct lpc_patch *patch)
> > {
> > - lpc_free_objects(patch);
> > + lpc_free_objects_limited(patch, NULL);
> > kobject_put(&patch->kobj);
> > }
> >
> > -static struct lpc_func *lpc_create_func(struct kobject *root,
> > - struct lp_func *userfunc)
> > +static int lpc_init_funcs(struct lpc_object *obj)
> > {
> > struct lpc_func *func;
> > struct ftrace_ops *ops;
> > - int ret;
> > -
> > - /* alloc */
> > - func = kzalloc(sizeof(*func), GFP_KERNEL);
> > - if (!func)
> > - return NULL;
> > -
> > - /* init */
> > - INIT_LIST_HEAD(&func->list);
> > - func->old_name = userfunc->old_name;
> > - func->new_addr = (unsigned long)userfunc->new_func;
> > - func->old_addr = userfunc->old_addr;
> > - func->state = LPC_DISABLED;
> > - ops = &func->fops;
> > - ops->private = func;
> > - ops->func = lpc_ftrace_handler;
> > - ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;
>
> FTRACE_OPS_FL_DYNAMIC is probably no longer appropriate here, since I
> think both kGraft and kpatch allocate the func struct statically.
>
> However, we can't know for sure how the user allocated it, so maybe we
> need to change funcs->fops to be a pointer, and allocate it here?
>
> That may also require embedding kobj into the func struct so that ops
> can be freed with lpc_kobj_release_func.
>
> > -
> > - /* sysfs */
> > - ret = kobject_init_and_add(&func->kobj, &lpc_ktype_func,
> > - root, func->old_name);
> > - if (ret) {
> > - kfree(func);
> > - return NULL;
> > - }
> > -
> > - return func;
> > -}
> > -
> > -static int lpc_create_funcs(struct lpc_object *obj,
> > - struct lp_func *userfuncs)
> > -{
> > - struct lp_func *userfunc;
> > - struct lpc_func *func;
> >
> > - if (!userfuncs)
> > - return -EINVAL;
> > -
> > - for (userfunc = userfuncs; userfunc->old_name; userfunc++) {
> > - func = lpc_create_func(&obj->kobj, userfunc);
> > - if (!func)
> > + for (func = obj->funcs; func->old_name; func++) {
> > + func->new_addr = (unsigned long)func->new_func;
> > + func->state = LPC_DISABLED;
> > + ops = &func->fops;
> > + ops->private = func;
> > + ops->func = lpc_ftrace_handler;
> > + ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;
> > +
> > + /* sysfs */
> > + func->kobj = kobject_create_and_add(func->old_name, obj->kobj);
> > + if (!func->kobj)
> > goto free;
> > - list_add(&func->list, &obj->funcs);
> > }
> > +
> > return 0;
> > free:
> > - lpc_free_funcs(obj);
> > + lpc_free_funcs_limited(obj, func);
> > return -ENOMEM;
> > }
> >
> > -static struct lpc_object *lpc_create_object(struct kobject *root,
> > - struct lp_object *userobj)
> > +static int lpc_init_objects(struct lpc_patch *patch)
> > {
> > struct lpc_object *obj;
> > int ret;
> >
> > - /* alloc */
> > - obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> > - if (!obj)
> > - return NULL;
> > + for (obj = patch->objs; obj->name; obj++) {
> > + /* obj->mod set by lpc_object_module_get() */
> > + obj->state = LPC_DISABLED;
> >
> > - /* init */
> > - INIT_LIST_HEAD(&obj->list);
> > - obj->name = userobj->name;
> > - obj->relocs = userobj->relocs;
> > - obj->state = LPC_DISABLED;
> > - /* obj->mod set by lpc_object_module_get() */
> > - INIT_LIST_HEAD(&obj->funcs);
> > -
> > - /* sysfs */
> > - ret = kobject_init_and_add(&obj->kobj, &lpc_ktype_object,
> > - root, obj->name);
> > - if (ret) {
> > - kfree(obj);
> > - return NULL;
> > - }
> > -
> > - /* create functions */
> > - ret = lpc_create_funcs(obj, userobj->funcs);
> > - if (ret) {
> > - kobject_put(&obj->kobj);
> > - return NULL;
> > - }
> > -
> > - return obj;
> > -}
> > -
> > -static int lpc_create_objects(struct lpc_patch *patch,
> > - struct lp_object *userobjs)
> > -{
> > - struct lp_object *userobj;
> > - struct lpc_object *obj;
> > -
> > - if (!userobjs)
> > - return -EINVAL;
> > + /* sysfs */
> > + obj->kobj = kobject_create_and_add(obj->name, &patch->kobj);
> > + if (!obj->kobj)
> > + goto free;
> >
> > - for (userobj = userobjs; userobj->name; userobj++) {
> > - obj = lpc_create_object(&patch->kobj, userobj);
> > - if (!obj)
> > + /* init functions */
> > + ret = lpc_init_funcs(obj);
> > + if (ret) {
> > + kobject_put(obj->kobj);
> > goto free;
> > - list_add(&obj->list, &patch->objs);
> > + }
> > }
> > +
> > return 0;
> > free:
> > - lpc_free_objects(patch);
> > + lpc_free_objects_limited(patch, obj);
> > return -ENOMEM;
> > }
> >
> > -static int lpc_create_patch(struct lp_patch *userpatch)
> > +static int lpc_init_patch(struct lpc_patch *patch)
> > {
> > - struct lpc_patch *patch;
> > int ret;
> >
> > - /* alloc */
> > - patch = kzalloc(sizeof(*patch), GFP_KERNEL);
> > - if (!patch)
> > - return -ENOMEM;
> > + mutex_lock(&lpc_mutex);
> >
> > /* init */
> > - INIT_LIST_HEAD(&patch->list);
> > - patch->userpatch = userpatch;
> > - patch->mod = userpatch->mod;
> > patch->state = LPC_DISABLED;
>
> Would be nice if we could detect a double call to lpc_register() and
> return -EINVAL if the patch is already enabled (but I'm not sure how to
> do that with this data layout). Probably not a big deal.
>
>
> > - INIT_LIST_HEAD(&patch->objs);
> >
> > /* sysfs */
> > ret = kobject_init_and_add(&patch->kobj, &lpc_ktype_patch,
> > lpc_root_kobj, patch->mod->name);
> > - if (ret) {
> > - kfree(patch);
> > + if (ret)
> > return ret;
> > - }
> >
> > /* create objects */
> > - ret = lpc_create_objects(patch, userpatch->objs);
> > + ret = lpc_init_objects(patch);
> > if (ret) {
> > kobject_put(&patch->kobj);
> > return ret;
> > }
> >
> > /* add to global list of patches */
> > - mutex_lock(&lpc_mutex);
> > list_add(&patch->list, &lpc_patches);
> > +
> > mutex_unlock(&lpc_mutex);
> >
> > return 0;
> > @@ -902,7 +740,7 @@ static int lpc_create_patch(struct lp_patch *userpatch)
> > ***********************************/
> >
> > /**
> > - * lp_register_patch() - registers a patch
> > + * lpc_register_patch() - registers a patch
> > * @userpatch: Patch to be registered
> > *
> > * Allocates the data structure associated with the patch and
>
> s/Allocates/Initializes/
>
> > @@ -910,11 +748,11 @@ static int lpc_create_patch(struct lp_patch *userpatch)
> > *
> > * Return: 0 on success, otherwise error
> > */
> > -int lp_register_patch(struct lp_patch *userpatch)
> > +int lpc_register_patch(struct lpc_patch *userpatch)
>
> We can probably s/userpatch/patch/g .
>
> > {
> > int ret;
> >
> > - if (!userpatch || !userpatch->mod || !userpatch->objs)
> > + if (!userpatch || !userpatch->mod)
>
> Why?
>
> > return -EINVAL;
> >
> > /*
> > @@ -927,43 +765,37 @@ int lp_register_patch(struct lp_patch *userpatch)
> > if (!try_module_get(userpatch->mod))
> > return -ENODEV;
> >
> > - ret = lpc_create_patch(userpatch);
> > + ret = lpc_init_patch(userpatch);
> > if (ret)
> > module_put(userpatch->mod);
> >
> > return ret;
> > }
> > -EXPORT_SYMBOL_GPL(lp_register_patch);
> > +EXPORT_SYMBOL_GPL(lpc_register_patch);
> >
> > /**
> > - * lp_unregister_patch() - unregisters a patch
> > + * lpc_unregister_patch() - unregisters a patch
> > * @userpatch: Disabled patch to be unregistered
> > *
> > * Frees the data structures and removes the sysfs interface.
> > *
> > * Return: 0 on success, otherwise error
> > */
> > -int lp_unregister_patch(struct lp_patch *userpatch)
> > +int lpc_unregister_patch(struct lpc_patch *userpatch)
> > {
> > - struct lpc_patch *patch;
> > int ret = 0;
> >
> > mutex_lock(&lpc_mutex);
> > - patch = lpc_find_patch(userpatch);
> > - if (!patch) {
> > - ret = -ENODEV;
> > - goto out;
> > - }
> > - if (patch->state == LPC_ENABLED) {
> > + if (userpatch->state == LPC_ENABLED) {
> > ret = -EINVAL;
> > goto out;
> > }
> > - lpc_free_patch(patch);
> > + lpc_free_patch(userpatch);
> > out:
> > mutex_unlock(&lpc_mutex);
> > return ret;
> > }
> > -EXPORT_SYMBOL_GPL(lp_unregister_patch);
> > +EXPORT_SYMBOL_GPL(lpc_unregister_patch);
> >
> > /************************************
> > * entry/exit
> > --
> > 2.1.2
> >
>
> --
> Josh
> --
> To unsubscribe from this list: send the line "unsubscribe live-patching" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-11-21 14:38:10

by Miroslav Benes

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Thu, 20 Nov 2014, Josh Poimboeuf wrote:

> On Thu, Nov 20, 2014 at 02:10:33PM +0100, Miroslav Benes wrote:
> >
> > On Sun, 16 Nov 2014, Seth Jennings wrote:
> >
> > > 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.
> > >
> > > Signed-off-by: Seth Jennings <[email protected]>
> >
> > Hi,
> >
> > below is the patch which merges the internal and external data structures
> > (so it is only one part of our original patch for version 1). Apart from
> > that I tried to make minimal changes to the code. Only unnecessary
> > kobjects were removed and I renamed lpc_create_* functions to lpc_init_*
> > as it made more sense in this approach, I think.
> >
> > I hope this clearly shows our point of view stated previously. What do
> > you say?
>
> Thanks for rebasing to v2 and splitting up the patches! Personally I'm
> ok with this patch (though I do have a few comments below).

Great.

> > Next, I'll look at the three level hierarchy and sysfs directory and see
> > if we can make it simpler yet keep its advantages.
> >
> > Regards,
> >
> > Miroslav Benes
> > SUSE Labs
> >
> > -- >8 --
> > From aba839eb6b3292b193843715bfce7834969c0c17 Mon Sep 17 00:00:00 2001
> > From: Miroslav Benes <[email protected]>
> > Date: Wed, 19 Nov 2014 16:06:35 +0100
> > Subject: [PATCH] Remove the data duplication in internal and public structures
> >
> > The split of internal and external structures is cleaner and makes the API more
> > stable. But it makes the code more complicated. It requires more space and data
> > copying. Also the one letter difference of the names (lp_ vs. lpc_ prefix)
> > causes confusion.
> >
> > The API is not a real issue for live patching. We take care neither of backward
> > nor forward compatibility. The dependency between a patch and kernel is even
> > more strict than by version. They have to use the same configuration and the
> > same build environment.
> >
> > This patch merge the external and internal structures into one. The structures
> > are initialized using ".item = value" syntax. Therefore the API is basically as
> > stable as it was before. We could later even hide it under some helper macros
> > if requested.
> >
> > For the purpose if this patch, we used the prefix "lpc". It allows to make as
> > less changes as possible and show the real effect. If the patch is accepted, it
> > would make sense to merge it into the original patch and even use another
> > common prefix, for example the proposed "klp".
> >
> > Signed-off-by: Miroslav Benes <[email protected]>
> > ---
> > include/linux/livepatch.h | 47 +++++--
> > kernel/livepatch/core.c | 338 ++++++++++++----------------------------------
> > 2 files changed, 121 insertions(+), 264 deletions(-)
> >
> > diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> > index 8b68fef..f16de32 100644
> > --- a/include/linux/livepatch.h
> > +++ b/include/linux/livepatch.h
> > @@ -21,10 +21,23 @@
> > #define _LINUX_LIVEPATCH_H_
> >
> > #include <linux/module.h>
> > +#include <linux/ftrace.h>
> >
> > /* TODO: add kernel-doc for structures once agreed upon */
> >
> > -struct lp_func {
> > +enum lpc_state {
> > + LPC_DISABLED,
> > + LPC_ENABLED
> > +};
> > +
> > +struct lpc_func {
> > + /* internal */
>
> Would it be a little clearer to list the external fields first?

It would. It was not possible in v1 of my patch as flexible array needs to
be the last in struct (struct lpc_func funcs[]). Now as it is a pointer it
makes perfect sense to have the external fields first.

> > + struct kobject *kobj;
> > + struct ftrace_ops fops;
> > + enum lpc_state state;
> > + unsigned long new_addr;
>
> I think this internal new_addr field isn't really needed since we
> already have the external new_func field.

True.

> > +
> > + /* external */
> > const char *old_name; /* function to be patched */
> > void *new_func; /* replacement function in patch module */
> > /*
> > @@ -38,7 +51,7 @@ struct lp_func {
> > unsigned long old_addr;
> > };

[...]

> > -static struct lpc_func *lpc_create_func(struct kobject *root,
> > - struct lp_func *userfunc)
> > +static int lpc_init_funcs(struct lpc_object *obj)
> > {
> > struct lpc_func *func;
> > struct ftrace_ops *ops;
> > - int ret;
> > -
> > - /* alloc */
> > - func = kzalloc(sizeof(*func), GFP_KERNEL);
> > - if (!func)
> > - return NULL;
> > -
> > - /* init */
> > - INIT_LIST_HEAD(&func->list);
> > - func->old_name = userfunc->old_name;
> > - func->new_addr = (unsigned long)userfunc->new_func;
> > - func->old_addr = userfunc->old_addr;
> > - func->state = LPC_DISABLED;
> > - ops = &func->fops;
> > - ops->private = func;
> > - ops->func = lpc_ftrace_handler;
> > - ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;
>
> FTRACE_OPS_FL_DYNAMIC is probably no longer appropriate here, since I
> think both kGraft and kpatch allocate the func struct statically.

Yes, that's right. Also looking at kGraft code we have only SAVE_REGS
there.

> However, we can't know for sure how the user allocated it, so maybe we
> need to change funcs->fops to be a pointer, and allocate it here?
>
> That may also require embedding kobj into the func struct so that ops
> can be freed with lpc_kobj_release_func.

Hm, dynamic allocation in lpc_init_funcs and freeing in kobj release
callback would probably be better.

> > -
> > - /* sysfs */
> > - ret = kobject_init_and_add(&func->kobj, &lpc_ktype_func,
> > - root, func->old_name);
> > - if (ret) {
> > - kfree(func);
> > - return NULL;
> > - }
> > -
> > - return func;
> > -}
> > -
> > -static int lpc_create_funcs(struct lpc_object *obj,
> > - struct lp_func *userfuncs)
> > -{
> > - struct lp_func *userfunc;
> > - struct lpc_func *func;
> >
> > - if (!userfuncs)
> > - return -EINVAL;
> > -
> > - for (userfunc = userfuncs; userfunc->old_name; userfunc++) {
> > - func = lpc_create_func(&obj->kobj, userfunc);
> > - if (!func)
> > + for (func = obj->funcs; func->old_name; func++) {
> > + func->new_addr = (unsigned long)func->new_func;
> > + func->state = LPC_DISABLED;
> > + ops = &func->fops;
> > + ops->private = func;
> > + ops->func = lpc_ftrace_handler;
> > + ops->flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_DYNAMIC;
> > +
> > + /* sysfs */
> > + func->kobj = kobject_create_and_add(func->old_name, obj->kobj);
> > + if (!func->kobj)
> > goto free;
> > - list_add(&func->list, &obj->funcs);
> > }
> > +
> > return 0;
> > free:
> > - lpc_free_funcs(obj);
> > + lpc_free_funcs_limited(obj, func);
> > return -ENOMEM;
> > }
> >
> > -static struct lpc_object *lpc_create_object(struct kobject *root,
> > - struct lp_object *userobj)
> > +static int lpc_init_objects(struct lpc_patch *patch)
> > {
> > struct lpc_object *obj;
> > int ret;
> >
> > - /* alloc */
> > - obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> > - if (!obj)
> > - return NULL;
> > + for (obj = patch->objs; obj->name; obj++) {
> > + /* obj->mod set by lpc_object_module_get() */
> > + obj->state = LPC_DISABLED;
> >
> > - /* init */
> > - INIT_LIST_HEAD(&obj->list);
> > - obj->name = userobj->name;
> > - obj->relocs = userobj->relocs;
> > - obj->state = LPC_DISABLED;
> > - /* obj->mod set by lpc_object_module_get() */
> > - INIT_LIST_HEAD(&obj->funcs);
> > -
> > - /* sysfs */
> > - ret = kobject_init_and_add(&obj->kobj, &lpc_ktype_object,
> > - root, obj->name);
> > - if (ret) {
> > - kfree(obj);
> > - return NULL;
> > - }
> > -
> > - /* create functions */
> > - ret = lpc_create_funcs(obj, userobj->funcs);
> > - if (ret) {
> > - kobject_put(&obj->kobj);
> > - return NULL;
> > - }
> > -
> > - return obj;
> > -}
> > -
> > -static int lpc_create_objects(struct lpc_patch *patch,
> > - struct lp_object *userobjs)
> > -{
> > - struct lp_object *userobj;
> > - struct lpc_object *obj;
> > -
> > - if (!userobjs)
> > - return -EINVAL;
> > + /* sysfs */
> > + obj->kobj = kobject_create_and_add(obj->name, &patch->kobj);
> > + if (!obj->kobj)
> > + goto free;
> >
> > - for (userobj = userobjs; userobj->name; userobj++) {
> > - obj = lpc_create_object(&patch->kobj, userobj);
> > - if (!obj)
> > + /* init functions */
> > + ret = lpc_init_funcs(obj);
> > + if (ret) {
> > + kobject_put(obj->kobj);
> > goto free;
> > - list_add(&obj->list, &patch->objs);
> > + }
> > }
> > +
> > return 0;
> > free:
> > - lpc_free_objects(patch);
> > + lpc_free_objects_limited(patch, obj);
> > return -ENOMEM;
> > }
> >
> > -static int lpc_create_patch(struct lp_patch *userpatch)
> > +static int lpc_init_patch(struct lpc_patch *patch)
> > {
> > - struct lpc_patch *patch;
> > int ret;
> >
> > - /* alloc */
> > - patch = kzalloc(sizeof(*patch), GFP_KERNEL);
> > - if (!patch)
> > - return -ENOMEM;
> > + mutex_lock(&lpc_mutex);
> >
> > /* init */
> > - INIT_LIST_HEAD(&patch->list);
> > - patch->userpatch = userpatch;
> > - patch->mod = userpatch->mod;
> > patch->state = LPC_DISABLED;
>
> Would be nice if we could detect a double call to lpc_register() and
> return -EINVAL if the patch is already enabled (but I'm not sure how to
> do that with this data layout). Probably not a big deal.

If I understand you correctly we could return -EINVAL in
lpc_register_patch if the patch is already enabled. If it is disabled a
double call of register could be detected only with addition of some flag
to lpc_patch struct. I don't know if it is worth it.

> > - INIT_LIST_HEAD(&patch->objs);
> >
> > /* sysfs */
> > ret = kobject_init_and_add(&patch->kobj, &lpc_ktype_patch,
> > lpc_root_kobj, patch->mod->name);
> > - if (ret) {
> > - kfree(patch);
> > + if (ret)
> > return ret;
> > - }
> >
> > /* create objects */
> > - ret = lpc_create_objects(patch, userpatch->objs);
> > + ret = lpc_init_objects(patch);
> > if (ret) {
> > kobject_put(&patch->kobj);
> > return ret;
> > }
> >
> > /* add to global list of patches */
> > - mutex_lock(&lpc_mutex);
> > list_add(&patch->list, &lpc_patches);
> > +
> > mutex_unlock(&lpc_mutex);
> >
> > return 0;
> > @@ -902,7 +740,7 @@ static int lpc_create_patch(struct lp_patch *userpatch)
> > ***********************************/
> >
> > /**
> > - * lp_register_patch() - registers a patch
> > + * lpc_register_patch() - registers a patch
> > * @userpatch: Patch to be registered
> > *
> > * Allocates the data structure associated with the patch and
>
> s/Allocates/Initializes/

Yes.

>
> > @@ -910,11 +748,11 @@ static int lpc_create_patch(struct lp_patch *userpatch)
> > *
> > * Return: 0 on success, otherwise error
> > */
> > -int lp_register_patch(struct lp_patch *userpatch)
> > +int lpc_register_patch(struct lpc_patch *userpatch)
>
> We can probably s/userpatch/patch/g .

Definitely better.

> > {
> > int ret;
> >
> > - if (!userpatch || !userpatch->mod || !userpatch->objs)
> > + if (!userpatch || !userpatch->mod)
>
> Why?

Oh, this is a remnant of some intermediate state where objs in lpc_patch
was declared as a flexible array (struct lpc_object objs[]). In that case
the check was redundant. In the end I changed it back to a pointer and
forgot to fix that. So thanks. It means that similar check for
obj->funcs should be returned to lpc_init_funcs or somewhere. Sorry for
that.

Thank you
Mira

> > return -EINVAL;
> >
> > /*
> > @@ -927,43 +765,37 @@ int lp_register_patch(struct lp_patch *userpatch)
> > if (!try_module_get(userpatch->mod))
> > return -ENODEV;
> >
> > - ret = lpc_create_patch(userpatch);
> > + ret = lpc_init_patch(userpatch);
> > if (ret)
> > module_put(userpatch->mod);
> >
> > return ret;
> > }
> > -EXPORT_SYMBOL_GPL(lp_register_patch);
> > +EXPORT_SYMBOL_GPL(lpc_register_patch);
> >
> > /**
> > - * lp_unregister_patch() - unregisters a patch
> > + * lpc_unregister_patch() - unregisters a patch
> > * @userpatch: Disabled patch to be unregistered
> > *
> > * Frees the data structures and removes the sysfs interface.
> > *
> > * Return: 0 on success, otherwise error
> > */
> > -int lp_unregister_patch(struct lp_patch *userpatch)
> > +int lpc_unregister_patch(struct lpc_patch *userpatch)
> > {
> > - struct lpc_patch *patch;
> > int ret = 0;
> >
> > mutex_lock(&lpc_mutex);
> > - patch = lpc_find_patch(userpatch);
> > - if (!patch) {
> > - ret = -ENODEV;
> > - goto out;
> > - }
> > - if (patch->state == LPC_ENABLED) {
> > + if (userpatch->state == LPC_ENABLED) {
> > ret = -EINVAL;
> > goto out;
> > }
> > - lpc_free_patch(patch);
> > + lpc_free_patch(userpatch);
> > out:
> > mutex_unlock(&lpc_mutex);
> > return ret;
> > }
> > -EXPORT_SYMBOL_GPL(lp_unregister_patch);
> > +EXPORT_SYMBOL_GPL(lpc_unregister_patch);
> >
> > /************************************
> > * entry/exit
> > --
> > 2.1.2
> >
>
> --
> Josh
>

--
Miroslav Benes
SUSE Labs

2014-11-21 14:41:06

by Miroslav Benes

[permalink] [raw]
Subject: Re: [PATCHv2 2/3] kernel: add support for live patching

On Thu, 20 Nov 2014, Seth Jennings wrote:

> On Thu, Nov 20, 2014 at 11:35:52AM -0600, Josh Poimboeuf wrote:
> > On Thu, Nov 20, 2014 at 02:10:33PM +0100, Miroslav Benes wrote:
> > >
> > > On Sun, 16 Nov 2014, Seth Jennings wrote:
> > >
> > > > 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.
> > > >
> > > > Signed-off-by: Seth Jennings <[email protected]>
> > >
> > > Hi,
> > >
> > > below is the patch which merges the internal and external data structures
> > > (so it is only one part of our original patch for version 1). Apart from
> > > that I tried to make minimal changes to the code. Only unnecessary
> > > kobjects were removed and I renamed lpc_create_* functions to lpc_init_*
> > > as it made more sense in this approach, I think.
> > >
> > > I hope this clearly shows our point of view stated previously. What do
> > > you say?
> >
> > Thanks for rebasing to v2 and splitting up the patches! Personally I'm
> > ok with this patch (though I do have a few comments below).
>
> Thanks Josh :)
>
> Miroslav, before you send out a revision on this patch, I'm merging it
> for v3 right now. I'll fixup any trivial fixes from this email.
>
> I'm putting the finishing touches on v3 now. Hopefully it will make
> everyone happy, or happier, with your changes merged. Should be getting
> close...
>
> Thanks,
> Seth

Ok, thank you for the fixes and merging. I'll take a closer look at v3 to
be sure that everything is ok.

Mira