diff options
| author | Joe Lawrence <joe.lawrence@redhat.com> | 2017-10-13 11:08:41 -0800 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2017-10-19 00:08:56 -0800 |
| commit | 93862e385ded7c60351e09fcd2a541d273650905 (patch) | |
| tree | 22dc512303e988d00e327fa05c884e542fb5af55 /kernel/livepatch | |
| parent | 19205da6a0da701787d42ad754edd1ffb514c956 (diff) | |
| download | linux-stable-93862e385ded7c60351e09fcd2a541d273650905.tar.gz linux-stable-93862e385ded7c60351e09fcd2a541d273650905.tar.bz2 linux-stable-93862e385ded7c60351e09fcd2a541d273650905.tar.xz linux-stable-93862e385ded7c60351e09fcd2a541d273650905.zip | |
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')
| -rw-r--r-- | kernel/livepatch/core.c | 51 | ||||
| -rw-r--r-- | kernel/livepatch/core.h | 38 | ||||
| -rw-r--r-- | kernel/livepatch/patch.c | 1 | ||||
| -rw-r--r-- | kernel/livepatch/transition.c | 21 |
4 files changed, 98 insertions, 13 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); |
diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h index c74f24c47837..6fc907b54e71 100644 --- a/kernel/livepatch/core.h +++ b/kernel/livepatch/core.h | |||
| @@ -1,6 +1,44 @@ | |||
| 1 | #ifndef _LIVEPATCH_CORE_H | 1 | #ifndef _LIVEPATCH_CORE_H |
| 2 | #define _LIVEPATCH_CORE_H | 2 | #define _LIVEPATCH_CORE_H |
| 3 | 3 | ||
| 4 | #include <linux/livepatch.h> | ||
| 5 | |||
| 4 | extern struct mutex klp_mutex; | 6 | extern struct mutex klp_mutex; |
| 5 | 7 | ||
| 8 | static inline bool klp_is_object_loaded(struct klp_object *obj) | ||
| 9 | { | ||
| 10 | return !obj->name || obj->mod; | ||
| 11 | } | ||
| 12 | |||
| 13 | static inline int klp_pre_patch_callback(struct klp_object *obj) | ||
| 14 | { | ||
| 15 | int ret; | ||
| 16 | |||
| 17 | ret = (obj->callbacks.pre_patch) ? | ||
| 18 | (*obj->callbacks.pre_patch)(obj) : 0; | ||
| 19 | |||
| 20 | obj->callbacks.post_unpatch_enabled = !ret; | ||
| 21 | |||
| 22 | return ret; | ||
| 23 | } | ||
| 24 | |||
| 25 | static inline void klp_post_patch_callback(struct klp_object *obj) | ||
| 26 | { | ||
| 27 | if (obj->callbacks.post_patch) | ||
| 28 | (*obj->callbacks.post_patch)(obj); | ||
| 29 | } | ||
| 30 | |||
| 31 | static inline void klp_pre_unpatch_callback(struct klp_object *obj) | ||
| 32 | { | ||
| 33 | if (obj->callbacks.pre_unpatch) | ||
| 34 | (*obj->callbacks.pre_unpatch)(obj); | ||
| 35 | } | ||
| 36 | |||
| 37 | static inline void klp_post_unpatch_callback(struct klp_object *obj) | ||
| 38 | { | ||
| 39 | if (obj->callbacks.post_unpatch_enabled && | ||
| 40 | obj->callbacks.post_unpatch) | ||
| 41 | (*obj->callbacks.post_unpatch)(obj); | ||
| 42 | } | ||
| 43 | |||
| 6 | #endif /* _LIVEPATCH_CORE_H */ | 44 | #endif /* _LIVEPATCH_CORE_H */ |
diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c index 52c4e907c14b..82d584225dc6 100644 --- a/kernel/livepatch/patch.c +++ b/kernel/livepatch/patch.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
| 29 | #include <linux/bug.h> | 29 | #include <linux/bug.h> |
| 30 | #include <linux/printk.h> | 30 | #include <linux/printk.h> |
| 31 | #include "core.h" | ||
| 31 | #include "patch.h" | 32 | #include "patch.h" |
| 32 | #include "transition.h" | 33 | #include "transition.h" |
| 33 | 34 | ||
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c index b004a1fb6032..7bf55b7f3687 100644 --- a/kernel/livepatch/transition.c +++ b/kernel/livepatch/transition.c | |||
| @@ -109,9 +109,6 @@ static void klp_complete_transition(void) | |||
| 109 | } | 109 | } |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | if (klp_target_state == KLP_UNPATCHED && !immediate_func) | ||
| 113 | module_put(klp_transition_patch->mod); | ||
| 114 | |||
| 115 | /* Prevent klp_ftrace_handler() from seeing KLP_UNDEFINED state */ | 112 | /* Prevent klp_ftrace_handler() from seeing KLP_UNDEFINED state */ |
| 116 | if (klp_target_state == KLP_PATCHED) | 113 | if (klp_target_state == KLP_PATCHED) |
| 117 | klp_synchronize_transition(); | 114 | klp_synchronize_transition(); |
| @@ -130,6 +127,24 @@ static void klp_complete_transition(void) | |||
| 130 | } | 127 | } |
| 131 | 128 | ||
| 132 | done: | 129 | done: |
| 130 | klp_for_each_object(klp_transition_patch, obj) { | ||
| 131 | if (!klp_is_object_loaded(obj)) | ||
| 132 | continue; | ||
| 133 | if (klp_target_state == KLP_PATCHED) | ||
| 134 | klp_post_patch_callback(obj); | ||
| 135 | else if (klp_target_state == KLP_UNPATCHED) | ||
| 136 | klp_post_unpatch_callback(obj); | ||
| 137 | } | ||
| 138 | |||
| 139 | /* | ||
| 140 | * See complementary comment in __klp_enable_patch() for why we | ||
| 141 | * keep the module reference for immediate patches. | ||
| 142 | */ | ||
| 143 | if (!klp_transition_patch->immediate && !immediate_func && | ||
| 144 | klp_target_state == KLP_UNPATCHED) { | ||
| 145 | module_put(klp_transition_patch->mod); | ||
| 146 | } | ||
| 147 | |||
| 133 | klp_target_state = KLP_UNDEFINED; | 148 | klp_target_state = KLP_UNDEFINED; |
| 134 | klp_transition_patch = NULL; | 149 | klp_transition_patch = NULL; |
| 135 | } | 150 | } |
