PATCH v6 3/6] livepatch: Initial support for dynamic structures

From: Petr Mladek
Date: Thu Jan 25 2018 - 11:02:31 EST


From: Jason Baron <jbaron@xxxxxxxxxx>

We are going to add a feature called atomic replace. It will allow to
create a patch that would replace all already registered patches.
For this, we will need to dynamically create funcs' and objects'
for functions that are not longer patched.

This patch adds basic framework to handle such dynamic structures.

It adds enum klp_func_type that allows to distinguish the dynamically
allocated funcs' structures. Note that objects' structures do not have
a clear type. Namely the static objects' structures might list both static
and dynamic funcs' structures.

The function type is then used to limit klp_free() functions. We will
want to free the dynamic structures separately when they are not
longer needed. At the same time, we also want to make our life easier,
and introduce _any_ type that will allow to process all existing
structures in one go.

We need to be careful here. First, objects' structures must be freed
only when all listed funcs' structures are freed. Also we must avoid
double free. Both problems are solved by removing the freed structures
from the list.

Also note that klp_free*() functions are called also in klp_init_patch()
error path when only some kobjects have been initialized. The other
dynamic structures must be freed immediately by calling the respective
klp_free_*_dynamic() functions.

Finally, the dynamic objects' structures are generic. The respective
klp_allocate_object_dynamic() and klp_free_object_dynamic() can
be implemented here. On the other hand, klp_free_func_dynamic()
is empty. It must be updated when a particular dynamic
klp_func_type is introduced.

Signed-off-by: Jason Baron <jbaron@xxxxxxxxxx>
[pmladek@xxxxxxxx: Converted into a generic API]
Signed-off-by: Petr Mladek <pmladek@xxxxxxxx>
Cc: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
Cc: Jessica Yu <jeyu@xxxxxxxxxx>
Cc: Jiri Kosina <jikos@xxxxxxxxxx>
Cc: Miroslav Benes <mbenes@xxxxxxx>
---
include/linux/livepatch.h | 37 +++++++++++-
kernel/livepatch/core.c | 139 ++++++++++++++++++++++++++++++++++++++++------
2 files changed, 159 insertions(+), 17 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index e5db2ba7e2a5..21cad200f949 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -35,12 +35,22 @@
#define KLP_UNPATCHED 0
#define KLP_PATCHED 1

+/*
+ * Function type is used to distinguish dynamically allocated structures
+ * and limit some operations.
+ */
+enum klp_func_type {
+ KLP_FUNC_ANY = -1, /* Substitute any type */
+ KLP_FUNC_ORIGINAL = 0, /* Original statically defined structure */
+};
+
/**
* 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)
+ * @ftype: distinguish static and dynamic structures
* @old_addr: the address of the function being patched
* @kobj: kobject for sysfs resources
* @stack_node: list node for klp_ops func_stack list
@@ -79,6 +89,7 @@ struct klp_func {
unsigned long old_sympos;

/* internal */
+ enum klp_func_type ftype;
unsigned long old_addr;
struct kobject kobj;
struct list_head stack_node;
@@ -164,17 +175,41 @@ struct klp_patch {
#define klp_for_each_object_static(patch, obj) \
for (obj = patch->objs; obj->funcs || obj->name; obj++)

+#define klp_for_each_object_safe(patch, obj, tmp_obj) \
+ list_for_each_entry_safe(obj, tmp_obj, &patch->obj_list, obj_entry)
+
#define klp_for_each_object(patch, obj) \
list_for_each_entry(obj, &patch->obj_list, obj_entry)

+/* Support also dynamically allocated struct klp_object */
#define klp_for_each_func_static(obj, func) \
for (func = obj->funcs; \
- func->old_name || func->new_func || func->old_sympos; \
+ func && (func->old_name || func->new_func || func->old_sympos); \
func++)

+#define klp_for_each_func_safe(obj, func, tmp_func) \
+ list_for_each_entry_safe(func, tmp_func, &obj->func_list, func_entry)
+
#define klp_for_each_func(obj, func) \
list_for_each_entry(func, &obj->func_list, func_entry)

+static inline bool klp_is_object_dynamic(struct klp_object *obj)
+{
+ return !obj->funcs;
+}
+
+static inline bool klp_is_func_dynamic(struct klp_func *func)
+{
+ WARN_ON_ONCE(func->ftype == KLP_FUNC_ANY);
+ return func->ftype != KLP_FUNC_ORIGINAL;
+}
+
+static inline bool klp_is_func_type(struct klp_func *func,
+ enum klp_func_type ftype)
+{
+ return ftype == KLP_FUNC_ANY || ftype == func->ftype;
+}
+
int klp_register_patch(struct klp_patch *);
int klp_unregister_patch(struct klp_patch *);
int klp_enable_patch(struct klp_patch *);
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 05d60035525d..6c06311f9ce3 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -124,6 +124,26 @@ static bool klp_initialized(void)
return !!klp_root_kobj;
}

