[PATCH v3 1/2] livepatch: Add dynamic klp_object and klp_func iterators

From: Jason Baron
Date: Wed Sep 27 2017 - 23:42:40 EST


In preparation to introducing atomic replace, introduce iterators for
klp_func and klp_object, such that objects and functions can be dynamically
allocated (needed for atomic replace). This patch is intended to
effectively be a no-op until atomic replace is introduced.

Signed-off-by: Jason Baron <jbaron@xxxxxxxxxx>
Cc: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
Cc: Jessica Yu <jeyu@xxxxxxxxxx>
Cc: Jiri Kosina <jikos@xxxxxxxxxx>
Cc: Miroslav Benes <mbenes@xxxxxxx>
Cc: Petr Mladek <pmladek@xxxxxxxx>
---
include/linux/livepatch.h | 81 +++++++++++++++++++++-------------
kernel/livepatch/core.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 160 insertions(+), 31 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 194991e..e03ce11 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/ftrace.h>
#include <linux/completion.h>
+#include <linux/list.h>

#if IS_ENABLED(CONFIG_LIVEPATCH)

@@ -36,18 +37,20 @@

/**
* struct klp_func - function structure for live patching
- * @old_name: name of the function to be patched
- * @new_func: pointer to the patched function code
- * @old_sympos: a hint indicating which symbol position the old function
- * can be found (optional)
- * @immediate: patch the func immediately, bypassing safety mechanisms
- * @old_addr: the address of the function being patched
- * @kobj: kobject for sysfs resources
- * @stack_node: list node for klp_ops func_stack list
- * @old_size: size of the old function
- * @new_size: size of the new function
- * @patched: the func has been added to the klp_ops list
- * @transition: the func is currently being applied or reverted
+ * @old_name: name of the function to be patched
+ * @new_func: pointer to the patched function code
+ * @old_sympos: a hint indicating which symbol position the old function
+ * can be found (optional)
+ * @immediate: patch the func immediately, bypassing safety mechanisms
+ * @old_addr: the address of the function being patched
+ * @kobj: kobject for sysfs resources
+ * @stack_node: list node for klp_ops func_stack list
+ * @nop_func_entry: links dynamically allocated struct klp_func to struct
+ * klp_object
+ * @old_size: size of the old function
+ * @new_size: size of the new function
+ * @patched: the func has been added to the klp_ops list
+ * @transition: the func is currently being applied or reverted
*
* The patched and transition variables define the func's patching state. When
* patching, a func is always in one of the following states:
@@ -82,6 +85,7 @@ struct klp_func {
unsigned long old_addr;
struct kobject kobj;
struct list_head stack_node;
+ struct list_head nop_func_entry;
unsigned long old_size, new_size;
bool patched;
bool transition;
@@ -89,12 +93,15 @@ struct klp_func {

/**
* struct klp_object - kernel object structure for live patching
- * @name: module name (or NULL for vmlinux)
- * @funcs: function entries for functions to be patched in the object
- * @kobj: kobject for sysfs resources
- * @mod: kernel module associated with the patched object
- * (NULL for vmlinux)
- * @patched: the object's funcs have been added to the klp_ops list
+ * @name: module name (or NULL for vmlinux)
+ * @funcs: function entries for functions to be patched in the object
+ * @kobj: kobject for sysfs resources
+ * @nop_func_list: head of list for dynamically allocated struct klp_func
+ * @nop_obj_entry: links dynamically allocated struct klp_object to struct
+ * klp_patch
+ * @mod: kernel module associated with the patched object
+ * (NULL for vmlinux)
+ * @patched: the object's funcs have been added to the klp_ops list
*/
struct klp_object {
/* external */
@@ -103,19 +110,22 @@ struct klp_object {

/* internal */
struct kobject kobj;
+ struct list_head nop_func_list;
+ struct list_head nop_obj_entry;
struct module *mod;
bool patched;
};

/**
* struct klp_patch - patch structure for live patching
- * @mod: reference to the live patch module
- * @objs: object entries for kernel objects to be patched
- * @immediate: patch all funcs immediately, bypassing safety mechanisms
- * @list: list node for global list of registered patches
- * @kobj: kobject for sysfs resources
- * @enabled: the patch is enabled (but operation may be incomplete)
- * @finish: for waiting till it is safe to remove the patch module
+ * @mod: reference to the live patch module
+ * @objs: object entries for kernel objects to be patched
+ * @immediate: patch all funcs immediately, bypassing safety mechanisms
+ * @list: list node for global list of registered patches
+ * @kobj: kobject for sysfs resources
+ * @nop_obj_list: head of list for dynamically allocated struct klp_object
+ * @enabled: the patch is enabled (but operation may be incomplete)
+ * @finish: for waiting till it is safe to remove the patch module
*/
struct klp_patch {
/* external */
@@ -126,17 +136,26 @@ struct klp_patch {
/* internal */
struct list_head list;
struct kobject kobj;
+ struct list_head nop_obj_list;
bool enabled;
struct completion finish;
};

-#define klp_for_each_object(patch, obj) \
- for (obj = patch->objs; obj->funcs || obj->name; obj++)
+struct klp_object *klp_obj_iter_init(struct klp_patch *patch);
+struct klp_object *klp_obj_iter_next(struct klp_patch *patch,
+ struct klp_object *obj);

-#define klp_for_each_func(obj, func) \
- for (func = obj->funcs; \
- func->old_name || func->new_func || func->old_sympos; \
- func++)
+#define klp_for_each_object(patch, obj) \
+ for (obj = klp_obj_iter_init(patch); obj; \
+ obj = klp_obj_iter_next(patch, obj))
+
+struct klp_func *klp_func_iter_init(struct klp_object *obj);
+struct klp_func *klp_func_iter_next(struct klp_object *obj,
+ struct klp_func *func);
+
+#define klp_for_each_func(obj, func) \
+ for (func = klp_func_iter_init(obj); func; \
+ func = klp_func_iter_next(obj, func))

int klp_register_patch(struct klp_patch *);
int klp_unregister_patch(struct klp_patch *);
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index b9628e4..0d92fe6 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -49,6 +49,100 @@ static LIST_HEAD(klp_patches);

static struct kobject *klp_root_kobj;

+static bool klp_valid_obj_entry(struct klp_object *obj)
+{
+ return obj->funcs || obj->name;
+}
+
+struct klp_object *klp_obj_iter_init(struct klp_patch *patch)
+{
+ if (klp_valid_obj_entry(patch->objs))
+ return patch->objs;
+
+ return NULL;
+}
+
+struct klp_object *klp_obj_iter_next(struct klp_patch *patch,
+ struct klp_object *obj)
+{
+ struct klp_object *next_obj = NULL;
+
+ /*
+ * Statically defined objects are in NULL-ended array.
+ * Only dynamic ones are in the nop_obj_list.
+ */
+ if (list_empty(&obj->nop_obj_entry)) {
+ next_obj = obj + 1;
+ if (klp_valid_obj_entry(next_obj))
+ goto out;
+ next_obj = NULL;
+ if (!list_empty(&patch->nop_obj_list))
+ next_obj = list_entry(patch->nop_obj_list.next,
+ struct klp_object,
+ nop_obj_entry);
+ goto out;
+ }
+
+ if (obj->nop_obj_entry.next != &patch->nop_obj_list)
+ next_obj = list_entry(obj->nop_obj_entry.next,
+ struct klp_object,
+ nop_obj_entry);
+
+out:
+ return next_obj;
+}
+
+static bool klp_valid_func_entry(struct klp_func *func)
+{
+ return func->old_name || func->new_func || func->old_sympos;
+}
+
+struct klp_func *klp_func_iter_init(struct klp_object *obj)
+{
+ /* statically allocated */
+ if (list_empty(&obj->nop_obj_entry)) {
+ if (klp_valid_func_entry(obj->funcs))
+ return obj->funcs;
+ } else {
+ if (!list_empty(obj->nop_func_list.next))
+ return list_entry(obj->nop_func_list.next,
+ struct klp_func,
+ nop_func_entry);
+ }
+
+ return NULL;
+}
+
+struct klp_func *klp_func_iter_next(struct klp_object *obj,
+ struct klp_func *func)
+{
+ struct klp_func *next_func = NULL;
+
+ /*
+ * Statically defined functions are in NULL-ended array.
+ * Only dynamic ones are in the nop_func_list.
+ */
+ if (list_empty(&func->nop_func_entry)) {
+ next_func = func + 1;
+ if (klp_valid_func_entry(next_func))
+ goto out;
+ next_func = NULL;
+ if (!list_empty(&obj->nop_func_list))
+ next_func = list_entry(obj->nop_func_list.next,
+ struct klp_func,
+ nop_func_entry);
+ goto out;
+ }
+
+ if (func->nop_func_entry.next != &obj->nop_func_list)
+ next_func = list_entry(func->nop_func_entry.next,
+ struct klp_func,
+ nop_func_entry);
+
+out:
+ return next_func;
+}
+
static bool klp_is_module(struct klp_object *obj)
{
return obj->name;
@@ -709,6 +803,20 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
return ret;
}

+static void klp_init_patch_dyn(struct klp_patch *patch)
+{
+ struct klp_object *obj;
+ struct klp_func *func;
+
+ INIT_LIST_HEAD(&patch->nop_obj_list);
+ klp_for_each_object(patch, obj) {
+ INIT_LIST_HEAD(&obj->nop_obj_entry);
+ INIT_LIST_HEAD(&obj->nop_func_list);
+ klp_for_each_func(obj, func)
+ INIT_LIST_HEAD(&func->nop_func_entry);
+ }
+}
+
static int klp_init_patch(struct klp_patch *patch)
{
struct klp_object *obj;
@@ -729,6 +837,8 @@ static int klp_init_patch(struct klp_patch *patch)
return ret;
}

+ klp_init_patch_dyn(patch);
+
klp_for_each_object(patch, obj) {
ret = klp_init_object(patch, obj);
if (ret)
--
2.6.1