On 01/25/2018 11:02 AM, Petr Mladek wrote: > From: Jason Baron <jbaron@xxxxxxxxxx> > > Sometimes we would like to revert a particular fix. Currently, this > is not easy because we want to keep all other fixes active and we > could revert only the last applied patch. > > One solution would be to apply new patch that implemented all > the reverted functions like in the original code. It would work > as expected but there will be unnecessary redirections. In addition, > it would also require knowing which functions need to be reverted at > build time. > > Another problem is when there are many patches that touch the same > functions. There might be dependencies between patches that are > not enforced on the kernel side. Also it might be pretty hard to > actually prepare the patch and ensure compatibility with > the other patches. > > A better solution would be to create cumulative patch and say that > it replaces all older ones. > > This patch adds a new "replace" flag to struct klp_patch. When it is > enabled, a set of 'nop' klp_func will be dynamically created for all > functions that are already being patched but that will not longer be > modified by the new patch. They are temporarily used as a new target > during the patch transition. > > There are used several simplifications: > > + nops' structures are generated already when the patch is registered. > All registered patches are taken into account, even the disabled ones. > As a result, there might be more nops than are really needed when > the patch is enabled and some disabled patches were removed before. > But we are on the safe side and it simplifies the implementation. > Especially we could reuse the existing init() functions. Also freeing > is easier because the same nops are created and removed only once. > > Alternative solution would be to create nops when the patch is enabled. > But then we would need to complicated to reuse the init() functions > and error paths. It would increase the risk of errors because of > late kobject initialization. It would need tricky waiting for > freed kobjects when finalizing a reverted enable transaction. > > + The replaced patches are removed from the stack and cannot longer > be enabled directly. Otherwise, we would need to implement a more > complex logic of handling the stack of patches. It might be hard > to come with a reasonable semantic. > > A fallback is to remove (rmmod) the replaced patches and register > (insmod) them again. > > + Nops are handled like normal function patches. It reduces changes > in the existing code. > > It would be possible to copy internal values when they are allocated > and make short cuts in init() functions. It would be possible to use > the fact that old_func and new_func point to the same function and > do not init new_func and new_size at all. It would be possible to > detect nop func in ftrace handler and just leave. But all these would > just complicate the code and maintenance. > > + The callbacks from the replaced patches are not called. It would be > pretty hard to define a reasonable semantic and implement it. > > It might even be counter-productive. The new patch is cumulative. > It is supposed to include most of the changes from older patches. > In most cases, it will not want to call pre_unpatch() post_unpatch() > callbacks from the replaced patches. It would disable/break things > for no good reasons. Also it should be easier to handle various > scenarios in a single script in the new patch than think about > interactions caused by running many scripts from older patches. > No to say that the old scripts even would not expect to be called > in this situation. > > Signed-off-by: Jason Baron <jbaron@xxxxxxxxxx> > [pmladek@xxxxxxxx: Split, reuse existing code, simplified] Hi Petr, Thanks for cleaning this up - it looks good. I just had one comment/issue below thus far. > 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> > Cc: Petr Mladek <pmladek@xxxxxxxx> > --- > include/linux/livepatch.h | 3 + > kernel/livepatch/core.c | 162 +++++++++++++++++++++++++++++++++++++++++- > kernel/livepatch/core.h | 4 ++ > kernel/livepatch/transition.c | 36 ++++++++++ > 4 files changed, 203 insertions(+), 2 deletions(-) > > diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h > index 21cad200f949..9d44d105b278 100644 > --- a/include/linux/livepatch.h > +++ b/include/linux/livepatch.h > @@ -42,6 +42,7 @@ > enum klp_func_type { > KLP_FUNC_ANY = -1, /* Substitute any type */ > KLP_FUNC_ORIGINAL = 0, /* Original statically defined structure */ > + KLP_FUNC_NOP, /* Dynamically allocated NOP function patch */ > }; > > /** > @@ -153,6 +154,7 @@ 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 > + * @replace: replace all already registered patches > * @list: list node for global list of registered patches > * @kobj: kobject for sysfs resources > * @obj_list: head of list for struct klp_object > @@ -163,6 +165,7 @@ struct klp_patch { > /* external */ > struct module *mod; > struct klp_object *objs; > + bool replace; > > /* internal */ > struct list_head list; > diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c > index 6ad3195d995a..c606b291dfcd 100644 > --- a/kernel/livepatch/core.c > +++ b/kernel/livepatch/core.c > @@ -142,6 +142,21 @@ static bool klp_initialized(void) > return !!klp_root_kobj; > } > > +static struct klp_func *klp_find_func(struct klp_object *obj, > + struct klp_func *old_func) > +{ > + struct klp_func *func; > + > + klp_for_each_func(obj, func) { > + if ((strcmp(old_func->old_name, func->old_name) == 0) && > + (old_func->old_sympos == func->old_sympos)) { > + return func; > + } > + } > + > + return NULL; > +} > + > static struct klp_object *klp_find_object(struct klp_patch *patch, > struct klp_object *old_obj) > { > @@ -342,6 +357,39 @@ static int klp_write_object_relocations(struct module *pmod, > return ret; > } > > +/* > + * This function removes replaced patches from both func_stack > + * and klp_patches stack. > + * > + * We could be pretty aggressive here. It is called in situation > + * when these structures are not longer accessible. All functions > + * are redirected using the klp_transition_patch. They use either > + * a new code or they in the original code because of the special > + * nop function patches. > + */ > +void klp_throw_away_replaced_patches(struct klp_patch *new_patch, > + bool keep_module) > +{ > + struct klp_patch *old_patch, *tmp_patch; > + > + list_for_each_entry_safe(old_patch, tmp_patch, &klp_patches, list) { > + if (old_patch == new_patch) > + return; > + > + klp_unpatch_objects(old_patch, KLP_FUNC_ANY); > + old_patch->enabled = false; > + > + /* > + * Replaced patches could not get re-enabled to keep > + * the code sane. > + */ > + list_del_init(&old_patch->list); I'm wondering if this should be: list_move(&old_patch->list, &klp_replaced_patches); Which ensures that the only valid transition after a patch has been 'replaced' is an 'unregister'. Otherwise, klp_replaced_patches is not used anywhere. Thanks, -Jason > + > + if (!keep_module) > + module_put(old_patch->mod); > + } > +} > + > static int __klp_disable_patch(struct klp_patch *patch) > { > struct klp_object *obj; > @@ -537,7 +585,7 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, > if (!klp_is_patch_usable(patch)) { > /* > * Module with the patch could either disappear meanwhile or is > - * not properly initialized yet. > + * not properly initialized yet or the patch was just replaced. > */ > ret = -EINVAL; > goto err; > @@ -662,8 +710,16 @@ static struct attribute *klp_patch_attrs[] = { > /* > * Dynamically allocated objects and functions. > */ > +static void klp_free_func_nop(struct klp_func *func) > +{ > + kfree(func->old_name); > + kfree(func); > +} > + > static void klp_free_func_dynamic(struct klp_func *func) > { > + if (func->ftype == KLP_FUNC_NOP) > + klp_free_func_nop(func); > } > > static void klp_free_object_dynamic(struct klp_object *obj) > @@ -691,7 +747,7 @@ static struct klp_object *klp_alloc_object_dynamic(const char *name) > return obj; > } > > -struct klp_object *klp_get_or_add_object(struct klp_patch *patch, > +static struct klp_object *klp_get_or_add_object(struct klp_patch *patch, > struct klp_object *old_obj) > { > struct klp_object *obj; > @@ -708,6 +764,102 @@ struct klp_object *klp_get_or_add_object(struct klp_patch *patch, > return obj; > } > > +static struct klp_func *klp_alloc_func_nop(struct klp_func *old_func, > + struct klp_object *obj) > +{ > + struct klp_func *func; > + > + func = kzalloc(sizeof(*func), GFP_KERNEL); > + if (!func) > + return ERR_PTR(-ENOMEM); > + > + if (old_func->old_name) { > + func->old_name = kstrdup(old_func->old_name, GFP_KERNEL); > + if (!func->old_name) { > + kfree(func); > + return ERR_PTR(-ENOMEM); > + } > + } > + func->old_sympos = old_func->old_sympos; > + /* NOP func is the same as using the original implementation. */ > + func->new_func = (void *)old_func->old_addr; > + func->ftype = KLP_FUNC_NOP; > + > + return func; > +} > + > +static int klp_add_func_nop(struct klp_object *obj, > + struct klp_func *old_func) > +{ > + struct klp_func *func; > + > + func = klp_find_func(obj, old_func); > + > + if (func) > + return 0; > + > + func = klp_alloc_func_nop(old_func, obj); > + if (IS_ERR(func)) > + return PTR_ERR(func); > + > + klp_init_func_list(obj, func); > + > + return 0; > +} > + > +static int klp_add_object_nops(struct klp_patch *patch, > + struct klp_object *old_obj) > +{ > + struct klp_object *obj; > + struct klp_func *old_func; > + int err = 0; > + > + obj = klp_get_or_add_object(patch, old_obj); > + if (IS_ERR(obj)) > + return PTR_ERR(obj); > + > + klp_for_each_func(old_obj, old_func) { > + err = klp_add_func_nop(obj, old_func); > + if (err) > + return err; > + } > + > + return 0; > +} > + > +/* > + * Add 'nop' functions which simply return to the caller to run > + * the original function. The 'nop' functions are added to a > + * patch to facilitate a 'replace' mode > + * > + * The nops are generated for all patches on the stack when > + * the new patch is initialized. It is safe even though some > + * older patches might get disabled and removed before the > + * new one is enabled. In the worst case, there might be nops > + * there will not be really needed. But it does not harm and > + * simplifies the implementation a lot. Especially we could > + * use the init functions as is. > + */ > +static int klp_add_nops(struct klp_patch *patch) > +{ > + struct klp_patch *old_patch; > + struct klp_object *old_obj; > + int err = 0; > + > + if (WARN_ON(!patch->replace)) > + return -EINVAL; > + > + list_for_each_entry(old_patch, &klp_patches, list) { > + klp_for_each_object(old_patch, old_obj) { > + err = klp_add_object_nops(patch, old_obj); > + if (err) > + return err; > + } > + } > + > + return 0; > +} > + > /* > * Patch release framework must support the following scenarios: > * > @@ -952,6 +1104,12 @@ static int klp_init_patch(struct klp_patch *patch) > return ret; > } > > + if (patch->replace) { > + ret = klp_add_nops(patch); > + if (ret) > + goto free; > + } > + > klp_for_each_object(patch, obj) { > ret = klp_init_object(patch, obj); > if (ret) > diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h > index 48a83d4364cf..43184a5318d8 100644 > --- a/kernel/livepatch/core.h > +++ b/kernel/livepatch/core.h > @@ -6,6 +6,10 @@ > > extern struct mutex klp_mutex; > > +void klp_throw_away_replaced_patches(struct klp_patch *new_patch, > + bool keep_module); > +void klp_free_objects(struct klp_patch *patch, enum klp_func_type ftype); > + > static inline bool klp_is_object_loaded(struct klp_object *obj) > { > return !obj->name || obj->mod; > diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c > index 6917100fbe79..3f6cf5b9e048 100644 > --- a/kernel/livepatch/transition.c > +++ b/kernel/livepatch/transition.c > @@ -87,6 +87,33 @@ static void klp_complete_transition(void) > klp_transition_patch->mod->name, > klp_target_state == KLP_PATCHED ? "patching" : "unpatching"); > > + /* > + * For replace patches, we disable all previous patches, and replace > + * the dynamic no-op functions by removing the ftrace hook. > + */ > + if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) { > + /* > + * Make sure klp_ftrace_handler() can no longer see functions > + * from the replaced patches. Then all functions will be > + * redirected using klp_transition_patch. They will use > + * either a new code or they will stay in the original code > + * because of the nop funcs' structures. > + */ > + if (klp_forced) > + klp_synchronize_transition(); > + > + klp_throw_away_replaced_patches(klp_transition_patch, > + klp_forced); > + > + /* > + * There is no need to synchronize the transition after removing > + * nops. They must be the last on the func_stack. Ftrace > + * gurantees that nobody will stay in the trampoline after > + * the ftrace handler is unregistered. > + */ > + klp_unpatch_objects(klp_transition_patch, KLP_FUNC_NOP); > + } > + > if (klp_target_state == KLP_UNPATCHED) { > /* > * All tasks have transitioned to KLP_UNPATCHED so we can now > @@ -143,6 +170,15 @@ static void klp_complete_transition(void) > if (!klp_forced && klp_target_state == KLP_UNPATCHED) > module_put(klp_transition_patch->mod); > > + /* > + * We do not need to wait until the objects are really freed. > + * The patch must be on the bottom of the stack. Therefore it > + * will never replace anything else. The only important thing > + * is that we wait when the patch is being unregistered. > + */ > + if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) > + klp_free_objects(klp_transition_patch, KLP_FUNC_NOP); > + > klp_target_state = KLP_UNDEFINED; > klp_transition_patch = NULL; > } > -- 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