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 -- To unsubscribe from this list: send the line "unsubscribe live-patching" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html