diff options
author | Joe Lawrence <joe.lawrence@redhat.com> | 2017-10-13 15:08:41 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2017-10-19 04:08:56 -0400 |
commit | 93862e385ded7c60351e09fcd2a541d273650905 (patch) | |
tree | 22dc512303e988d00e327fa05c884e542fb5af55 /kernel/livepatch/core.c | |
parent | 19205da6a0da701787d42ad754edd1ffb514c956 (diff) |
livepatch: add (un)patch callbacks
Provide livepatch modules a klp_object (un)patching notification
mechanism. Pre and post-(un)patch callbacks allow livepatch modules to
setup or synchronize changes that would be difficult to support in only
patched-or-unpatched code contexts.
Callbacks can be registered for target module or vmlinux klp_objects,
but each implementation is klp_object specific.
- Pre-(un)patch callbacks run before any (un)patching transition
starts.
- Post-(un)patch callbacks run once an object has been (un)patched and
the klp_patch fully transitioned to its target state.
Example use cases include modification of global data and registration
of newly available services/handlers.
See Documentation/livepatch/callbacks.txt for details and
samples/livepatch/ for examples.
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Acked-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'kernel/livepatch/core.c')
-rw-r--r-- | kernel/livepatch/core.c | 51 |
1 files changed, 41 insertions, 10 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index b9628e43c78f..cafb5a84417d 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c | |||
@@ -54,11 +54,6 @@ static bool klp_is_module(struct klp_object *obj) | |||
54 | return obj->name; | 54 | return obj->name; |
55 | } | 55 | } |
56 | 56 | ||
57 | static bool klp_is_object_loaded(struct klp_object *obj) | ||
58 | { | ||
59 | return !obj->name || obj->mod; | ||
60 | } | ||
61 | |||
62 | /* sets obj->mod if object is not vmlinux and module is found */ | 57 | /* sets obj->mod if object is not vmlinux and module is found */ |
63 | static void klp_find_object_module(struct klp_object *obj) | 58 | static void klp_find_object_module(struct klp_object *obj) |
64 | { | 59 | { |
@@ -285,6 +280,8 @@ static int klp_write_object_relocations(struct module *pmod, | |||
285 | 280 | ||
286 | static int __klp_disable_patch(struct klp_patch *patch) | 281 | static int __klp_disable_patch(struct klp_patch *patch) |
287 | { | 282 | { |
283 | struct klp_object *obj; | ||
284 | |||
288 | if (klp_transition_patch) | 285 | if (klp_transition_patch) |
289 | return -EBUSY; | 286 | return -EBUSY; |
290 | 287 | ||
@@ -295,6 +292,10 @@ static int __klp_disable_patch(struct klp_patch *patch) | |||
295 | 292 | ||
296 | klp_init_transition(patch, KLP_UNPATCHED); | 293 | klp_init_transition(patch, KLP_UNPATCHED); |
297 | 294 | ||
295 | klp_for_each_object(patch, obj) | ||
296 | if (patch->enabled && obj->patched) | ||
297 | klp_pre_unpatch_callback(obj); | ||
298 | |||
298 | /* | 299 | /* |
299 | * Enforce the order of the func->transition writes in | 300 | * Enforce the order of the func->transition writes in |
300 | * klp_init_transition() and the TIF_PATCH_PENDING writes in | 301 | * klp_init_transition() and the TIF_PATCH_PENDING writes in |
@@ -388,13 +389,18 @@ static int __klp_enable_patch(struct klp_patch *patch) | |||
388 | if (!klp_is_object_loaded(obj)) | 389 | if (!klp_is_object_loaded(obj)) |
389 | continue; | 390 | continue; |
390 | 391 | ||
391 | ret = klp_patch_object(obj); | 392 | ret = klp_pre_patch_callback(obj); |
392 | if (ret) { | 393 | if (ret) { |
393 | pr_warn("failed to enable patch '%s'\n", | 394 | pr_warn("pre-patch callback failed for object '%s'\n", |
394 | patch->mod->name); | 395 | klp_is_module(obj) ? obj->name : "vmlinux"); |
396 | goto err; | ||
397 | } | ||
395 | 398 | ||
396 | klp_cancel_transition(); | 399 | ret = klp_patch_object(obj); |
397 | return ret; | 400 | if (ret) { |
401 | pr_warn("failed to patch object '%s'\n", | ||
402 | klp_is_module(obj) ? obj->name : "vmlinux"); | ||
403 | goto err; | ||
398 | } | 404 | } |
399 | } | 405 | } |
400 | 406 | ||
@@ -403,6 +409,11 @@ static int __klp_enable_patch(struct klp_patch *patch) | |||
403 | patch->enabled = true; | 409 | patch->enabled = true; |
404 | 410 | ||
405 | return 0; | 411 | return 0; |
412 | err: | ||
413 | pr_warn("failed to enable patch '%s'\n", patch->mod->name); | ||
414 | |||
415 | klp_cancel_transition(); | ||
416 | return ret; | ||
406 | } | 417 | } |
407 | 418 | ||
408 | /** | 419 | /** |
@@ -871,13 +882,27 @@ int klp_module_coming(struct module *mod) | |||
871 | pr_notice("applying patch '%s' to loading module '%s'\n", | 882 | pr_notice("applying patch '%s' to loading module '%s'\n", |
872 | patch->mod->name, obj->mod->name); | 883 | patch->mod->name, obj->mod->name); |
873 | 884 | ||
885 | ret = klp_pre_patch_callback(obj); | ||
886 | if (ret) { | ||
887 | pr_warn("pre-patch callback failed for object '%s'\n", | ||
888 | obj->name); | ||
889 | goto err; | ||
890 | } | ||
891 | |||
874 | ret = klp_patch_object(obj); | 892 | ret = klp_patch_object(obj); |
875 | if (ret) { | 893 | if (ret) { |
876 | pr_warn("failed to apply patch '%s' to module '%s' (%d)\n", | 894 | pr_warn("failed to apply patch '%s' to module '%s' (%d)\n", |
877 | patch->mod->name, obj->mod->name, ret); | 895 | patch->mod->name, obj->mod->name, ret); |
896 | |||
897 | if (patch != klp_transition_patch) | ||
898 | klp_post_unpatch_callback(obj); | ||
899 | |||
878 | goto err; | 900 | goto err; |
879 | } | 901 | } |
880 | 902 | ||
903 | if (patch != klp_transition_patch) | ||
904 | klp_post_patch_callback(obj); | ||
905 | |||
881 | break; | 906 | break; |
882 | } | 907 | } |
883 | } | 908 | } |
@@ -927,9 +952,15 @@ void klp_module_going(struct module *mod) | |||
927 | * is in transition. | 952 | * is in transition. |
928 | */ | 953 | */ |
929 | if (patch->enabled || patch == klp_transition_patch) { | 954 | if (patch->enabled || patch == klp_transition_patch) { |
955 | |||
956 | if (patch != klp_transition_patch) | ||
957 | klp_pre_unpatch_callback(obj); | ||
958 | |||
930 | pr_notice("reverting patch '%s' on unloading module '%s'\n", | 959 | pr_notice("reverting patch '%s' on unloading module '%s'\n", |
931 | patch->mod->name, obj->mod->name); | 960 | patch->mod->name, obj->mod->name); |
932 | klp_unpatch_object(obj); | 961 | klp_unpatch_object(obj); |
962 | |||
963 | klp_post_unpatch_callback(obj); | ||
933 | } | 964 | } |
934 | 965 | ||
935 | klp_free_object_loaded(obj); | 966 | klp_free_object_loaded(obj); |