Skip to content

Commit 93862e3

Browse files
joe-lawrenceJiri Kosina
authored andcommitted
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 <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Acked-by: Miroslav Benes <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 19205da commit 93862e3

File tree

10 files changed

+1091
-13
lines changed

10 files changed

+1091
-13
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: 41 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,8 @@ 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+
288285
if (klp_transition_patch)
289286
return -EBUSY;
290287

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

296293
klp_init_transition(patch, KLP_UNPATCHED);
297294

295+
klp_for_each_object(patch, obj)
296+
if (patch->enabled && obj->patched)
297+
klp_pre_unpatch_callback(obj);
298+
298299
/*
299300
* Enforce the order of the func->transition writes in
300301
* klp_init_transition() and the TIF_PATCH_PENDING writes in
@@ -388,13 +389,18 @@ static int __klp_enable_patch(struct klp_patch *patch)
388389
if (!klp_is_object_loaded(obj))
389390
continue;
390391

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

396-
klp_cancel_transition();
397-
return ret;
399+
ret = klp_patch_object(obj);
400+
if (ret) {
401+
pr_warn("failed to patch object '%s'\n",
402+
klp_is_module(obj) ? obj->name : "vmlinux");
403+
goto err;
398404
}
399405
}
400406

@@ -403,6 +409,11 @@ static int __klp_enable_patch(struct klp_patch *patch)
403409
patch->enabled = true;
404410

405411
return 0;
412+
err:
413+
pr_warn("failed to enable patch '%s'\n", patch->mod->name);
414+
415+
klp_cancel_transition();
416+
return ret;
406417
}
407418

408419
/**
@@ -871,13 +882,27 @@ int klp_module_coming(struct module *mod)
871882
pr_notice("applying patch '%s' to loading module '%s'\n",
872883
patch->mod->name, obj->mod->name);
873884

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+
874892
ret = klp_patch_object(obj);
875893
if (ret) {
876894
pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
877895
patch->mod->name, obj->mod->name, ret);
896+
897+
if (patch != klp_transition_patch)
898+
klp_post_unpatch_callback(obj);
899+
878900
goto err;
879901
}
880902

903+
if (patch != klp_transition_patch)
904+
klp_post_patch_callback(obj);
905+
881906
break;
882907
}
883908
}
@@ -927,9 +952,15 @@ void klp_module_going(struct module *mod)
927952
* is in transition.
928953
*/
929954
if (patch->enabled || patch == klp_transition_patch) {
955+
956+
if (patch != klp_transition_patch)
957+
klp_pre_unpatch_callback(obj);
958+
930959
pr_notice("reverting patch '%s' on unloading module '%s'\n",
931960
patch->mod->name, obj->mod->name);
932961
klp_unpatch_object(obj);
962+
963+
klp_post_unpatch_callback(obj);
933964
}
934965

935966
klp_free_object_loaded(obj);

kernel/livepatch/core.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,44 @@
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;
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+
644
#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: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,6 @@ static void klp_complete_transition(void)
109109
}
110110
}
111111

112-
if (klp_target_state == KLP_UNPATCHED && !immediate_func)
113-
module_put(klp_transition_patch->mod);
114-
115112
/* Prevent klp_ftrace_handler() from seeing KLP_UNDEFINED state */
116113
if (klp_target_state == KLP_PATCHED)
117114
klp_synchronize_transition();
@@ -130,6 +127,24 @@ static void klp_complete_transition(void)
130127
}
131128

132129
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+
133148
klp_target_state = KLP_UNDEFINED;
134149
klp_transition_patch = NULL;
135150
}

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)