Skip to content

Commit fc41efc

Browse files
author
Jiri Kosina
committed
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 <[email protected]>
2 parents cb65dc7 + 89a9a1c commit fc41efc

File tree

10 files changed

+1114
-17
lines changed

10 files changed

+1114
-17
lines changed

Documentation/livepatch/callbacks.txt

Lines changed: 605 additions & 0 deletions
Large diffs are not rendered by default.

include/linux/livepatch.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,35 @@ struct klp_func {
8787
bool transition;
8888
};
8989

90+
struct klp_object;
91+
92+
/**
93+
* struct klp_callbacks - pre/post live-(un)patch callback structure
94+
* @pre_patch: executed before code patching
95+
* @post_patch: executed after code patching
96+
* @pre_unpatch: executed before code unpatching
97+
* @post_unpatch: executed after code unpatching
98+
* @post_unpatch_enabled: flag indicating if post-unpatch callback
99+
* should run
100+
*
101+
* All callbacks are optional. Only the pre-patch callback, if provided,
102+
* will be unconditionally executed. If the parent klp_object fails to
103+
* patch for any reason, including a non-zero error status returned from
104+
* the pre-patch callback, no further callbacks will be executed.
105+
*/
106+
struct klp_callbacks {
107+
int (*pre_patch)(struct klp_object *obj);
108+
void (*post_patch)(struct klp_object *obj);
109+
void (*pre_unpatch)(struct klp_object *obj);
110+
void (*post_unpatch)(struct klp_object *obj);
111+
bool post_unpatch_enabled;
112+
};
113+
90114
/**
91115
* struct klp_object - kernel object structure for live patching
92116
* @name: module name (or NULL for vmlinux)
93117
* @funcs: function entries for functions to be patched in the object
118+
* @callbacks: functions to be executed pre/post (un)patching
94119
* @kobj: kobject for sysfs resources
95120
* @mod: kernel module associated with the patched object
96121
* (NULL for vmlinux)
@@ -100,6 +125,7 @@ struct klp_object {
100125
/* external */
101126
const char *name;
102127
struct klp_func *funcs;
128+
struct klp_callbacks callbacks;
103129

104130
/* internal */
105131
struct kobject kobj;

kernel/livepatch/core.c

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,6 @@ static bool klp_is_module(struct klp_object *obj)
5454
return obj->name;
5555
}
5656

57-
static bool klp_is_object_loaded(struct klp_object *obj)
58-
{
59-
return !obj->name || obj->mod;
60-
}
61-
6257
/* sets obj->mod if object is not vmlinux and module is found */
6358
static void klp_find_object_module(struct klp_object *obj)
6459
{
@@ -285,6 +280,11 @@ static int klp_write_object_relocations(struct module *pmod,
285280

286281
static int __klp_disable_patch(struct klp_patch *patch)
287282
{
283+
struct klp_object *obj;
284+
285+
if (WARN_ON(!patch->enabled))
286+
return -EINVAL;
287+
288288
if (klp_transition_patch)
289289
return -EBUSY;
290290

@@ -295,6 +295,10 @@ static int __klp_disable_patch(struct klp_patch *patch)
295295

296296
klp_init_transition(patch, KLP_UNPATCHED);
297297

298+
klp_for_each_object(patch, obj)
299+
if (obj->patched)
300+
klp_pre_unpatch_callback(obj);
301+
298302
/*
299303
* Enforce the order of the func->transition writes in
300304
* klp_init_transition() and the TIF_PATCH_PENDING writes in
@@ -388,13 +392,18 @@ static int __klp_enable_patch(struct klp_patch *patch)
388392
if (!klp_is_object_loaded(obj))
389393
continue;
390394

391-
ret = klp_patch_object(obj);
395+
ret = klp_pre_patch_callback(obj);
392396
if (ret) {
393-
pr_warn("failed to enable patch '%s'\n",
394-
patch->mod->name);
397+
pr_warn("pre-patch callback failed for object '%s'\n",
398+
klp_is_module(obj) ? obj->name : "vmlinux");
399+
goto err;
400+
}
395401

396-
klp_cancel_transition();
397-
return ret;
402+
ret = klp_patch_object(obj);
403+
if (ret) {
404+
pr_warn("failed to patch object '%s'\n",
405+
klp_is_module(obj) ? obj->name : "vmlinux");
406+
goto err;
398407
}
399408
}
400409

@@ -403,6 +412,11 @@ static int __klp_enable_patch(struct klp_patch *patch)
403412
patch->enabled = true;
404413

405414
return 0;
415+
err:
416+
pr_warn("failed to enable patch '%s'\n", patch->mod->name);
417+
418+
klp_cancel_transition();
419+
return ret;
406420
}
407421

408422
/**
@@ -854,9 +868,15 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
854868
* is in transition.
855869
*/
856870
if (patch->enabled || patch == klp_transition_patch) {
871+
872+
if (patch != klp_transition_patch)
873+
klp_pre_unpatch_callback(obj);
874+
857875
pr_notice("reverting patch '%s' on unloading module '%s'\n",
858876
patch->mod->name, obj->mod->name);
859877
klp_unpatch_object(obj);
878+
879+
klp_post_unpatch_callback(obj);
860880
}
861881

862882
klp_free_object_loaded(obj);
@@ -906,13 +926,25 @@ int klp_module_coming(struct module *mod)
906926
pr_notice("applying patch '%s' to loading module '%s'\n",
907927
patch->mod->name, obj->mod->name);
908928

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+
909936
ret = klp_patch_object(obj);
910937
if (ret) {
911938
pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
912939
patch->mod->name, obj->mod->name, ret);
940+
941+
klp_post_unpatch_callback(obj);
913942
goto err;
914943
}
915944

945+
if (patch != klp_transition_patch)
946+
klp_post_patch_callback(obj);
947+
916948
break;
917949
}
918950
}

