diff options
author | Jiri Kosina <jkosina@suse.cz> | 2017-11-15 04:53:24 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2017-11-15 04:54:27 -0500 |
commit | fc41efc1843009ebcdb4850b21f1c371ad203f4e (patch) | |
tree | 71878da67ed83e19ba6d5e30ebee72e6d3612760 /kernel/livepatch | |
parent | cb65dc7b89043a66d4459a6a811645d43185b5f0 (diff) | |
parent | 89a9a1c1c89cea5f70975c338c011b9f7024dee5 (diff) |
Merge branch 'for-4.15/callbacks' into for-linus
This pulls in an infrastructure/API that allows livepatch writers to
register pre-patch and post-patch callbacks that allow for running a
glue code necessary for finalizing the patching if necessary.
Conflicts:
kernel/livepatch/core.c
- trivial conflict by adding a callback call into
module going notifier vs. moving that code block
to klp_cleanup_module_patches_limited()
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'kernel/livepatch')
-rw-r--r-- | kernel/livepatch/core.c | 52 | ||||
-rw-r--r-- | kernel/livepatch/core.h | 40 | ||||
-rw-r--r-- | kernel/livepatch/patch.c | 1 | ||||
-rw-r--r-- | kernel/livepatch/transition.c | 45 |
4 files changed, 121 insertions, 17 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index bf8c8fd72589..de9e45dca70f 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,11 @@ 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 | |||
285 | if (WARN_ON(!patch->enabled)) | ||
286 | return -EINVAL; | ||
287 | |||
288 | if (klp_transition_patch) | 288 | if (klp_transition_patch) |
289 | return -EBUSY; | 289 | return -EBUSY; |
290 | 290 | ||
@@ -295,6 +295,10 @@ static int __klp_disable_patch(struct klp_patch *patch) | |||
295 | 295 | ||
296 | klp_init_transition(patch, KLP_UNPATCHED); | 296 | klp_init_transition(patch, KLP_UNPATCHED); |
297 | 297 | ||
298 | klp_for_each_object(patch, obj) | ||
299 | if (obj->patched) | ||
300 | klp_pre_unpatch_callback(obj); | ||
301 | |||
298 | /* | 302 | /* |
299 | * Enforce the order of the func->transition writes in | 303 | * Enforce the order of the func->transition writes in |
300 | * klp_init_transition() and the TIF_PATCH_PENDING writes in | 304 | * klp_init_transition() and the TIF_PATCH_PENDING writes in |
@@ -388,13 +392,18 @@ static int __klp_enable_patch(struct klp_patch *patch) | |||
388 | if (!klp_is_object_loaded(obj)) | 392 | if (!klp_is_object_loaded(obj)) |
389 | continue; | 393 | continue; |
390 | 394 | ||
391 | ret = klp_patch_object(obj); | 395 | ret = klp_pre_patch_callback(obj); |
392 | if (ret) { | 396 | if (ret) { |
393 | pr_warn("failed to enable patch '%s'\n", | 397 | pr_warn("pre-patch callback failed for object '%s'\n", |
394 | patch->mod->name); | 398 | klp_is_module(obj) ? obj->name : "vmlinux"); |
399 | goto err; | ||
400 | } | ||
395 | 401 | ||
396 | klp_cancel_transition(); | 402 | ret = klp_patch_object(obj); |
397 | return ret; | 403 | if (ret) { |
404 | pr_warn("failed to patch object '%s'\n", | ||
405 | klp_is_module(obj) ? obj->name : "vmlinux"); | ||
406 | goto err; | ||
398 | } | 407 | } |
399 | } | 408 | } |
400 | 409 | ||
@@ -403,6 +412,11 @@ static int __klp_enable_patch(struct klp_patch *patch) | |||
403 | patch->enabled = true; | 412 | patch->enabled = true; |
404 | 413 | ||
405 | return 0; | 414 | return 0; |
415 | err: | ||
416 | pr_warn("failed to enable patch '%s'\n", patch->mod->name); | ||
417 | |||
418 | klp_cancel_transition(); | ||
419 | return ret; | ||
406 | } | 420 | } |
407 | 421 | ||
408 | /** | 422 | /** |
@@ -854,9 +868,15 @@ static void klp_cleanup_module_patches_limited(struct module *mod, | |||
854 | * is in transition. | 868 | * is in transition. |
855 | */ | 869 | */ |
856 | if (patch->enabled || patch == klp_transition_patch) { | 870 | if (patch->enabled || patch == klp_transition_patch) { |
871 | |||
872 | if (patch != klp_transition_patch) | ||
873 | klp_pre_unpatch_callback(obj); | ||
874 | |||
857 | pr_notice("reverting patch '%s' on unloading module '%s'\n", | 875 | pr_notice("reverting patch '%s' on unloading module '%s'\n", |
858 | patch->mod->name, obj->mod->name); | 876 | patch->mod->name, obj->mod->name); |
859 | klp_unpatch_object(obj); | 877 | klp_unpatch_object(obj); |
878 | |||
879 | klp_post_unpatch_callback(obj); | ||
860 | } | 880 | } |
861 | 881 | ||
862 | klp_free_object_loaded(obj); | 882 | klp_free_object_loaded(obj); |
@@ -906,13 +926,25 @@ int klp_module_coming(struct module *mod) | |||
906 | pr_notice("applying patch '%s' to loading module '%s'\n", | 926 | pr_notice("applying patch '%s' to loading module '%s'\n", |
907 | patch->mod->name, obj->mod->name); | 927 | patch->mod->name, obj->mod->name); |
908 | 928 | ||
929 | ret = klp_pre_patch_callback(obj); | ||
930 | if (ret) { | ||
931 | pr_warn("pre-patch callback failed for object '%s'\n", | ||
932 | obj->name); | ||
933 | goto err; | ||
934 | } | ||
935 | |||
909 | ret = klp_patch_object(obj); | 936 | ret = klp_patch_object(obj); |
910 | if (ret) { | 937 | if (ret) { |
911 | pr_warn("failed to apply patch '%s' to module '%s' (%d)\n", | 938 | pr_warn("failed to apply patch '%s' to module '%s' (%d)\n", |
912 | patch->mod->name, obj->mod->name, ret); | 939 | patch->mod->name, obj->mod->name, ret); |
940 | |||
941 | klp_post_unpatch_callback(obj); | ||
913 | goto err; | 942 | goto err; |
914 | } | 943 | } |
915 | 944 | ||
945 | if (patch != klp_transition_patch) | ||
946 | klp_post_patch_callback(obj); | ||
947 | |||
916 | break; | 948 | break; |
917 | } | 949 | } |
918 | } | 950 | } |
diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h index c74f24c47837..cc3aa708e0b4 100644 --- a/kernel/livepatch/core.h +++ b/kernel/livepatch/core.h | |||
@@ -1,6 +1,46 @@ | |||
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 = 0; | ||
16 | |||
17 | if (obj->callbacks.pre_patch) | ||
18 | ret = (*obj->callbacks.pre_patch)(obj); | ||
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 | obj->callbacks.post_unpatch_enabled = false; | ||
44 | } | ||
45 | |||
6 | #endif /* _LIVEPATCH_CORE_H */ | 46 | #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..56add6327736 100644 --- a/kernel/livepatch/transition.c +++ b/kernel/livepatch/transition.c | |||
@@ -82,6 +82,10 @@ static void klp_complete_transition(void) | |||
82 | unsigned int cpu; | 82 | unsigned int cpu; |
83 | bool immediate_func = false; | 83 | bool immediate_func = false; |
84 | 84 | ||
85 | pr_debug("'%s': completing %s transition\n", | ||
86 | klp_transition_patch->mod->name, | ||
87 | klp_target_state == KLP_PATCHED ? "patching" : "unpatching"); | ||
88 | |||
85 | if (klp_target_state == KLP_UNPATCHED) { | 89 | if (klp_target_state == KLP_UNPATCHED) { |
86 | /* | 90 | /* |
87 | * All tasks have transitioned to KLP_UNPATCHED so we can now | 91 | * All tasks have transitioned to KLP_UNPATCHED so we can now |
@@ -109,9 +113,6 @@ static void klp_complete_transition(void) | |||
109 | } | 113 | } |
110 | } | 114 | } |
111 | 115 | ||
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 */ | 116 | /* Prevent klp_ftrace_handler() from seeing KLP_UNDEFINED state */ |
116 | if (klp_target_state == KLP_PATCHED) | 117 | if (klp_target_state == KLP_PATCHED) |
117 | klp_synchronize_transition(); | 118 | klp_synchronize_transition(); |
@@ -130,6 +131,27 @@ static void klp_complete_transition(void) | |||
130 | } | 131 | } |
131 | 132 | ||
132 | done: | 133 | done: |
134 | klp_for_each_object(klp_transition_patch, obj) { | ||
135 | if (!klp_is_object_loaded(obj)) | ||
136 | continue; | ||
137 | if (klp_target_state == KLP_PATCHED) | ||
138 | klp_post_patch_callback(obj); | ||
139 | else if (klp_target_state == KLP_UNPATCHED) | ||
140 | klp_post_unpatch_callback(obj); | ||
141 | } | ||
142 | |||
143 | pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name, | ||
144 | klp_target_state == KLP_PATCHED ? "patching" : "unpatching"); | ||
145 | |||
146 | /* | ||
147 | * See complementary comment in __klp_enable_patch() for why we | ||
148 | * keep the module reference for immediate patches. | ||
149 | */ | ||
150 | if (!klp_transition_patch->immediate && !immediate_func && | ||
151 | klp_target_state == KLP_UNPATCHED) { | ||
152 | module_put(klp_transition_patch->mod); | ||
153 | } | ||
154 | |||
133 | klp_target_state = KLP_UNDEFINED; | 155 | klp_target_state = KLP_UNDEFINED; |
134 | klp_transition_patch = NULL; | 156 | klp_transition_patch = NULL; |
135 | } | 157 | } |
@@ -145,6 +167,9 @@ void klp_cancel_transition(void) | |||
145 | if (WARN_ON_ONCE(klp_target_state != KLP_PATCHED)) | 167 | if (WARN_ON_ONCE(klp_target_state != KLP_PATCHED)) |
146 | return; | 168 | return; |
147 | 169 | ||
170 | pr_debug("'%s': canceling patching transition, going to unpatch\n", | ||
171 | klp_transition_patch->mod->name); | ||
172 | |||
148 | klp_target_state = KLP_UNPATCHED; | 173 | klp_target_state = KLP_UNPATCHED; |
149 | klp_complete_transition(); | 174 | klp_complete_transition(); |
150 | } | 175 | } |
@@ -408,9 +433,6 @@ void klp_try_complete_transition(void) | |||
408 | } | 433 | } |
409 | 434 | ||
410 | success: | 435 | success: |
411 | pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name, | ||
412 | klp_target_state == KLP_PATCHED ? "patching" : "unpatching"); | ||
413 | |||
414 | /* we're done, now cleanup the data structures */ | 436 | /* we're done, now cleanup the data structures */ |
415 | klp_complete_transition(); | 437 | klp_complete_transition(); |
416 | } | 438 | } |
@@ -426,7 +448,8 @@ void klp_start_transition(void) | |||
426 | 448 | ||
427 | WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED); | 449 | WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED); |
428 | 450 | ||
429 | pr_notice("'%s': %s...\n", klp_transition_patch->mod->name, | 451 | pr_notice("'%s': starting %s transition\n", |
452 | klp_transition_patch->mod->name, | ||
430 | klp_target_state == KLP_PATCHED ? "patching" : "unpatching"); | 453 | klp_target_state == KLP_PATCHED ? "patching" : "unpatching"); |
431 | 454 | ||
432 | /* | 455 | /* |
@@ -482,6 +505,9 @@ void klp_init_transition(struct klp_patch *patch, int state) | |||
482 | */ | 505 | */ |
483 | klp_target_state = state; | 506 | klp_target_state = state; |
484 | 507 | ||
508 | pr_debug("'%s': initializing %s transition\n", patch->mod->name, | ||
509 | klp_target_state == KLP_PATCHED ? "patching" : "unpatching"); | ||
510 | |||
485 | /* | 511 | /* |
486 | * If the patch can be applied or reverted immediately, skip the | 512 | * If the patch can be applied or reverted immediately, skip the |
487 | * per-task transitions. | 513 | * per-task transitions. |
@@ -547,6 +573,11 @@ void klp_reverse_transition(void) | |||
547 | unsigned int cpu; | 573 | unsigned int cpu; |
548 | struct task_struct *g, *task; | 574 | struct task_struct *g, *task; |
549 | 575 | ||
576 | pr_debug("'%s': reversing transition from %s\n", | ||
577 | klp_transition_patch->mod->name, | ||
578 | klp_target_state == KLP_PATCHED ? "patching to unpatching" : | ||
579 | "unpatching to patching"); | ||
580 | |||
550 | klp_transition_patch->enabled = !klp_transition_patch->enabled; | 581 | klp_transition_patch->enabled = !klp_transition_patch->enabled; |
551 | 582 | ||
552 | klp_target_state = !klp_target_state; | 583 | klp_target_state = !klp_target_state; |