[PATCH 1/2] livepatch: Implement separate coming and going module notifiers

From: Jessica Yu
Date: Fri Jan 29 2016 - 01:44:30 EST


Detangle klp_module_notify() into two separate module notifiers
klp_module_notify_{coming,going}() with the appropriate priorities,
so that on module load, the ftrace module notifier will run *before*
the livepatch coming module notifier but *after* the livepatch going
module modifier.

This fixes a notifier ordering issue in which the ftrace module notifier
(and hence ftrace_module_enable()) for COMING modules was being called
after klp_module_notify(), which caused livepatch modules to initialize
incorrectly.

Signed-off-by: Jessica Yu <jeyu@xxxxxxxxxx>
---
kernel/livepatch/core.c | 128 +++++++++++++++++++++++++++---------------------
1 file changed, 73 insertions(+), 55 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index bc2c85c..23f4234 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -866,60 +866,73 @@ int klp_register_patch(struct klp_patch *patch)
}
EXPORT_SYMBOL_GPL(klp_register_patch);

-static int klp_module_notify_coming(struct klp_patch *patch,
- struct klp_object *obj)
+static int klp_module_notify_coming(struct notifier_block *nb,
+ unsigned long action, void *data)
{
- struct module *pmod = patch->mod;
- struct module *mod = obj->mod;
int ret;
+ struct module *mod = data;
+ struct klp_patch *patch;
+ struct klp_object *obj;

- ret = klp_init_object_loaded(patch, obj);
- if (ret) {
- pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
- pmod->name, mod->name, ret);
- return ret;
- }
-
- if (patch->state == KLP_DISABLED)
+ if (action != MODULE_STATE_COMING)
return 0;

- pr_notice("applying patch '%s' to loading module '%s'\n",
- pmod->name, mod->name);
+ mutex_lock(&klp_mutex);

- ret = klp_enable_object(obj);
- if (ret)
- pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
- pmod->name, mod->name, ret);
- return ret;
-}
+ /*
+ * Each module has to know that the notifier has been called.
+ * We never know what module will get patched by a new patch.
+ */
+ mod->klp_alive = true;

-static void klp_module_notify_going(struct klp_patch *patch,
- struct klp_object *obj)
-{
- struct module *pmod = patch->mod;
- struct module *mod = obj->mod;
+ list_for_each_entry(patch, &klp_patches, list) {
+ klp_for_each_object(patch, obj) {
+ if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
+ continue;
+
+ obj->mod = mod;

- if (patch->state == KLP_DISABLED)
- goto disabled;
+ ret = klp_init_object_loaded(patch, obj);
+ if (ret) {
+ pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
+ patch->mod->name, obj->mod->name, ret);
+ goto err;
+ }

- pr_notice("reverting patch '%s' on unloading module '%s'\n",
- pmod->name, mod->name);
+ if (patch->state == KLP_DISABLED)
+ break;

- klp_disable_object(obj);
+ pr_notice("applying patch '%s' to loading module '%s'\n",
+ patch->mod->name, obj->mod->name);

-disabled:
- klp_free_object_loaded(obj);
+ ret = klp_enable_object(obj);
+ if (ret)
+ pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
+ patch->mod->name, obj->mod->name, ret);
+err:
+ if (ret) {
+ obj->mod = NULL;
+ pr_warn("patch '%s' is in an inconsistent state!\n",
+ patch->mod->name);
+ }
+
+ break;
+ }
+ }
+
+ mutex_unlock(&klp_mutex);
+
+ return 0;
}

-static int klp_module_notify(struct notifier_block *nb, unsigned long action,
- void *data)
+static int klp_module_notify_going(struct notifier_block *nb,
+ unsigned long action, void *data)
{
- int ret;
struct module *mod = data;
struct klp_patch *patch;
struct klp_object *obj;

- if (action != MODULE_STATE_COMING && action != MODULE_STATE_GOING)
+ if (action != MODULE_STATE_GOING)
return 0;

mutex_lock(&klp_mutex);
@@ -928,27 +941,22 @@ static int klp_module_notify(struct notifier_block *nb, unsigned long action,
* Each module has to know that the notifier has been called.
* We never know what module will get patched by a new patch.
*/
- if (action == MODULE_STATE_COMING)
- mod->klp_alive = true;
- else /* MODULE_STATE_GOING */
- mod->klp_alive = false;
+ mod->klp_alive = false;

list_for_each_entry(patch, &klp_patches, list) {
klp_for_each_object(patch, obj) {
if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
continue;

- if (action == MODULE_STATE_COMING) {
- obj->mod = mod;
- ret = klp_module_notify_coming(patch, obj);
- if (ret) {
- obj->mod = NULL;
- pr_warn("patch '%s' is in an inconsistent state!\n",
- patch->mod->name);
- }
- } else /* MODULE_STATE_GOING */
- klp_module_notify_going(patch, obj);
+ if (patch->state == KLP_DISABLED)
+ goto disabled;
+
+ pr_notice("reverting patch '%s' on unloading module '%s'\n",
+ patch->mod->name, obj->mod->name);

+ klp_disable_object(obj);
+disabled:
+ klp_free_object_loaded(obj);
break;
}
}
@@ -958,9 +966,14 @@ static int klp_module_notify(struct notifier_block *nb, unsigned long action,
return 0;
}

-static struct notifier_block klp_module_nb = {
- .notifier_call = klp_module_notify,
- .priority = INT_MIN+1, /* called late but before ftrace notifier */
+static struct notifier_block klp_module_nb_coming = {
+ .notifier_call = klp_module_notify_coming,
+ .priority = INT_MIN, /* called late but after ftrace (coming) notifier */
+};
+
+static struct notifier_block klp_module_nb_going = {
+ .notifier_call = klp_module_notify_going,
+ .priority = INT_MIN+2, /* called late but before ftrace (going) notifier */
};

static int __init klp_init(void)
@@ -973,7 +986,11 @@ static int __init klp_init(void)
return -EINVAL;
}

- ret = register_module_notifier(&klp_module_nb);
+ ret = register_module_notifier(&klp_module_nb_coming);
+ if (ret)
+ return ret;
+
+ ret = register_module_notifier(&klp_module_nb_going);
if (ret)
return ret;

@@ -986,7 +1003,8 @@ static int __init klp_init(void)
return 0;

unregister:
- unregister_module_notifier(&klp_module_nb);
+ unregister_module_notifier(&klp_module_nb_coming);
+ unregister_module_notifier(&klp_module_nb_going);
return ret;
}

--
2.4.3