[RFC] livepatch: unpatch all klp_objects if klp_module_coming fails

From: Joe Lawrence
Date: Wed Sep 13 2017 - 16:51:13 EST


When an incoming module is considered for livepatching by
klp_module_coming(), it iterates over multiple patches and multiple
kernel objects in this order:

list_for_each_entry(patch, &klp_patches, list) {
klp_for_each_object(patch, obj) {

which means that if one of the kernel objects fail to patch for whatever
reason, klp_module_coming()'s error path should double back and unpatch
any previous kernel object that was patched for a previous patch.

Reported-by: Miroslav Benes <mbenes@xxxxxxx>
Signed-off-by: Joe Lawrence <joe.lawrence@xxxxxxxxxx>
---
kernel/livepatch/core.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index aca62c4b8616..7f5192618cc8 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -889,6 +889,8 @@ int klp_module_coming(struct module *mod)
goto err;
}

+pr_err("JL: klp_patch_object(%p) patch=%p obj->name: %s\n", obj, patch, obj->name);
+
ret = klp_patch_object(obj);
if (ret) {
pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
@@ -919,7 +921,33 @@ int klp_module_coming(struct module *mod)
pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n",
patch->mod->name, obj->mod->name, obj->mod->name);
mod->klp_alive = false;
- klp_free_object_loaded(obj);
+
+ /*
+ * Run back through the patch list and unpatch any klp_object that
+ * was patched before hitting an error above.
+ */
+
+ list_for_each_entry(patch, &klp_patches, list) {
+
+ if (!patch->enabled || patch == klp_transition_patch)
+ continue;
+
+ klp_for_each_object(patch, obj) {
+
+ if (!obj->patched || !klp_is_module(obj) ||
+ strcmp(obj->name, mod->name))
+ continue;
+
+ klp_pre_unpatch_callback(obj);
+pr_err("JL: klp_unpatch_object(%p) patch=%p obj->name: %s\n", obj, patch, obj->name);
+ klp_unpatch_object(obj);
+ klp_post_unpatch_callback(obj);
+ klp_free_object_loaded(obj);
+
+ break;
+ }
+ }
+
mutex_unlock(&klp_mutex);

return ret;
--
2.7.5