aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/livepatch
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2017-11-15 04:53:24 -0500
committerJiri Kosina <jkosina@suse.cz>2017-11-15 04:54:27 -0500
commitfc41efc1843009ebcdb4850b21f1c371ad203f4e (patch)
tree71878da67ed83e19ba6d5e30ebee72e6d3612760 /kernel/livepatch
parentcb65dc7b89043a66d4459a6a811645d43185b5f0 (diff)
parent89a9a1c1c89cea5f70975c338c011b9f7024dee5 (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.c52
-rw-r--r--kernel/livepatch/core.h40
-rw-r--r--kernel/livepatch/patch.c1
-rw-r--r--kernel/livepatch/transition.c45
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
57static 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 */
63static void klp_find_object_module(struct klp_object *obj) 58static 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
286static int __klp_disable_patch(struct klp_patch *patch) 281static 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;
415err:
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
4extern struct mutex klp_mutex; 6extern struct mutex klp_mutex;
5 7
8static inline bool klp_is_object_loaded(struct klp_object *obj)
9{
10 return !obj->name || obj->mod;
11}
12
13static 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
25static 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
31static 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
37static 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
132done: 133done:
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
410success: 435success:
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;