[RFC kgr on klp 5/9] livepatch: teach klp about consistency models

From: Jiri Slaby
Date: Mon May 04 2015 - 07:41:20 EST


We want more concise consistency models than simple is. This is a
preparation for other, more complex ones. It moves the simple handling
out of ftrace handler and is called as newly introduced struct
klp_cmodel->stub. This way, every model can implement its own handler.

On the top of that, I assume the structure will be extended over time.
For example, kGraft-like patching will need pre-patch and post-patch
hooks and more.

We store the models in a list and all have its ID, specified in every
patch. The ID is then looked up in the list and appropriate cmodel
used.

Signed-off-by: Jiri Slaby <jslaby@xxxxxxx>
---
include/linux/livepatch.h | 35 ++++++++++++++++++++++++++++++++
kernel/livepatch/Makefile | 2 +-
kernel/livepatch/cmodel-simple.c | 39 ++++++++++++++++++++++++++++++++++++
kernel/livepatch/core.c | 37 ++++++++++++++++++++++++++++++----
samples/livepatch/livepatch-sample.c | 1 +
5 files changed, 109 insertions(+), 5 deletions(-)
create mode 100644 kernel/livepatch/cmodel-simple.c

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index fabb067a3f1d..009f308ff756 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -23,11 +23,36 @@

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

#if IS_ENABLED(CONFIG_LIVEPATCH)

#include <asm/livepatch.h>