+static struct klp_object *klp_find_object(struct klp_patch *patch,
+ struct klp_object *old_obj)
+{
+ struct klp_object *obj;
+ bool mod = klp_is_module(old_obj);
+
+ klp_for_each_object(patch, obj) {
+ if (mod) {
+ if (klp_is_module(obj) &&
+ strcmp(old_obj->name, obj->name) == 0) {
+ return obj;
+ }
+ } else if (!klp_is_module(obj)) {
+ return obj;
+ }
+ }
+
+ return NULL;
+}
+
struct klp_find_arg {
const char *objname;
const char *name;
@@ -621,6 +641,66 @@ static struct attribute *klp_patch_attrs[] = {
NULL
};

+/*
+ * Dynamically allocated objects and functions.
+ */
+static void klp_free_func_dynamic(struct klp_func *func)
+{
+}
+
+static void klp_free_object_dynamic(struct klp_object *obj)
+{
+ kfree(obj->name);
+ kfree(obj);
+}
+
+static struct klp_object *klp_alloc_object_dynamic(const char *name)
+{
+ struct klp_object *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return ERR_PTR(-ENOMEM);
+
+ if (name) {
+ obj->name = kstrdup(name, GFP_KERNEL);
+ if (!obj->name) {
+ kfree(obj);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ return obj;
+}
+
+struct klp_object *klp_get_or_add_object(struct klp_patch *patch,
+ struct klp_object *old_obj)
+{
+ struct klp_object *obj;
+
+ obj = klp_find_object(patch, old_obj);
+ if (obj)
+ return obj;
+
+ obj = klp_alloc_object_dynamic(old_obj->name);
+ if (IS_ERR(obj))
+ return obj;
+
+ klp_init_object_list(patch, obj);
+ return obj;
+}
+
+/*
+ * Patch release framework must support the following scenarios:
+ *
+ * + Asynchonous release is used when kobjects are initialized.
+ *
+ * + Direct release is used in error paths for structures that
+ * have not had kobj initialized yet.
+ *
+ * + Allow to release dynamic structures of the given type when
+ * they are not longer needed.
+ */
static void klp_kobj_release_patch(struct kobject *kobj)
{
struct klp_patch *patch;
@@ -637,6 +717,12 @@ static struct kobj_type klp_ktype_patch = {

static void klp_kobj_release_object(struct kobject *kobj)
{
+ struct klp_object *obj;
+
+ obj = container_of(kobj, struct klp_object, kobj);
+
+ if (klp_is_object_dynamic(obj))
+ klp_free_object_dynamic(obj);
}

static struct kobj_type klp_ktype_object = {
@@ -646,6 +732,12 @@ static struct kobj_type klp_ktype_object = {

static void klp_kobj_release_func(struct kobject *kobj)
{
+ struct klp_func *func;
+
+ func = container_of(kobj, struct klp_func, kobj);
+
+ if (klp_is_func_dynamic(func))
+ klp_free_func_dynamic(func);
}

static struct kobj_type klp_ktype_func = {
@@ -654,16 +746,25 @@ static struct kobj_type klp_ktype_func = {
};

/*
- * Free all funcs' that have the kobject initialized. Otherwise,
- * nothing is needed.
+ * Free all funcs' of the given ftype. Use the kobject when it has already
+ * been initialized. Otherwise, do it directly.
*/
-static void klp_free_funcs(struct klp_object *obj)
+static void klp_free_funcs(struct klp_object *obj,
+ enum klp_func_type ftype)
{
- struct klp_func *func;
+ struct klp_func *func, *tmp_func;
+
+ klp_for_each_func_safe(obj, func, tmp_func) {
+ if (!klp_is_func_type(func, ftype))
+ continue;
+
+ /* Avoid double free and allow to detect empty objects. */
+ list_del(&func->func_entry);

- klp_for_each_func(obj, func) {
if (func->kobj.state_initialized)
kobject_put(&func->kobj);
+ else if (klp_is_func_dynamic(func))
+ klp_free_func_dynamic(func);
}
}

@@ -679,24 +780,33 @@ static void klp_free_object_loaded(struct klp_object *obj)
}

/*
- * Free all funcs' and objects's that have the kobject initialized.
- * Otherwise, nothing is needed.
+ * Free all linked funcs' of the given ftype. Then free empty objects.
+ * Use the kobject when it has already been initialized. Otherwise,
+ * do it directly.
*/
-static void klp_free_objects(struct klp_patch *patch)
+void klp_free_objects(struct klp_patch *patch, enum klp_func_type ftype)
{
- struct klp_object *obj;
+ struct klp_object *obj, *tmp_obj;

- klp_for_each_object(patch, obj) {
- klp_free_funcs(obj);
+ klp_for_each_object_safe(patch, obj, tmp_obj) {
+ klp_free_funcs(obj, ftype);
+
+ if (!list_empty(&obj->func_list))
+ continue;
+
+ /* Avoid freeing the object twice. */
+ list_del(&obj->obj_entry);

if (obj->kobj.state_initialized)
kobject_put(&obj->kobj);
+ else if (klp_is_object_dynamic(obj))
+ klp_free_object_dynamic(obj);
}
}

static void klp_free_patch(struct klp_patch *patch)
{
- klp_free_objects(patch);
+ klp_free_objects(patch, KLP_FUNC_ANY);

if (!list_empty(&patch->list))
list_del(&patch->list);
@@ -777,9 +887,6 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
int ret;
const char *name;

- if (!obj->funcs)
- return -EINVAL;
-
obj->patched = false;
obj->mod = NULL;

@@ -840,7 +947,7 @@ static int klp_init_patch(struct klp_patch *patch)
return 0;

free:
- klp_free_objects(patch);
+ klp_free_objects(patch, KLP_FUNC_ANY);

mutex_unlock(&klp_mutex);

--
2.13.6