diff options
-rw-r--r-- | Documentation/livepatch/callbacks.txt | 605 | ||||
-rw-r--r-- | include/linux/livepatch.h | 26 | ||||
-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 | ||||
-rw-r--r-- | samples/livepatch/Makefile | 3 | ||||
-rw-r--r-- | samples/livepatch/livepatch-callbacks-busymod.c | 72 | ||||
-rw-r--r-- | samples/livepatch/livepatch-callbacks-demo.c | 234 | ||||
-rw-r--r-- | samples/livepatch/livepatch-callbacks-mod.c | 53 |
10 files changed, 1114 insertions, 17 deletions
diff --git a/Documentation/livepatch/callbacks.txt b/Documentation/livepatch/callbacks.txt new file mode 100644 index 000000000000..c9776f48e458 --- /dev/null +++ b/Documentation/livepatch/callbacks.txt | |||
@@ -0,0 +1,605 @@ | |||
1 | ====================== | ||
2 | (Un)patching Callbacks | ||
3 | ====================== | ||
4 | |||
5 | Livepatch (un)patch-callbacks provide a mechanism for livepatch modules | ||
6 | to execute callback functions when a kernel object is (un)patched. They | ||
7 | can be considered a "power feature" that extends livepatching abilities | ||
8 | to include: | ||
9 | |||
10 | - Safe updates to global data | ||
11 | |||
12 | - "Patches" to init and probe functions | ||
13 | |||
14 | - Patching otherwise unpatchable code (i.e. assembly) | ||
15 | |||
16 | In most cases, (un)patch callbacks will need to be used in conjunction | ||
17 | with memory barriers and kernel synchronization primitives, like | ||
18 | mutexes/spinlocks, or even stop_machine(), to avoid concurrency issues. | ||
19 | |||
20 | Callbacks differ from existing kernel facilities: | ||
21 | |||
22 | - Module init/exit code doesn't run when disabling and re-enabling a | ||
23 | patch. | ||
24 | |||
25 | - A module notifier can't stop a to-be-patched module from loading. | ||
26 | |||
27 | Callbacks are part of the klp_object structure and their implementation | ||
28 | is specific to that klp_object. Other livepatch objects may or may not | ||
29 | be patched, irrespective of the target klp_object's current state. | ||
30 | |||
31 | Callbacks can be registered for the following livepatch actions: | ||
32 | |||
33 | * Pre-patch - before a klp_object is patched | ||
34 | |||
35 | * Post-patch - after a klp_object has been patched and is active | ||
36 | across all tasks | ||
37 | |||
38 | * Pre-unpatch - before a klp_object is unpatched (ie, patched code is | ||
39 | active), used to clean up post-patch callback | ||
40 | resources | ||
41 | |||
42 | * Post-unpatch - after a klp_object has been patched, all code has | ||
43 | been restored and no tasks are running patched code, | ||
44 | used to cleanup pre-patch callback resources | ||
45 | |||
46 | Each callback is optional, omitting one does not preclude specifying any | ||
47 | other. However, the livepatching core executes the handlers in | ||
48 | symmetry: pre-patch callbacks have a post-unpatch counterpart and | ||
49 | post-patch callbacks have a pre-unpatch counterpart. An unpatch | ||
50 | callback will only be executed if its corresponding patch callback was | ||
51 | executed. Typical use cases pair a patch handler that acquires and | ||
52 | configures resources with an unpatch handler tears down and releases | ||
53 | those same resources. | ||
54 | |||
55 | A callback is only executed if its host klp_object is loaded. For | ||
56 | in-kernel vmlinux targets, this means that callbacks will always execute | ||
57 | when a livepatch is enabled/disabled. For patch target kernel modules, | ||
58 | callbacks will only execute if the target module is loaded. When a | ||
59 | module target is (un)loaded, its callbacks will execute only if the | ||
60 | livepatch module is enabled. | ||
61 | |||
62 | The pre-patch callback, if specified, is expected to return a status | ||
63 | code (0 for success, -ERRNO on error). An error status code indicates | ||
64 | to the livepatching core that patching of the current klp_object is not | ||
65 | safe and to stop the current patching request. (When no pre-patch | ||
66 | callback is provided, the transition is assumed to be safe.) If a | ||
67 | pre-patch callback returns failure, the kernel's module loader will: | ||
68 | |||
69 | - Refuse to load a livepatch, if the livepatch is loaded after | ||
70 | targeted code. | ||
71 | |||
72 | or: | ||
73 | |||
74 | - Refuse to load a module, if the livepatch was already successfully | ||
75 | loaded. | ||
76 | |||
77 | No post-patch, pre-unpatch, or post-unpatch callbacks will be executed | ||
78 | for a given klp_object if the object failed to patch, due to a failed | ||
79 | pre_patch callback or for any other reason. | ||
80 | |||
81 | If a patch transition is reversed, no pre-unpatch handlers will be run | ||
82 | (this follows the previously mentioned symmetry -- pre-unpatch callbacks | ||
83 | will only occur if their corresponding post-patch callback executed). | ||
84 | |||
85 | If the object did successfully patch, but the patch transition never | ||
86 | started for some reason (e.g., if another object failed to patch), | ||
87 | only the post-unpatch callback will be called. | ||
88 | |||
89 | |||
90 | Example Use-cases | ||
91 | ================= | ||
92 | |||
93 | Update global data | ||
94 | ------------------ | ||
95 | |||
96 | A pre-patch callback can be useful to update a global variable. For | ||
97 | example, 75ff39ccc1bd ("tcp: make challenge acks less predictable") | ||
98 | changes a global sysctl, as well as patches the tcp_send_challenge_ack() | ||
99 | function. | ||
100 | |||
101 | In this case, if we're being super paranoid, it might make sense to | ||
102 | patch the data *after* patching is complete with a post-patch callback, | ||
103 | so that tcp_send_challenge_ack() could first be changed to read | ||
104 | sysctl_tcp_challenge_ack_limit with READ_ONCE. | ||
105 | |||
106 | |||
107 | Support __init and probe function patches | ||
108 | ----------------------------------------- | ||
109 | |||
110 | Although __init and probe functions are not directly livepatch-able, it | ||
111 | may be possible to implement similar updates via pre/post-patch | ||
112 | callbacks. | ||
113 | |||
114 | 48900cb6af42 ("virtio-net: drop NETIF_F_FRAGLIST") change the way that | ||
115 | virtnet_probe() initialized its driver's net_device features. A | ||
116 | pre/post-patch callback could iterate over all such devices, making a | ||
117 | similar change to their hw_features value. (Client functions of the | ||
118 | value may need to be updated accordingly.) | ||
119 | |||
120 | |||
121 | Test cases | ||
122 | ========== | ||
123 | |||
124 | What follows is not an exhaustive test suite of every possible livepatch | ||
125 | pre/post-(un)patch combination, but a selection that demonstrates a few | ||
126 | important concepts. Each test case uses the kernel modules located in | ||
127 | the samples/livepatch/ and assumes that no livepatches are loaded at the | ||
128 | beginning of the test. | ||
129 | |||
130 | |||
131 | Test 1 | ||
132 | ------ | ||
133 | |||
134 | Test a combination of loading a kernel module and a livepatch that | ||
135 | patches a function in the first module. (Un)load the target module | ||
136 | before the livepatch module: | ||
137 | |||
138 | - load target module | ||
139 | - load livepatch | ||
140 | - disable livepatch | ||
141 | - unload target module | ||
142 | - unload livepatch | ||
143 | |||
144 | First load a target module: | ||
145 | |||
146 | % insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
147 | [ 34.475708] livepatch_callbacks_mod: livepatch_callbacks_mod_init | ||
148 | |||
149 | On livepatch enable, before the livepatch transition starts, pre-patch | ||
150 | callbacks are executed for vmlinux and livepatch_callbacks_mod (those | ||
151 | klp_objects currently loaded). After klp_objects are patched according | ||
152 | to the klp_patch, their post-patch callbacks run and the transition | ||
153 | completes: | ||
154 | |||
155 | % insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
156 | [ 36.503719] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
157 | [ 36.504213] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
158 | [ 36.504238] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
159 | [ 36.504721] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state | ||
160 | [ 36.505849] livepatch: 'livepatch_callbacks_demo': starting patching transition | ||
161 | [ 37.727133] livepatch: 'livepatch_callbacks_demo': completing patching transition | ||
162 | [ 37.727232] livepatch_callbacks_demo: post_patch_callback: vmlinux | ||
163 | [ 37.727860] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state | ||
164 | [ 37.728792] livepatch: 'livepatch_callbacks_demo': patching complete | ||
165 | |||
166 | Similarly, on livepatch disable, pre-patch callbacks run before the | ||
167 | unpatching transition starts. klp_objects are reverted, post-patch | ||
168 | callbacks execute and the transition completes: | ||
169 | |||
170 | % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
171 | [ 38.510209] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition | ||
172 | [ 38.510234] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux | ||
173 | [ 38.510982] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state | ||
174 | [ 38.512209] livepatch: 'livepatch_callbacks_demo': starting unpatching transition | ||
175 | [ 39.711132] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
176 | [ 39.711210] livepatch_callbacks_demo: post_unpatch_callback: vmlinux | ||
177 | [ 39.711779] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state | ||
178 | [ 39.712735] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
179 | |||
180 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
181 | % rmmod samples/livepatch/livepatch-callbacks-mod.ko | ||
182 | [ 42.534183] livepatch_callbacks_mod: livepatch_callbacks_mod_exit | ||
183 | |||
184 | |||
185 | Test 2 | ||
186 | ------ | ||
187 | |||
188 | This test is similar to the previous test, but (un)load the livepatch | ||
189 | module before the target kernel module. This tests the livepatch core's | ||
190 | module_coming handler: | ||
191 | |||
192 | - load livepatch | ||
193 | - load target module | ||
194 | - disable livepatch | ||
195 | - unload livepatch | ||
196 | - unload target module | ||
197 | |||
198 | |||
199 | On livepatch enable, only pre/post-patch callbacks are executed for | ||
200 | currently loaded klp_objects, in this case, vmlinux: | ||
201 | |||
202 | % insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
203 | [ 44.553328] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
204 | [ 44.553997] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
205 | [ 44.554049] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
206 | [ 44.554845] livepatch: 'livepatch_callbacks_demo': starting patching transition | ||
207 | [ 45.727128] livepatch: 'livepatch_callbacks_demo': completing patching transition | ||
208 | [ 45.727212] livepatch_callbacks_demo: post_patch_callback: vmlinux | ||
209 | [ 45.727961] livepatch: 'livepatch_callbacks_demo': patching complete | ||
210 | |||
211 | When a targeted module is subsequently loaded, only its pre/post-patch | ||
212 | callbacks are executed: | ||
213 | |||
214 | % insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
215 | [ 46.560845] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' | ||
216 | [ 46.561988] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init | ||
217 | [ 46.563452] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init | ||
218 | [ 46.565495] livepatch_callbacks_mod: livepatch_callbacks_mod_init | ||
219 | |||
220 | On livepatch disable, all currently loaded klp_objects' (vmlinux and | ||
221 | livepatch_callbacks_mod) pre/post-unpatch callbacks are executed: | ||
222 | |||
223 | % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
224 | [ 48.568885] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition | ||
225 | [ 48.568910] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux | ||
226 | [ 48.569441] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state | ||
227 | [ 48.570502] livepatch: 'livepatch_callbacks_demo': starting unpatching transition | ||
228 | [ 49.759091] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
229 | [ 49.759171] livepatch_callbacks_demo: post_unpatch_callback: vmlinux | ||
230 | [ 49.759742] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state | ||
231 | [ 49.760690] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
232 | |||
233 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
234 | % rmmod samples/livepatch/livepatch-callbacks-mod.ko | ||
235 | [ 52.592283] livepatch_callbacks_mod: livepatch_callbacks_mod_exit | ||
236 | |||
237 | |||
238 | Test 3 | ||
239 | ------ | ||
240 | |||
241 | Test loading the livepatch after a targeted kernel module, then unload | ||
242 | the kernel module before disabling the livepatch. This tests the | ||
243 | livepatch core's module_going handler: | ||
244 | |||
245 | - load target module | ||
246 | - load livepatch | ||
247 | - unload target module | ||
248 | - disable livepatch | ||
249 | - unload livepatch | ||
250 | |||
251 | First load a target module, then the livepatch: | ||
252 | |||
253 | % insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
254 | [ 54.607948] livepatch_callbacks_mod: livepatch_callbacks_mod_init | ||
255 | |||
256 | % insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
257 | [ 56.613919] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
258 | [ 56.614411] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
259 | [ 56.614436] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
260 | [ 56.614818] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state | ||
261 | [ 56.615656] livepatch: 'livepatch_callbacks_demo': starting patching transition | ||
262 | [ 57.759070] livepatch: 'livepatch_callbacks_demo': completing patching transition | ||
263 | [ 57.759147] livepatch_callbacks_demo: post_patch_callback: vmlinux | ||
264 | [ 57.759621] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state | ||
265 | [ 57.760307] livepatch: 'livepatch_callbacks_demo': patching complete | ||
266 | |||
267 | When a target module is unloaded, the livepatch is only reverted from | ||
268 | that klp_object (livepatch_callbacks_mod). As such, only its pre and | ||
269 | post-unpatch callbacks are executed when this occurs: | ||
270 | |||
271 | % rmmod samples/livepatch/livepatch-callbacks-mod.ko | ||
272 | [ 58.623409] livepatch_callbacks_mod: livepatch_callbacks_mod_exit | ||
273 | [ 58.623903] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away | ||
274 | [ 58.624658] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' | ||
275 | [ 58.625305] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away | ||
276 | |||
277 | When the livepatch is disabled, pre and post-unpatch callbacks are run | ||
278 | for the remaining klp_object, vmlinux: | ||
279 | |||
280 | % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
281 | [ 60.638420] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition | ||
282 | [ 60.638444] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux | ||
283 | [ 60.638996] livepatch: 'livepatch_callbacks_demo': starting unpatching transition | ||
284 | [ 61.727088] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
285 | [ 61.727165] livepatch_callbacks_demo: post_unpatch_callback: vmlinux | ||
286 | [ 61.727985] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
287 | |||
288 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
289 | |||
290 | |||
291 | Test 4 | ||
292 | ------ | ||
293 | |||
294 | This test is similar to the previous test, however the livepatch is | ||
295 | loaded first. This tests the livepatch core's module_coming and | ||
296 | module_going handlers: | ||
297 | |||
298 | - load livepatch | ||
299 | - load target module | ||
300 | - unload target module | ||
301 | - disable livepatch | ||
302 | - unload livepatch | ||
303 | |||
304 | First load the livepatch: | ||
305 | |||
306 | % insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
307 | [ 64.661552] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
308 | [ 64.662147] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
309 | [ 64.662175] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
310 | [ 64.662850] livepatch: 'livepatch_callbacks_demo': starting patching transition | ||
311 | [ 65.695056] livepatch: 'livepatch_callbacks_demo': completing patching transition | ||
312 | [ 65.695147] livepatch_callbacks_demo: post_patch_callback: vmlinux | ||
313 | [ 65.695561] livepatch: 'livepatch_callbacks_demo': patching complete | ||
314 | |||
315 | When a targeted kernel module is subsequently loaded, only its | ||
316 | pre/post-patch callbacks are executed: | ||
317 | |||
318 | % insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
319 | [ 66.669196] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' | ||
320 | [ 66.669882] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init | ||
321 | [ 66.670744] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init | ||
322 | [ 66.672873] livepatch_callbacks_mod: livepatch_callbacks_mod_init | ||
323 | |||
324 | When the target module is unloaded, the livepatch is only reverted from | ||
325 | the livepatch_callbacks_mod klp_object. As such, only pre and | ||
326 | post-unpatch callbacks are executed when this occurs: | ||
327 | |||
328 | % rmmod samples/livepatch/livepatch-callbacks-mod.ko | ||
329 | [ 68.680065] livepatch_callbacks_mod: livepatch_callbacks_mod_exit | ||
330 | [ 68.680688] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away | ||
331 | [ 68.681452] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' | ||
332 | [ 68.682094] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away | ||
333 | |||
334 | % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
335 | [ 70.689225] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition | ||
336 | [ 70.689256] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux | ||
337 | [ 70.689882] livepatch: 'livepatch_callbacks_demo': starting unpatching transition | ||
338 | [ 71.711080] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
339 | [ 71.711481] livepatch_callbacks_demo: post_unpatch_callback: vmlinux | ||
340 | [ 71.711988] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
341 | |||
342 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
343 | |||
344 | |||
345 | Test 5 | ||
346 | ------ | ||
347 | |||
348 | A simple test of loading a livepatch without one of its patch target | ||
349 | klp_objects ever loaded (livepatch_callbacks_mod): | ||
350 | |||
351 | - load livepatch | ||
352 | - disable livepatch | ||
353 | - unload livepatch | ||
354 | |||
355 | Load the livepatch: | ||
356 | |||
357 | % insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
358 | [ 74.711081] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
359 | [ 74.711595] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
360 | [ 74.711639] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
361 | [ 74.712272] livepatch: 'livepatch_callbacks_demo': starting patching transition | ||
362 | [ 75.743137] livepatch: 'livepatch_callbacks_demo': completing patching transition | ||
363 | [ 75.743219] livepatch_callbacks_demo: post_patch_callback: vmlinux | ||
364 | [ 75.743867] livepatch: 'livepatch_callbacks_demo': patching complete | ||
365 | |||
366 | As expected, only pre/post-(un)patch handlers are executed for vmlinux: | ||
367 | |||
368 | % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
369 | [ 76.716254] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition | ||
370 | [ 76.716278] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux | ||
371 | [ 76.716666] livepatch: 'livepatch_callbacks_demo': starting unpatching transition | ||
372 | [ 77.727089] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
373 | [ 77.727194] livepatch_callbacks_demo: post_unpatch_callback: vmlinux | ||
374 | [ 77.727907] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
375 | |||
376 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
377 | |||
378 | |||
379 | Test 6 | ||
380 | ------ | ||
381 | |||
382 | Test a scenario where a vmlinux pre-patch callback returns a non-zero | ||
383 | status (ie, failure): | ||
384 | |||
385 | - load target module | ||
386 | - load livepatch -ENODEV | ||
387 | - unload target module | ||
388 | |||
389 | First load a target module: | ||
390 | |||
391 | % insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
392 | [ 80.740520] livepatch_callbacks_mod: livepatch_callbacks_mod_init | ||
393 | |||
394 | Load the livepatch module, setting its 'pre_patch_ret' value to -19 | ||
395 | (-ENODEV). When its vmlinux pre-patch callback executed, this status | ||
396 | code will propagate back to the module-loading subsystem. The result is | ||
397 | that the insmod command refuses to load the livepatch module: | ||
398 | |||
399 | % insmod samples/livepatch/livepatch-callbacks-demo.ko pre_patch_ret=-19 | ||
400 | [ 82.747326] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
401 | [ 82.747743] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
402 | [ 82.747767] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
403 | [ 82.748237] livepatch: pre-patch callback failed for object 'vmlinux' | ||
404 | [ 82.748637] livepatch: failed to enable patch 'livepatch_callbacks_demo' | ||
405 | [ 82.749059] livepatch: 'livepatch_callbacks_demo': canceling transition, going to unpatch | ||
406 | [ 82.749060] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
407 | [ 82.749868] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
408 | [ 82.765809] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-demo.ko: No such device | ||
409 | |||
410 | % rmmod samples/livepatch/livepatch-callbacks-mod.ko | ||
411 | [ 84.774238] livepatch_callbacks_mod: livepatch_callbacks_mod_exit | ||
412 | |||
413 | |||
414 | Test 7 | ||
415 | ------ | ||
416 | |||
417 | Similar to the previous test, setup a livepatch such that its vmlinux | ||
418 | pre-patch callback returns success. However, when a targeted kernel | ||
419 | module is later loaded, have the livepatch return a failing status code: | ||
420 | |||
421 | - load livepatch | ||
422 | - setup -ENODEV | ||
423 | - load target module | ||
424 | - disable livepatch | ||
425 | - unload livepatch | ||
426 | |||
427 | Load the livepatch, notice vmlinux pre-patch callback succeeds: | ||
428 | |||
429 | % insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
430 | [ 86.787845] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
431 | [ 86.788325] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
432 | [ 86.788427] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
433 | [ 86.788821] livepatch: 'livepatch_callbacks_demo': starting patching transition | ||
434 | [ 87.711069] livepatch: 'livepatch_callbacks_demo': completing patching transition | ||
435 | [ 87.711143] livepatch_callbacks_demo: post_patch_callback: vmlinux | ||
436 | [ 87.711886] livepatch: 'livepatch_callbacks_demo': patching complete | ||
437 | |||
438 | Set a trap so subsequent pre-patch callbacks to this livepatch will | ||
439 | return -ENODEV: | ||
440 | |||
441 | % echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret | ||
442 | |||
443 | The livepatch pre-patch callback for subsequently loaded target modules | ||
444 | will return failure, so the module loader refuses to load the kernel | ||
445 | module. Notice that no post-patch or pre/post-unpatch callbacks are | ||
446 | executed for this klp_object: | ||
447 | |||
448 | % insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
449 | [ 90.796976] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' | ||
450 | [ 90.797834] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init | ||
451 | [ 90.798900] livepatch: pre-patch callback failed for object 'livepatch_callbacks_mod' | ||
452 | [ 90.799652] livepatch: patch 'livepatch_callbacks_demo' failed for module 'livepatch_callbacks_mod', refusing to load module 'livepatch_callbacks_mod' | ||
453 | [ 90.819737] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device | ||
454 | |||
455 | However, pre/post-unpatch callbacks run for the vmlinux klp_object: | ||
456 | |||
457 | % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
458 | [ 92.823547] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition | ||
459 | [ 92.823573] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux | ||
460 | [ 92.824331] livepatch: 'livepatch_callbacks_demo': starting unpatching transition | ||
461 | [ 93.727128] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
462 | [ 93.727327] livepatch_callbacks_demo: post_unpatch_callback: vmlinux | ||
463 | [ 93.727861] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
464 | |||
465 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
466 | |||
467 | |||
468 | Test 8 | ||
469 | ------ | ||
470 | |||
471 | Test loading multiple targeted kernel modules. This test-case is | ||
472 | mainly for comparing with the next test-case. | ||
473 | |||
474 | - load busy target module (0s sleep), | ||
475 | - load livepatch | ||
476 | - load target module | ||
477 | - unload target module | ||
478 | - disable livepatch | ||
479 | - unload livepatch | ||
480 | - unload busy target module | ||
481 | |||
482 | |||
483 | Load a target "busy" kernel module which kicks off a worker function | ||
484 | that immediately exits: | ||
485 | |||
486 | % insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=0 | ||
487 | [ 96.910107] livepatch_callbacks_busymod: livepatch_callbacks_mod_init | ||
488 | [ 96.910600] livepatch_callbacks_busymod: busymod_work_func, sleeping 0 seconds ... | ||
489 | [ 96.913024] livepatch_callbacks_busymod: busymod_work_func exit | ||
490 | |||
491 | Proceed with loading the livepatch and another ordinary target module, | ||
492 | notice that the post-patch callbacks are executed and the transition | ||
493 | completes quickly: | ||
494 | |||
495 | % insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
496 | [ 98.917892] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
497 | [ 98.918426] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
498 | [ 98.918453] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
499 | [ 98.918955] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state | ||
500 | [ 98.923835] livepatch: 'livepatch_callbacks_demo': starting patching transition | ||
501 | [ 99.743104] livepatch: 'livepatch_callbacks_demo': completing patching transition | ||
502 | [ 99.743156] livepatch_callbacks_demo: post_patch_callback: vmlinux | ||
503 | [ 99.743679] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state | ||
504 | [ 99.744616] livepatch: 'livepatch_callbacks_demo': patching complete | ||
505 | |||
506 | % insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
507 | [ 100.930955] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' | ||
508 | [ 100.931668] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init | ||
509 | [ 100.932645] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init | ||
510 | [ 100.934125] livepatch_callbacks_mod: livepatch_callbacks_mod_init | ||
511 | |||
512 | % rmmod samples/livepatch/livepatch-callbacks-mod.ko | ||
513 | [ 102.942805] livepatch_callbacks_mod: livepatch_callbacks_mod_exit | ||
514 | [ 102.943640] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away | ||
515 | [ 102.944585] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' | ||
516 | [ 102.945455] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away | ||
517 | |||
518 | % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
519 | [ 104.953815] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition | ||
520 | [ 104.953838] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux | ||
521 | [ 104.954431] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state | ||
522 | [ 104.955426] livepatch: 'livepatch_callbacks_demo': starting unpatching transition | ||
523 | [ 106.719073] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
524 | [ 106.722633] livepatch_callbacks_demo: post_unpatch_callback: vmlinux | ||
525 | [ 106.723282] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state | ||
526 | [ 106.724279] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
527 | |||
528 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
529 | % rmmod samples/livepatch/livepatch-callbacks-busymod.ko | ||
530 | [ 108.975660] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit | ||
531 | |||
532 | |||
533 | Test 9 | ||
534 | ------ | ||
535 | |||
536 | A similar test as the previous one, but force the "busy" kernel module | ||
537 | to do longer work. | ||
538 | |||
539 | The livepatching core will refuse to patch a task that is currently | ||
540 | executing a to-be-patched function -- the consistency model stalls the | ||
541 | current patch transition until this safety-check is met. Test a | ||
542 | scenario where one of a livepatch's target klp_objects sits on such a | ||
543 | function for a long time. Meanwhile, load and unload other target | ||
544 | kernel modules while the livepatch transition is in progress. | ||
545 | |||
546 | - load busy target module (30s sleep) | ||
547 | - load livepatch | ||
548 | - load target module | ||
549 | - unload target module | ||
550 | - disable livepatch | ||
551 | - unload livepatch | ||
552 | - unload busy target module | ||
553 | |||
554 | |||
555 | Load the "busy" kernel module, this time make it do 30 seconds worth of | ||
556 | work: | ||
557 | |||
558 | % insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30 | ||
559 | [ 110.993362] livepatch_callbacks_busymod: livepatch_callbacks_mod_init | ||
560 | [ 110.994059] livepatch_callbacks_busymod: busymod_work_func, sleeping 30 seconds ... | ||
561 | |||
562 | Meanwhile, the livepatch is loaded. Notice that the patch transition | ||
563 | does not complete as the targeted "busy" module is sitting on a | ||
564 | to-be-patched function: | ||
565 | |||
566 | % insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
567 | [ 113.000309] livepatch: enabling patch 'livepatch_callbacks_demo' | ||
568 | [ 113.000764] livepatch: 'livepatch_callbacks_demo': initializing patching transition | ||
569 | [ 113.000791] livepatch_callbacks_demo: pre_patch_callback: vmlinux | ||
570 | [ 113.001289] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state | ||
571 | [ 113.005208] livepatch: 'livepatch_callbacks_demo': starting patching transition | ||
572 | |||
573 | Load a second target module (this one is an ordinary idle kernel | ||
574 | module). Note that *no* post-patch callbacks will be executed while the | ||
575 | livepatch is still in transition: | ||
576 | |||
577 | % insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
578 | [ 115.012740] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' | ||
579 | [ 115.013406] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init | ||
580 | [ 115.015315] livepatch_callbacks_mod: livepatch_callbacks_mod_init | ||
581 | |||
582 | Request an unload of the simple kernel module. The patch is still | ||
583 | transitioning, so its pre-unpatch callbacks are skipped: | ||
584 | |||
585 | % rmmod samples/livepatch/livepatch-callbacks-mod.ko | ||
586 | [ 117.022626] livepatch_callbacks_mod: livepatch_callbacks_mod_exit | ||
587 | [ 117.023376] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' | ||
588 | [ 117.024533] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away | ||
589 | |||
590 | Finally the livepatch is disabled. Since none of the patch's | ||
591 | klp_object's post-patch callbacks executed, the remaining klp_object's | ||
592 | pre-unpatch callbacks are skipped: | ||
593 | |||
594 | % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
595 | [ 119.035408] livepatch: 'livepatch_callbacks_demo': reversing transition from patching to unpatching | ||
596 | [ 119.035485] livepatch: 'livepatch_callbacks_demo': starting unpatching transition | ||
597 | [ 119.711166] livepatch: 'livepatch_callbacks_demo': completing unpatching transition | ||
598 | [ 119.714179] livepatch_callbacks_demo: post_unpatch_callback: vmlinux | ||
599 | [ 119.714653] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state | ||
600 | [ 119.715437] livepatch: 'livepatch_callbacks_demo': unpatching complete | ||
601 | |||
602 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
603 | % rmmod samples/livepatch/livepatch-callbacks-busymod.ko | ||
604 | [ 141.279111] livepatch_callbacks_busymod: busymod_work_func exit | ||
605 | [ 141.279760] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit | ||
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index d08eddc00497..fc5c1be3f6f4 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h | |||
@@ -87,10 +87,35 @@ struct klp_func { | |||
87 | bool transition; | 87 | bool transition; |
88 | }; | 88 | }; |
89 | 89 | ||
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 | |||
90 | /** | 114 | /** |
91 | * struct klp_object - kernel object structure for live patching | 115 | * struct klp_object - kernel object structure for live patching |
92 | * @name: module name (or NULL for vmlinux) | 116 | * @name: module name (or NULL for vmlinux) |
93 | * @funcs: function entries for functions to be patched in the object | 117 | * @funcs: function entries for functions to be patched in the object |
118 | * @callbacks: functions to be executed pre/post (un)patching | ||
94 | * @kobj: kobject for sysfs resources | 119 | * @kobj: kobject for sysfs resources |
95 | * @mod: kernel module associated with the patched object | 120 | * @mod: kernel module associated with the patched object |
96 | * (NULL for vmlinux) | 121 | * (NULL for vmlinux) |
@@ -100,6 +125,7 @@ struct klp_object { | |||
100 | /* external */ | 125 | /* external */ |
101 | const char *name; | 126 | const char *name; |
102 | struct klp_func *funcs; | 127 | struct klp_func *funcs; |
128 | struct klp_callbacks callbacks; | ||
103 | 129 | ||
104 | /* internal */ | 130 | /* internal */ |
105 | struct kobject kobj; | 131 | struct kobject kobj; |
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; |
diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile index 539e81d433cd..2472ce39a18d 100644 --- a/samples/livepatch/Makefile +++ b/samples/livepatch/Makefile | |||
@@ -2,3 +2,6 @@ obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o | |||
2 | obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o | 2 | obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o |
3 | obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o | 3 | obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o |
4 | obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o | 4 | 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 | ||
diff --git a/samples/livepatch/livepatch-callbacks-busymod.c b/samples/livepatch/livepatch-callbacks-busymod.c new file mode 100644 index 000000000000..80d06e103f1b --- /dev/null +++ b/samples/livepatch/livepatch-callbacks-busymod.c | |||
@@ -0,0 +1,72 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com> | ||
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"); | ||
diff --git a/samples/livepatch/livepatch-callbacks-demo.c b/samples/livepatch/livepatch-callbacks-demo.c new file mode 100644 index 000000000000..3d115bd68442 --- /dev/null +++ b/samples/livepatch/livepatch-callbacks-demo.c | |||
@@ -0,0 +1,234 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com> | ||
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-demo.c - (un)patching callbacks livepatch demo | ||
20 | * | ||
21 | * | ||
22 | * Purpose | ||
23 | * ------- | ||
24 | * | ||
25 | * Demonstration of registering livepatch (un)patching callbacks. | ||
26 | * | ||
27 | * | ||
28 | * Usage | ||
29 | * ----- | ||
30 | * | ||
31 | * Step 1 - load the simple module | ||
32 | * | ||
33 | * insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
34 | * | ||
35 | * | ||
36 | * Step 2 - load the demonstration livepatch (with callbacks) | ||
37 | * | ||
38 | * insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
39 | * | ||
40 | * | ||
41 | * Step 3 - cleanup | ||
42 | * | ||
43 | * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
44 | * rmmod livepatch_callbacks_demo | ||
45 | * rmmod livepatch_callbacks_mod | ||
46 | * | ||
47 | * Watch dmesg output to see livepatch enablement, callback execution | ||
48 | * and patching operations for both vmlinux and module targets. | ||
49 | * | ||
50 | * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and | ||
51 | * livepatch-callbacks-demo.ko to observe what happens when a | ||
52 | * target module is loaded after a livepatch with callbacks. | ||
53 | * | ||
54 | * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch | ||
55 | * callback return status. Try setting up a non-zero status | ||
56 | * such as -19 (-ENODEV): | ||
57 | * | ||
58 | * # Load demo livepatch, vmlinux is patched | ||
59 | * insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
60 | * | ||
61 | * # Setup next pre-patch callback to return -ENODEV | ||
62 | * echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret | ||
63 | * | ||
64 | * # Module loader refuses to load the target module | ||
65 | * insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
66 | * insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device | ||
67 | * | ||
68 | * NOTE: There is a second target module, | ||
69 | * livepatch-callbacks-busymod.ko, available for experimenting | ||
70 | * with livepatch (un)patch callbacks. This module contains | ||
71 | * a 'sleep_secs' parameter that parks the module on one of the | ||
72 | * functions that the livepatch demo module wants to patch. | ||
73 | * Modifying this value and tweaking the order of module loads can | ||
74 | * effectively demonstrate stalled patch transitions: | ||
75 | * | ||
76 | * # Load a target module, let it park on 'busymod_work_func' for | ||
77 | * # thirty seconds | ||
78 | * insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30 | ||
79 | * | ||
80 | * # Meanwhile load the livepatch | ||
81 | * insmod samples/livepatch/livepatch-callbacks-demo.ko | ||
82 | * | ||
83 | * # ... then load and unload another target module while the | ||
84 | * # transition is in progress | ||
85 | * insmod samples/livepatch/livepatch-callbacks-mod.ko | ||
86 | * rmmod samples/livepatch/livepatch-callbacks-mod.ko | ||
87 | * | ||
88 | * # Finally cleanup | ||
89 | * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled | ||
90 | * rmmod samples/livepatch/livepatch-callbacks-demo.ko | ||
91 | */ | ||
92 | |||
93 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
94 | |||
95 | #include <linux/module.h> | ||
96 | #include <linux/kernel.h> | ||
97 | #include <linux/livepatch.h> | ||
98 | |||
99 | static int pre_patch_ret; | ||
100 | module_param(pre_patch_ret, int, 0644); | ||
101 | MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)"); | ||
102 | |||
103 | static const char *const module_state[] = { | ||
104 | [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", | ||
105 | [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", | ||
106 | [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", | ||
107 | [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", | ||
108 | }; | ||
109 | |||
110 | static void callback_info(const char *callback, struct klp_object *obj) | ||
111 | { | ||
112 | if (obj->mod) | ||
113 | pr_info("%s: %s -> %s\n", callback, obj->mod->name, | ||
114 | module_state[obj->mod->state]); | ||
115 | else | ||
116 | pr_info("%s: vmlinux\n", callback); | ||
117 | } | ||
118 | |||
119 | /* Executed on object patching (ie, patch enablement) */ | ||
120 | static int pre_patch_callback(struct klp_object *obj) | ||
121 | { | ||
122 | callback_info(__func__, obj); | ||
123 | return pre_patch_ret; | ||
124 | } | ||
125 | |||
126 | /* Executed on object unpatching (ie, patch disablement) */ | ||
127 | static void post_patch_callback(struct klp_object *obj) | ||
128 | { | ||
129 | callback_info(__func__, obj); | ||
130 | } | ||
131 | |||
132 | /* Executed on object unpatching (ie, patch disablement) */ | ||
133 | static void pre_unpatch_callback(struct klp_object *obj) | ||
134 | { | ||
135 | callback_info(__func__, obj); | ||
136 | } | ||
137 | |||
138 | /* Executed on object unpatching (ie, patch disablement) */ | ||
139 | static void post_unpatch_callback(struct klp_object *obj) | ||
140 | { | ||
141 | callback_info(__func__, obj); | ||
142 | } | ||
143 | |||
144 | static void patched_work_func(struct work_struct *work) | ||
145 | { | ||
146 | pr_info("%s\n", __func__); | ||
147 | } | ||
148 | |||
149 | static struct klp_func no_funcs[] = { | ||
150 | { } | ||
151 | }; | ||
152 | |||
153 | static struct klp_func busymod_funcs[] = { | ||
154 | { | ||
155 | .old_name = "busymod_work_func", | ||
156 | .new_func = patched_work_func, | ||
157 | }, { } | ||
158 | }; | ||
159 | |||
160 | static struct klp_object objs[] = { | ||
161 | { | ||
162 | .name = NULL, /* vmlinux */ | ||
163 | .funcs = no_funcs, | ||
164 | .callbacks = { | ||
165 | .pre_patch = pre_patch_callback, | ||
166 | .post_patch = post_patch_callback, | ||
167 | .pre_unpatch = pre_unpatch_callback, | ||
168 | .post_unpatch = post_unpatch_callback, | ||
169 | }, | ||
170 | }, { | ||
171 | .name = "livepatch_callbacks_mod", | ||
172 | .funcs = no_funcs, | ||
173 | .callbacks = { | ||
174 | .pre_patch = pre_patch_callback, | ||
175 | .post_patch = post_patch_callback, | ||
176 | .pre_unpatch = pre_unpatch_callback, | ||
177 | .post_unpatch = post_unpatch_callback, | ||
178 | }, | ||
179 | }, { | ||
180 | .name = "livepatch_callbacks_busymod", | ||
181 | .funcs = busymod_funcs, | ||
182 | .callbacks = { | ||
183 | .pre_patch = pre_patch_callback, | ||
184 | .post_patch = post_patch_callback, | ||
185 | .pre_unpatch = pre_unpatch_callback, | ||
186 | .post_unpatch = post_unpatch_callback, | ||
187 | }, | ||
188 | }, { } | ||
189 | }; | ||
190 | |||
191 | static struct klp_patch patch = { | ||
192 | .mod = THIS_MODULE, | ||
193 | .objs = objs, | ||
194 | }; | ||
195 | |||
196 | static int livepatch_callbacks_demo_init(void) | ||
197 | { | ||
198 | int ret; | ||
199 | |||
200 | if (!klp_have_reliable_stack() && !patch.immediate) { | ||
201 | /* | ||
202 | * WARNING: Be very careful when using 'patch.immediate' in | ||
203 | * your patches. It's ok to use it for simple patches like | ||
204 | * this, but for more complex patches which change function | ||
205 | * semantics, locking semantics, or data structures, it may not | ||
206 | * be safe. Use of this option will also prevent removal of | ||
207 | * the patch. | ||
208 | * | ||
209 | * See Documentation/livepatch/livepatch.txt for more details. | ||
210 | */ | ||
211 | patch.immediate = true; | ||
212 | pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n"); | ||
213 | } | ||
214 | |||
215 | ret = klp_register_patch(&patch); | ||
216 | if (ret) | ||
217 | return ret; | ||
218 | ret = klp_enable_patch(&patch); | ||
219 | if (ret) { | ||
220 | WARN_ON(klp_unregister_patch(&patch)); | ||
221 | return ret; | ||
222 | } | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static void livepatch_callbacks_demo_exit(void) | ||
227 | { | ||
228 | WARN_ON(klp_unregister_patch(&patch)); | ||
229 | } | ||
230 | |||
231 | module_init(livepatch_callbacks_demo_init); | ||
232 | module_exit(livepatch_callbacks_demo_exit); | ||
233 | MODULE_LICENSE("GPL"); | ||
234 | MODULE_INFO(livepatch, "Y"); | ||
diff --git a/samples/livepatch/livepatch-callbacks-mod.c b/samples/livepatch/livepatch-callbacks-mod.c new file mode 100644 index 000000000000..e610ce29ba44 --- /dev/null +++ b/samples/livepatch/livepatch-callbacks-mod.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 Joe Lawrence <joe.lawrence@redhat.com> | ||
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-mod.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-demo.c. | ||
33 | */ | ||
34 | |||
35 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
36 | |||
37 | #include <linux/module.h> | ||
38 | #include <linux/kernel.h> | ||
39 | |||
40 | static int livepatch_callbacks_mod_init(void) | ||
41 | { | ||
42 | pr_info("%s\n", __func__); | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static void livepatch_callbacks_mod_exit(void) | ||
47 | { | ||
48 | pr_info("%s\n", __func__); | ||
49 | } | ||
50 | |||
51 | module_init(livepatch_callbacks_mod_init); | ||
52 | module_exit(livepatch_callbacks_mod_exit); | ||
53 | MODULE_LICENSE("GPL"); | ||