kernel/livepatch/core.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,46 @@
11
#ifndef _LIVEPATCH_CORE_H
22
#define _LIVEPATCH_CORE_H
33

4+
#include <linux/livepatch.h>
5+
46
extern struct mutex klp_mutex;
57

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+
646
#endif /* _LIVEPATCH_CORE_H */

kernel/livepatch/patch.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <linux/slab.h>
2929
#include <linux/bug.h>
3030
#include <linux/printk.h>
31+
#include "core.h"
3132
#include "patch.h"
3233
#include "transition.h"
3334

kernel/livepatch/transition.c

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ static void klp_complete_transition(void)
8282
unsigned int cpu;
8383
bool immediate_func = false;
8484

85+
pr_debug("'%s': completing %s transition\n",
86+
klp_transition_patch->mod->name,
87+
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
88+
8589
if (klp_target_state == KLP_UNPATCHED) {
8690
/*
8791
* All tasks have transitioned to KLP_UNPATCHED so we can now
@@ -109,9 +113,6 @@ static void klp_complete_transition(void)
109113
}
110114
}
111115

112-
if (klp_target_state == KLP_UNPATCHED && !immediate_func)
113-
module_put(klp_transition_patch->mod);
114-
115116
/* Prevent klp_ftrace_handler() from seeing KLP_UNDEFINED state */
116117
if (klp_target_state == KLP_PATCHED)
117118
klp_synchronize_transition();
@@ -130,6 +131,27 @@ static void klp_complete_transition(void)
130131
}
131132

132133
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+
133155
klp_target_state = KLP_UNDEFINED;
134156
klp_transition_patch = NULL;
135157
}
@@ -145,6 +167,9 @@ void klp_cancel_transition(void)
145167
if (WARN_ON_ONCE(klp_target_state != KLP_PATCHED))
146168
return;
147169

170+
pr_debug("'%s': canceling patching transition, going to unpatch\n",
171+
klp_transition_patch->mod->name);
172+
148173
klp_target_state = KLP_UNPATCHED;
149174
klp_complete_transition();
150175
}
@@ -408,9 +433,6 @@ void klp_try_complete_transition(void)
408433
}
409434

410435
success:
411-
pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
412-
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
413-
414436
/* we're done, now cleanup the data structures */
415437
klp_complete_transition();
416438
}
@@ -426,7 +448,8 @@ void klp_start_transition(void)
426448

427449
WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED);
428450

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,
430453
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
431454

432455
/*
@@ -482,6 +505,9 @@ void klp_init_transition(struct klp_patch *patch, int state)
482505
*/
483506
klp_target_state = state;
484507

508+
pr_debug("'%s': initializing %s transition\n", patch->mod->name,
509+
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
510+
485511
/*
486512
* If the patch can be applied or reverted immediately, skip the
487513
* per-task transitions.
@@ -547,6 +573,11 @@ void klp_reverse_transition(void)
547573
unsigned int cpu;
548574
struct task_struct *g, *task;
549575

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+
550581
klp_transition_patch->enabled = !klp_transition_patch->enabled;
551582

552583
klp_target_state = !klp_target_state;

samples/livepatch/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o
22
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o
33
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o
44
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o
5+
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo.o
6+
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-mod.o
7+
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-busymod.o
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (C) 2017 Joe Lawrence <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation; either version 2
7+
* of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
/*
19+
* livepatch-callbacks-busymod.c - (un)patching callbacks demo support module
20+
*
21+
*
22+
* Purpose
23+
* -------
24+
*
25+
* Simple module to demonstrate livepatch (un)patching callbacks.
26+
*
27+
*
28+
* Usage
29+
* -----
30+
*
31+
* This module is not intended to be standalone. See the "Usage"
32+
* section of livepatch-callbacks-mod.c.
33+
*/
34+
35+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
36+
37+
#include <linux/module.h>
38+
#include <linux/kernel.h>
39+
#include <linux/workqueue.h>
40+
#include <linux/delay.h>
41+
42+
static int sleep_secs;
43+
module_param(sleep_secs, int, 0644);
44+
MODULE_PARM_DESC(sleep_secs, "sleep_secs (default=0)");
45+
46+
static void busymod_work_func(struct work_struct *work);
47+
static DECLARE_DELAYED_WORK(work, busymod_work_func);
48+
49+
static void busymod_work_func(struct work_struct *work)
50+
{
51+
pr_info("%s, sleeping %d seconds ...\n", __func__, sleep_secs);
52+
msleep(sleep_secs * 1000);
53+
pr_info("%s exit\n", __func__);
54+
}
55+
56+
static int livepatch_callbacks_mod_init(void)
57+
{
58+
pr_info("%s\n", __func__);
59+
schedule_delayed_work(&work,
60+
msecs_to_jiffies(1000 * 0));
61+
return 0;
62+
}
63+
64+
static void livepatch_callbacks_mod_exit(void)
65+
{
66+
cancel_delayed_work_sync(&work);
67+
pr_info("%s\n", __func__);
68+
}
69+
70+
module_init(livepatch_callbacks_mod_init);
71+
module_exit(livepatch_callbacks_mod_exit);
72+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)