+struct klp_func;
+
+/**
+ * enum klp_cmodel_id - possible consistency models
+ */
+enum klp_cmodel_id {
+ KLP_CM_INVALID = 0,
+ KLP_CM_SIMPLE, /* LEAVE_FUNCTION and SWITCH_FUNCTION */
+};
+
+/**
+ * struct klp_cmodel - implementation of a consistency model
+ * @id: id of this model (from enum klp_cmodel_id)
+ * @list: member of klp_cmodel_list
+ * @stub: what to use as an ftrace handler (annotate with notrace!)
+ */
+struct klp_cmodel {
+ const enum klp_cmodel_id id;
+ struct list_head list;
+
+ void (*stub)(struct list_head *func_stack, struct klp_func *func,
+ struct pt_regs *regs);
+};
+
enum klp_state {
KLP_DISABLED,
KLP_ENABLED
@@ -42,6 +67,7 @@ enum klp_state {
* @kobj: kobject for sysfs resources
* @state: tracks function-level patch application state
* @stack_node: list node for klp_ops func_stack list
+ * @stub: cache of klp_patch.cmodel.stub
*/
struct klp_func {
/* external */
@@ -61,6 +87,8 @@ struct klp_func {
struct kobject kobj;
enum klp_state state;
struct list_head stack_node;
+ void (*stub)(struct list_head *func_stack, struct klp_func *func,
+ struct pt_regs *regs);
};

/**
@@ -108,19 +136,23 @@ struct klp_object {
* struct klp_patch - patch structure for live patching
* @mod: reference to the live patch module
* @objs: object entries for kernel objects to be patched
+ * @cmodel_id: consistency model used to apply this patch
* @list: list node for global list of registered patches
* @kobj: kobject for sysfs resources
* @state: tracks patch-level application state
+ * @cmodel: cmodel_id's implementation
*/
struct klp_patch {
/* external */
struct module *mod;
struct klp_object *objs;
+ const enum klp_cmodel_id cmodel_id;

/* internal */
struct list_head list;
struct kobject kobj;
enum klp_state state;
+ struct klp_cmodel *cmodel;
};

#define klp_for_each_object(patch, obj) \
@@ -144,6 +176,9 @@ int klp_unregister_patch(struct klp_patch *);
int klp_enable_patch(struct klp_patch *);
int klp_disable_patch(struct klp_patch *);

+void klp_init_cmodel_simple(void);
+void klp_register_cmodel(struct klp_cmodel *);
+
#endif /* CONFIG_LIVEPATCH */

#endif /* _LINUX_LIVEPATCH_H_ */
diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile
index e8780c0901d9..926533777247 100644
--- a/kernel/livepatch/Makefile
+++ b/kernel/livepatch/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_LIVEPATCH) += livepatch.o

-livepatch-objs := core.o
+livepatch-objs := core.o cmodel-simple.o
diff --git a/kernel/livepatch/cmodel-simple.c b/kernel/livepatch/cmodel-simple.c
new file mode 100644
index 000000000000..d4e430ff40c0
--- /dev/null
+++ b/kernel/livepatch/cmodel-simple.c
@@ -0,0 +1,39 @@
+/*
+ * cmodel-simple.c - KLP Simple Consistency Model
+ *
+ * Copyright (C) 2015 Seth Jennings <sjenning@xxxxxxxxxx>
+ * Copyright (C) 2015 SUSE
+ *
+ * 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/ptrace.h>
+#include <linux/list.h>
+#include <linux/livepatch.h>
+
+static void notrace klp_simple_stub(struct list_head *func_stack,
+ struct klp_func *func, struct pt_regs *regs)
+{
+ klp_arch_set_pc(regs, (unsigned long)func->new_func);
+}
+
+static struct klp_cmodel klp_simple_model = {
+ .id = KLP_CM_SIMPLE,
+ .stub = klp_simple_stub,
+};
+
+void klp_init_cmodel_simple(void)
+{
+ klp_register_cmodel(&klp_simple_model);
+}
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 2da42be84452..ab6a36688c93 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -58,6 +58,7 @@ static DEFINE_MUTEX(klp_mutex);

static LIST_HEAD(klp_patches);
static LIST_HEAD(klp_ops);
+static LIST_HEAD(klp_cmodel_list);

static struct kobject *klp_root_kobj;

@@ -319,18 +320,16 @@ static void notrace klp_ftrace_handler(unsigned long ip,
struct ftrace_ops *fops,
struct pt_regs *regs)
{
- struct klp_ops *ops;
+ struct klp_ops *ops = container_of(fops, struct klp_ops, fops);
struct klp_func *func;

- ops = container_of(fops, struct klp_ops, fops);
-
rcu_read_lock();
func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
stack_node);
if (WARN_ON_ONCE(!func))
goto unlock;

- klp_arch_set_pc(regs, (unsigned long)func->new_func);
+ func->stub(&ops->func_stack, func, regs);
unlock:
rcu_read_unlock();
}
@@ -720,6 +719,7 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
{
INIT_LIST_HEAD(&func->stack_node);
func->state = KLP_DISABLED;
+ func->stub = klp_object_to_patch(obj)->cmodel->stub;

return kobject_init_and_add(&func->kobj, &klp_ktype_func,
&obj->kobj, "%s", func->old_name);
@@ -790,13 +790,28 @@ free:
static int klp_init_patch(struct klp_patch *patch)
{
struct klp_object *obj;
+ struct klp_cmodel *cm, *cmodel = NULL;
int ret;

if (!patch->objs)
return -EINVAL;

+ list_for_each_entry(cm, &klp_cmodel_list, list) {
+ if (patch->cmodel_id == cm->id) {
+ cmodel = cm;
+ break;
+ }
+ }
+
+ if (!cmodel) {
+ pr_err("%s: patch '%ps' requires unknown consistency model %d\n",
+ __func__, patch, patch->cmodel_id);
+ return -EINVAL;
+ }
+
mutex_lock(&klp_mutex);

+ patch->cmodel = cmodel;
patch->state = KLP_DISABLED;

ret = kobject_init_and_add(&patch->kobj, &klp_ktype_patch,
@@ -893,6 +908,18 @@ int klp_register_patch(struct klp_patch *patch)
}
EXPORT_SYMBOL_GPL(klp_register_patch);

+/**
+ * klp_register_cmodel - register a consistency model
+ * @model: model to register
+ *
+ * This functions has to be synchronously called before klp_root_kobj is
+ * created in klp_init since we use no locking.
+ */
+void klp_register_cmodel(struct klp_cmodel *model)
+{
+ list_add_tail(&model->list, &klp_cmodel_list);
+}
+
static void klp_module_notify_coming(struct klp_patch *patch,
struct klp_object *obj)
{
@@ -993,6 +1020,8 @@ static int klp_init(void)
return -EINVAL;
}

+ klp_init_cmodel_simple();
+
ret = register_module_notifier(&klp_module_nb);
if (ret)
return ret;
diff --git a/samples/livepatch/livepatch-sample.c b/samples/livepatch/livepatch-sample.c
index fb8c8614e728..48621de040db 100644
--- a/samples/livepatch/livepatch-sample.c
+++ b/samples/livepatch/livepatch-sample.c
@@ -63,6 +63,7 @@ static struct klp_object objs[] = {
static struct klp_patch patch = {
.mod = THIS_MODULE,
.objs = objs,
+ .cmodel_id = KLP_CM_SIMPLE,
};

static int livepatch_init(void)
--
2.3.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/