diff options
-rw-r--r-- | Documentation/livepatch/callbacks.txt | 489 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | lib/Kconfig.debug | 22 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/livepatch/Makefile | 15 | ||||
-rw-r--r-- | lib/livepatch/test_klp_atomic_replace.c | 57 | ||||
-rw-r--r-- | lib/livepatch/test_klp_callbacks_busy.c | 43 | ||||
-rw-r--r-- | lib/livepatch/test_klp_callbacks_demo.c | 121 | ||||
-rw-r--r-- | lib/livepatch/test_klp_callbacks_demo2.c | 93 | ||||
-rw-r--r-- | lib/livepatch/test_klp_callbacks_mod.c | 24 | ||||
-rw-r--r-- | lib/livepatch/test_klp_livepatch.c | 51 | ||||
-rw-r--r-- | lib/livepatch/test_klp_shadow_vars.c | 236 | ||||
-rw-r--r-- | tools/testing/selftests/Makefile | 1 | ||||
-rw-r--r-- | tools/testing/selftests/livepatch/Makefile | 8 | ||||
-rw-r--r-- | tools/testing/selftests/livepatch/README | 43 | ||||
-rw-r--r-- | tools/testing/selftests/livepatch/config | 1 | ||||
-rw-r--r-- | tools/testing/selftests/livepatch/functions.sh | 203 | ||||
-rwxr-xr-x | tools/testing/selftests/livepatch/test-callbacks.sh | 587 | ||||
-rwxr-xr-x | tools/testing/selftests/livepatch/test-livepatch.sh | 168 | ||||
-rwxr-xr-x | tools/testing/selftests/livepatch/test-shadow-vars.sh | 60 |
20 files changed, 1740 insertions, 485 deletions
diff --git a/Documentation/livepatch/callbacks.txt b/Documentation/livepatch/callbacks.txt index c9776f48e458..182e31d4abce 100644 --- a/Documentation/livepatch/callbacks.txt +++ b/Documentation/livepatch/callbacks.txt | |||
@@ -118,488 +118,9 @@ similar change to their hw_features value. (Client functions of the | |||
118 | value may need to be updated accordingly.) | 118 | value may need to be updated accordingly.) |
119 | 119 | ||
120 | 120 | ||
121 | Test cases | 121 | Other Examples |
122 | ========== | 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 | 123 | ||
376 | % rmmod samples/livepatch/livepatch-callbacks-demo.ko | 124 | Sample livepatch modules demonstrating the callback API can be found in |
377 | 125 | samples/livepatch/ directory. These samples were modified for use in | |
378 | 126 | kselftests and can be found in the lib/livepatch directory. | |
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/MAINTAINERS b/MAINTAINERS index 39e75bbefc3d..b1c5cf166703 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -8832,6 +8832,7 @@ F: arch/x86/kernel/livepatch.c | |||
8832 | F: Documentation/livepatch/ | 8832 | F: Documentation/livepatch/ |
8833 | F: Documentation/ABI/testing/sysfs-kernel-livepatch | 8833 | F: Documentation/ABI/testing/sysfs-kernel-livepatch |
8834 | F: samples/livepatch/ | 8834 | F: samples/livepatch/ |
8835 | F: tools/testing/selftests/livepatch/ | ||
8835 | L: live-patching@vger.kernel.org | 8836 | L: live-patching@vger.kernel.org |
8836 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching.git | 8837 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching.git |
8837 | 8838 | ||
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d4df5b24d75e..0298eb8eaa4e 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -1991,6 +1991,27 @@ config TEST_MEMCAT_P | |||
1991 | 1991 | ||
1992 | If unsure, say N. | 1992 | If unsure, say N. |
1993 | 1993 | ||
1994 | config TEST_LIVEPATCH | ||
1995 | tristate "Test livepatching" | ||
1996 | default n | ||
1997 | depends on LIVEPATCH | ||
1998 | depends on m | ||
1999 | help | ||
2000 | Test kernel livepatching features for correctness. The tests will | ||
2001 | load test modules that will be livepatched in various scenarios. | ||
2002 | |||
2003 | To run all the livepatching tests: | ||
2004 | |||
2005 | make -C tools/testing/selftests TARGETS=livepatch run_tests | ||
2006 | |||
2007 | Alternatively, individual tests may be invoked: | ||
2008 | |||
2009 | tools/testing/selftests/livepatch/test-callbacks.sh | ||
2010 | tools/testing/selftests/livepatch/test-livepatch.sh | ||
2011 | tools/testing/selftests/livepatch/test-shadow-vars.sh | ||
2012 | |||
2013 | If unsure, say N. | ||
2014 | |||
1994 | config TEST_OBJAGG | 2015 | config TEST_OBJAGG |
1995 | tristate "Perform selftest on object aggreration manager" | 2016 | tristate "Perform selftest on object aggreration manager" |
1996 | default n | 2017 | default n |
@@ -1999,7 +2020,6 @@ config TEST_OBJAGG | |||
1999 | Enable this option to test object aggregation manager on boot | 2020 | Enable this option to test object aggregation manager on boot |
2000 | (or module load). | 2021 | (or module load). |
2001 | 2022 | ||
2002 | If unsure, say N. | ||
2003 | 2023 | ||
2004 | endif # RUNTIME_TESTING_MENU | 2024 | endif # RUNTIME_TESTING_MENU |
2005 | 2025 | ||
diff --git a/lib/Makefile b/lib/Makefile index e1b59da71418..a1cfa1975ca3 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -77,6 +77,8 @@ obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o | |||
77 | obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o | 77 | obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o |
78 | obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o | 78 | obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o |
79 | 79 | ||
80 | obj-$(CONFIG_TEST_LIVEPATCH) += livepatch/ | ||
81 | |||
80 | ifeq ($(CONFIG_DEBUG_KOBJECT),y) | 82 | ifeq ($(CONFIG_DEBUG_KOBJECT),y) |
81 | CFLAGS_kobject.o += -DDEBUG | 83 | CFLAGS_kobject.o += -DDEBUG |
82 | CFLAGS_kobject_uevent.o += -DDEBUG | 84 | CFLAGS_kobject_uevent.o += -DDEBUG |
diff --git a/lib/livepatch/Makefile b/lib/livepatch/Makefile new file mode 100644 index 000000000000..26900ddaef82 --- /dev/null +++ b/lib/livepatch/Makefile | |||
@@ -0,0 +1,15 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Makefile for livepatch test code. | ||
4 | |||
5 | obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \ | ||
6 | test_klp_callbacks_demo.o \ | ||
7 | test_klp_callbacks_demo2.o \ | ||
8 | test_klp_callbacks_busy.o \ | ||
9 | test_klp_callbacks_mod.o \ | ||
10 | test_klp_livepatch.o \ | ||
11 | test_klp_shadow_vars.o | ||
12 | |||
13 | # Target modules to be livepatched require CC_FLAGS_FTRACE | ||
14 | CFLAGS_test_klp_callbacks_busy.o += $(CC_FLAGS_FTRACE) | ||
15 | CFLAGS_test_klp_callbacks_mod.o += $(CC_FLAGS_FTRACE) | ||
diff --git a/lib/livepatch/test_klp_atomic_replace.c b/lib/livepatch/test_klp_atomic_replace.c new file mode 100644 index 000000000000..5af7093ca00c --- /dev/null +++ b/lib/livepatch/test_klp_atomic_replace.c | |||
@@ -0,0 +1,57 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
3 | |||
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/livepatch.h> | ||
9 | |||
10 | static int replace; | ||
11 | module_param(replace, int, 0644); | ||
12 | MODULE_PARM_DESC(replace, "replace (default=0)"); | ||
13 | |||
14 | #include <linux/seq_file.h> | ||
15 | static int livepatch_meminfo_proc_show(struct seq_file *m, void *v) | ||
16 | { | ||
17 | seq_printf(m, "%s: %s\n", THIS_MODULE->name, | ||
18 | "this has been live patched"); | ||
19 | return 0; | ||
20 | } | ||
21 | |||
22 | static struct klp_func funcs[] = { | ||
23 | { | ||
24 | .old_name = "meminfo_proc_show", | ||
25 | .new_func = livepatch_meminfo_proc_show, | ||
26 | }, {} | ||
27 | }; | ||
28 | |||
29 | static struct klp_object objs[] = { | ||
30 | { | ||
31 | /* name being NULL means vmlinux */ | ||
32 | .funcs = funcs, | ||
33 | }, {} | ||
34 | }; | ||
35 | |||
36 | static struct klp_patch patch = { | ||
37 | .mod = THIS_MODULE, | ||
38 | .objs = objs, | ||
39 | /* set .replace in the init function below for demo purposes */ | ||
40 | }; | ||
41 | |||
42 | static int test_klp_atomic_replace_init(void) | ||
43 | { | ||
44 | patch.replace = replace; | ||
45 | return klp_enable_patch(&patch); | ||
46 | } | ||
47 | |||
48 | static void test_klp_atomic_replace_exit(void) | ||
49 | { | ||
50 | } | ||
51 | |||
52 | module_init(test_klp_atomic_replace_init); | ||
53 | module_exit(test_klp_atomic_replace_exit); | ||
54 | MODULE_LICENSE("GPL"); | ||
55 | MODULE_INFO(livepatch, "Y"); | ||
56 | MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>"); | ||
57 | MODULE_DESCRIPTION("Livepatch test: atomic replace"); | ||
diff --git a/lib/livepatch/test_klp_callbacks_busy.c b/lib/livepatch/test_klp_callbacks_busy.c new file mode 100644 index 000000000000..40beddf8a0e2 --- /dev/null +++ b/lib/livepatch/test_klp_callbacks_busy.c | |||
@@ -0,0 +1,43 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
3 | |||
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/workqueue.h> | ||
9 | #include <linux/delay.h> | ||
10 | |||
11 | static int sleep_secs; | ||
12 | module_param(sleep_secs, int, 0644); | ||
13 | MODULE_PARM_DESC(sleep_secs, "sleep_secs (default=0)"); | ||
14 | |||
15 | static void busymod_work_func(struct work_struct *work); | ||
16 | static DECLARE_DELAYED_WORK(work, busymod_work_func); | ||
17 | |||
18 | static void busymod_work_func(struct work_struct *work) | ||
19 | { | ||
20 | pr_info("%s, sleeping %d seconds ...\n", __func__, sleep_secs); | ||
21 | msleep(sleep_secs * 1000); | ||
22 | pr_info("%s exit\n", __func__); | ||
23 | } | ||
24 | |||
25 | static int test_klp_callbacks_busy_init(void) | ||
26 | { | ||
27 | pr_info("%s\n", __func__); | ||
28 | schedule_delayed_work(&work, | ||
29 | msecs_to_jiffies(1000 * 0)); | ||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | static void test_klp_callbacks_busy_exit(void) | ||
34 | { | ||
35 | cancel_delayed_work_sync(&work); | ||
36 | pr_info("%s\n", __func__); | ||
37 | } | ||
38 | |||
39 | module_init(test_klp_callbacks_busy_init); | ||
40 | module_exit(test_klp_callbacks_busy_exit); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>"); | ||
43 | MODULE_DESCRIPTION("Livepatch test: busy target module"); | ||
diff --git a/lib/livepatch/test_klp_callbacks_demo.c b/lib/livepatch/test_klp_callbacks_demo.c new file mode 100644 index 000000000000..3fd8fe1cd1cc --- /dev/null +++ b/lib/livepatch/test_klp_callbacks_demo.c | |||
@@ -0,0 +1,121 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
3 | |||
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/livepatch.h> | ||
9 | |||
10 | static int pre_patch_ret; | ||
11 | module_param(pre_patch_ret, int, 0644); | ||
12 | MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)"); | ||
13 | |||
14 | static const char *const module_state[] = { | ||
15 | [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", | ||
16 | [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", | ||
17 | [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", | ||
18 | [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", | ||
19 | }; | ||
20 | |||
21 | static void callback_info(const char *callback, struct klp_object *obj) | ||
22 | { | ||
23 | if (obj->mod) | ||
24 | pr_info("%s: %s -> %s\n", callback, obj->mod->name, | ||
25 | module_state[obj->mod->state]); | ||
26 | else | ||
27 | pr_info("%s: vmlinux\n", callback); | ||
28 | } | ||
29 | |||
30 | /* Executed on object patching (ie, patch enablement) */ | ||
31 | static int pre_patch_callback(struct klp_object *obj) | ||
32 | { | ||
33 | callback_info(__func__, obj); | ||
34 | return pre_patch_ret; | ||
35 | } | ||
36 | |||
37 | /* Executed on object unpatching (ie, patch disablement) */ | ||
38 | static void post_patch_callback(struct klp_object *obj) | ||
39 | { | ||
40 | callback_info(__func__, obj); | ||
41 | } | ||
42 | |||
43 | /* Executed on object unpatching (ie, patch disablement) */ | ||
44 | static void pre_unpatch_callback(struct klp_object *obj) | ||
45 | { | ||
46 | callback_info(__func__, obj); | ||
47 | } | ||
48 | |||
49 | /* Executed on object unpatching (ie, patch disablement) */ | ||
50 | static void post_unpatch_callback(struct klp_object *obj) | ||
51 | { | ||
52 | callback_info(__func__, obj); | ||
53 | } | ||
54 | |||
55 | static void patched_work_func(struct work_struct *work) | ||
56 | { | ||
57 | pr_info("%s\n", __func__); | ||
58 | } | ||
59 | |||
60 | static struct klp_func no_funcs[] = { | ||
61 | {} | ||
62 | }; | ||
63 | |||
64 | static struct klp_func busymod_funcs[] = { | ||
65 | { | ||
66 | .old_name = "busymod_work_func", | ||
67 | .new_func = patched_work_func, | ||
68 | }, {} | ||
69 | }; | ||
70 | |||
71 | static struct klp_object objs[] = { | ||
72 | { | ||
73 | .name = NULL, /* vmlinux */ | ||
74 | .funcs = no_funcs, | ||
75 | .callbacks = { | ||
76 | .pre_patch = pre_patch_callback, | ||
77 | .post_patch = post_patch_callback, | ||
78 | .pre_unpatch = pre_unpatch_callback, | ||
79 | .post_unpatch = post_unpatch_callback, | ||
80 | }, | ||
81 | }, { | ||
82 | .name = "test_klp_callbacks_mod", | ||
83 | .funcs = no_funcs, | ||
84 | .callbacks = { | ||
85 | .pre_patch = pre_patch_callback, | ||
86 | .post_patch = post_patch_callback, | ||
87 | .pre_unpatch = pre_unpatch_callback, | ||
88 | .post_unpatch = post_unpatch_callback, | ||
89 | }, | ||
90 | }, { | ||
91 | .name = "test_klp_callbacks_busy", | ||
92 | .funcs = busymod_funcs, | ||
93 | .callbacks = { | ||
94 | .pre_patch = pre_patch_callback, | ||
95 | .post_patch = post_patch_callback, | ||
96 | .pre_unpatch = pre_unpatch_callback, | ||
97 | .post_unpatch = post_unpatch_callback, | ||
98 | }, | ||
99 | }, { } | ||
100 | }; | ||
101 | |||
102 | static struct klp_patch patch = { | ||
103 | .mod = THIS_MODULE, | ||
104 | .objs = objs, | ||
105 | }; | ||
106 | |||
107 | static int test_klp_callbacks_demo_init(void) | ||
108 | { | ||
109 | return klp_enable_patch(&patch); | ||
110 | } | ||
111 | |||
112 | static void test_klp_callbacks_demo_exit(void) | ||
113 | { | ||
114 | } | ||
115 | |||
116 | module_init(test_klp_callbacks_demo_init); | ||
117 | module_exit(test_klp_callbacks_demo_exit); | ||
118 | MODULE_LICENSE("GPL"); | ||
119 | MODULE_INFO(livepatch, "Y"); | ||
120 | MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>"); | ||
121 | MODULE_DESCRIPTION("Livepatch test: livepatch demo"); | ||
diff --git a/lib/livepatch/test_klp_callbacks_demo2.c b/lib/livepatch/test_klp_callbacks_demo2.c new file mode 100644 index 000000000000..5417573e80af --- /dev/null +++ b/lib/livepatch/test_klp_callbacks_demo2.c | |||
@@ -0,0 +1,93 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
3 | |||
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/livepatch.h> | ||
9 | |||
10 | static int replace; | ||
11 | module_param(replace, int, 0644); | ||
12 | MODULE_PARM_DESC(replace, "replace (default=0)"); | ||
13 | |||
14 | static const char *const module_state[] = { | ||
15 | [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", | ||
16 | [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", | ||
17 | [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", | ||
18 | [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", | ||
19 | }; | ||
20 | |||
21 | static void callback_info(const char *callback, struct klp_object *obj) | ||
22 | { | ||
23 | if (obj->mod) | ||
24 | pr_info("%s: %s -> %s\n", callback, obj->mod->name, | ||
25 | module_state[obj->mod->state]); | ||
26 | else | ||
27 | pr_info("%s: vmlinux\n", callback); | ||
28 | } | ||
29 | |||
30 | /* Executed on object patching (ie, patch enablement) */ | ||
31 | static int pre_patch_callback(struct klp_object *obj) | ||
32 | { | ||
33 | callback_info(__func__, obj); | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | /* Executed on object unpatching (ie, patch disablement) */ | ||
38 | static void post_patch_callback(struct klp_object *obj) | ||
39 | { | ||
40 | callback_info(__func__, obj); | ||
41 | } | ||
42 | |||
43 | /* Executed on object unpatching (ie, patch disablement) */ | ||
44 | static void pre_unpatch_callback(struct klp_object *obj) | ||
45 | { | ||
46 | callback_info(__func__, obj); | ||
47 | } | ||
48 | |||
49 | /* Executed on object unpatching (ie, patch disablement) */ | ||
50 | static void post_unpatch_callback(struct klp_object *obj) | ||
51 | { | ||
52 | callback_info(__func__, obj); | ||
53 | } | ||
54 | |||
55 | static struct klp_func no_funcs[] = { | ||
56 | { } | ||
57 | }; | ||
58 | |||
59 | static struct klp_object objs[] = { | ||
60 | { | ||
61 | .name = NULL, /* vmlinux */ | ||
62 | .funcs = no_funcs, | ||
63 | .callbacks = { | ||
64 | .pre_patch = pre_patch_callback, | ||
65 | .post_patch = post_patch_callback, | ||
66 | .pre_unpatch = pre_unpatch_callback, | ||
67 | .post_unpatch = post_unpatch_callback, | ||
68 | }, | ||
69 | }, { } | ||
70 | }; | ||
71 | |||
72 | static struct klp_patch patch = { | ||
73 | .mod = THIS_MODULE, | ||
74 | .objs = objs, | ||
75 | /* set .replace in the init function below for demo purposes */ | ||
76 | }; | ||
77 | |||
78 | static int test_klp_callbacks_demo2_init(void) | ||
79 | { | ||
80 | patch.replace = replace; | ||
81 | return klp_enable_patch(&patch); | ||
82 | } | ||
83 | |||
84 | static void test_klp_callbacks_demo2_exit(void) | ||
85 | { | ||
86 | } | ||
87 | |||
88 | module_init(test_klp_callbacks_demo2_init); | ||
89 | module_exit(test_klp_callbacks_demo2_exit); | ||
90 | MODULE_LICENSE("GPL"); | ||
91 | MODULE_INFO(livepatch, "Y"); | ||
92 | MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>"); | ||
93 | MODULE_DESCRIPTION("Livepatch test: livepatch demo2"); | ||
diff --git a/lib/livepatch/test_klp_callbacks_mod.c b/lib/livepatch/test_klp_callbacks_mod.c new file mode 100644 index 000000000000..8fbe645b1c2c --- /dev/null +++ b/lib/livepatch/test_klp_callbacks_mod.c | |||
@@ -0,0 +1,24 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
3 | |||
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | |||
9 | static int test_klp_callbacks_mod_init(void) | ||
10 | { | ||
11 | pr_info("%s\n", __func__); | ||
12 | return 0; | ||
13 | } | ||
14 | |||
15 | static void test_klp_callbacks_mod_exit(void) | ||
16 | { | ||
17 | pr_info("%s\n", __func__); | ||
18 | } | ||
19 | |||
20 | module_init(test_klp_callbacks_mod_init); | ||
21 | module_exit(test_klp_callbacks_mod_exit); | ||
22 | MODULE_LICENSE("GPL"); | ||
23 | MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>"); | ||
24 | MODULE_DESCRIPTION("Livepatch test: target module"); | ||
diff --git a/lib/livepatch/test_klp_livepatch.c b/lib/livepatch/test_klp_livepatch.c new file mode 100644 index 000000000000..aff08199de71 --- /dev/null +++ b/lib/livepatch/test_klp_livepatch.c | |||
@@ -0,0 +1,51 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2014 Seth Jennings <sjenning@redhat.com> | ||
3 | |||
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/livepatch.h> | ||
9 | |||
10 | #include <linux/seq_file.h> | ||
11 | static int livepatch_cmdline_proc_show(struct seq_file *m, void *v) | ||
12 | { | ||
13 | seq_printf(m, "%s: %s\n", THIS_MODULE->name, | ||
14 | "this has been live patched"); | ||
15 | return 0; | ||
16 | } | ||
17 | |||
18 | static struct klp_func funcs[] = { | ||
19 | { | ||
20 | .old_name = "cmdline_proc_show", | ||
21 | .new_func = livepatch_cmdline_proc_show, | ||
22 | }, { } | ||
23 | }; | ||
24 | |||
25 | static struct klp_object objs[] = { | ||
26 | { | ||
27 | /* name being NULL means vmlinux */ | ||
28 | .funcs = funcs, | ||
29 | }, { } | ||
30 | }; | ||
31 | |||
32 | static struct klp_patch patch = { | ||
33 | .mod = THIS_MODULE, | ||
34 | .objs = objs, | ||
35 | }; | ||
36 | |||
37 | static int test_klp_livepatch_init(void) | ||
38 | { | ||
39 | return klp_enable_patch(&patch); | ||
40 | } | ||
41 | |||
42 | static void test_klp_livepatch_exit(void) | ||
43 | { | ||
44 | } | ||
45 | |||
46 | module_init(test_klp_livepatch_init); | ||
47 | module_exit(test_klp_livepatch_exit); | ||
48 | MODULE_LICENSE("GPL"); | ||
49 | MODULE_INFO(livepatch, "Y"); | ||
50 | MODULE_AUTHOR("Seth Jennings <sjenning@redhat.com>"); | ||
51 | MODULE_DESCRIPTION("Livepatch test: livepatch module"); | ||
diff --git a/lib/livepatch/test_klp_shadow_vars.c b/lib/livepatch/test_klp_shadow_vars.c new file mode 100644 index 000000000000..02f892f941dc --- /dev/null +++ b/lib/livepatch/test_klp_shadow_vars.c | |||
@@ -0,0 +1,236 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
3 | |||
4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/livepatch.h> | ||
10 | #include <linux/slab.h> | ||
11 | |||
12 | /* | ||
13 | * Keep a small list of pointers so that we can print address-agnostic | ||
14 | * pointer values. Use a rolling integer count to differentiate the values. | ||
15 | * Ironically we could have used the shadow variable API to do this, but | ||
16 | * let's not lean too heavily on the very code we're testing. | ||
17 | */ | ||
18 | static LIST_HEAD(ptr_list); | ||
19 | struct shadow_ptr { | ||
20 | void *ptr; | ||
21 | int id; | ||
22 | struct list_head list; | ||
23 | }; | ||
24 | |||
25 | static void free_ptr_list(void) | ||
26 | { | ||
27 | struct shadow_ptr *sp, *tmp_sp; | ||
28 | |||
29 | list_for_each_entry_safe(sp, tmp_sp, &ptr_list, list) { | ||
30 | list_del(&sp->list); | ||
31 | kfree(sp); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | static int ptr_id(void *ptr) | ||
36 | { | ||
37 | struct shadow_ptr *sp; | ||
38 | static int count; | ||
39 | |||
40 | list_for_each_entry(sp, &ptr_list, list) { | ||
41 | if (sp->ptr == ptr) | ||
42 | return sp->id; | ||
43 | } | ||
44 | |||
45 | sp = kmalloc(sizeof(*sp), GFP_ATOMIC); | ||
46 | if (!sp) | ||
47 | return -1; | ||
48 | sp->ptr = ptr; | ||
49 | sp->id = count++; | ||
50 | |||
51 | list_add(&sp->list, &ptr_list); | ||
52 | |||
53 | return sp->id; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Shadow variable wrapper functions that echo the function and arguments | ||
58 | * to the kernel log for testing verification. Don't display raw pointers, | ||
59 | * but use the ptr_id() value instead. | ||
60 | */ | ||
61 | static void *shadow_get(void *obj, unsigned long id) | ||
62 | { | ||
63 | void *ret = klp_shadow_get(obj, id); | ||
64 | |||
65 | pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n", | ||
66 | __func__, ptr_id(obj), id, ptr_id(ret)); | ||
67 | |||
68 | return ret; | ||
69 | } | ||
70 | |||
71 | static void *shadow_alloc(void *obj, unsigned long id, size_t size, | ||
72 | gfp_t gfp_flags, klp_shadow_ctor_t ctor, | ||
73 | void *ctor_data) | ||
74 | { | ||
75 | void *ret = klp_shadow_alloc(obj, id, size, gfp_flags, ctor, | ||
76 | ctor_data); | ||
77 | pr_info("klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d\n", | ||
78 | __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor), | ||
79 | ptr_id(ctor_data), ptr_id(ret)); | ||
80 | return ret; | ||
81 | } | ||
82 | |||
83 | static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size, | ||
84 | gfp_t gfp_flags, klp_shadow_ctor_t ctor, | ||
85 | void *ctor_data) | ||
86 | { | ||
87 | void *ret = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, | ||
88 | ctor_data); | ||
89 | pr_info("klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d\n", | ||
90 | __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor), | ||
91 | ptr_id(ctor_data), ptr_id(ret)); | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor) | ||
96 | { | ||
97 | klp_shadow_free(obj, id, dtor); | ||
98 | pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n", | ||
99 | __func__, ptr_id(obj), id, ptr_id(dtor)); | ||
100 | } | ||
101 | |||
102 | static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor) | ||
103 | { | ||
104 | klp_shadow_free_all(id, dtor); | ||
105 | pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n", | ||
106 | __func__, id, ptr_id(dtor)); | ||
107 | } | ||
108 | |||
109 | |||
110 | /* Shadow variable constructor - remember simple pointer data */ | ||
111 | static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data) | ||
112 | { | ||
113 | int **shadow_int = shadow_data; | ||
114 | *shadow_int = ctor_data; | ||
115 | pr_info("%s: PTR%d -> PTR%d\n", | ||
116 | __func__, ptr_id(shadow_int), ptr_id(ctor_data)); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static void shadow_dtor(void *obj, void *shadow_data) | ||
122 | { | ||
123 | pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n", | ||
124 | __func__, ptr_id(obj), ptr_id(shadow_data)); | ||
125 | } | ||
126 | |||
127 | static int test_klp_shadow_vars_init(void) | ||
128 | { | ||
129 | void *obj = THIS_MODULE; | ||
130 | int id = 0x1234; | ||
131 | size_t size = sizeof(int *); | ||
132 | gfp_t gfp_flags = GFP_KERNEL; | ||
133 | |||
134 | int var1, var2, var3, var4; | ||
135 | int **sv1, **sv2, **sv3, **sv4; | ||
136 | |||
137 | void *ret; | ||
138 | |||
139 | ptr_id(NULL); | ||
140 | ptr_id(&var1); | ||
141 | ptr_id(&var2); | ||
142 | ptr_id(&var3); | ||
143 | ptr_id(&var4); | ||
144 | |||
145 | /* | ||
146 | * With an empty shadow variable hash table, expect not to find | ||
147 | * any matches. | ||
148 | */ | ||
149 | ret = shadow_get(obj, id); | ||
150 | if (!ret) | ||
151 | pr_info(" got expected NULL result\n"); | ||
152 | |||
153 | /* | ||
154 | * Allocate a few shadow variables with different <obj> and <id>. | ||
155 | */ | ||
156 | sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1); | ||
157 | sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2); | ||
158 | sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3); | ||
159 | |||
160 | /* | ||
161 | * Verify we can find our new shadow variables and that they point | ||
162 | * to expected data. | ||
163 | */ | ||
164 | ret = shadow_get(obj, id); | ||
165 | if (ret == sv1 && *sv1 == &var1) | ||
166 | pr_info(" got expected PTR%d -> PTR%d result\n", | ||
167 | ptr_id(sv1), ptr_id(*sv1)); | ||
168 | ret = shadow_get(obj + 1, id); | ||
169 | if (ret == sv2 && *sv2 == &var2) | ||
170 | pr_info(" got expected PTR%d -> PTR%d result\n", | ||
171 | ptr_id(sv2), ptr_id(*sv2)); | ||
172 | ret = shadow_get(obj, id + 1); | ||
173 | if (ret == sv3 && *sv3 == &var3) | ||
174 | pr_info(" got expected PTR%d -> PTR%d result\n", | ||
175 | ptr_id(sv3), ptr_id(*sv3)); | ||
176 | |||
177 | /* | ||
178 | * Allocate or get a few more, this time with the same <obj>, <id>. | ||
179 | * The second invocation should return the same shadow var. | ||
180 | */ | ||
181 | sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4); | ||
182 | ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4); | ||
183 | if (ret == sv4 && *sv4 == &var4) | ||
184 | pr_info(" got expected PTR%d -> PTR%d result\n", | ||
185 | ptr_id(sv4), ptr_id(*sv4)); | ||
186 | |||
187 | /* | ||
188 | * Free the <obj=*, id> shadow variables and check that we can no | ||
189 | * longer find them. | ||
190 | */ | ||
191 | shadow_free(obj, id, shadow_dtor); /* sv1 */ | ||
192 | ret = shadow_get(obj, id); | ||
193 | if (!ret) | ||
194 | pr_info(" got expected NULL result\n"); | ||
195 | |||
196 | shadow_free(obj + 1, id, shadow_dtor); /* sv2 */ | ||
197 | ret = shadow_get(obj + 1, id); | ||
198 | if (!ret) | ||
199 | pr_info(" got expected NULL result\n"); | ||
200 | |||
201 | shadow_free(obj + 2, id, shadow_dtor); /* sv4 */ | ||
202 | ret = shadow_get(obj + 2, id); | ||
203 | if (!ret) | ||
204 | pr_info(" got expected NULL result\n"); | ||
205 | |||
206 | /* | ||
207 | * We should still find an <id+1> variable. | ||
208 | */ | ||
209 | ret = shadow_get(obj, id + 1); | ||
210 | if (ret == sv3 && *sv3 == &var3) | ||
211 | pr_info(" got expected PTR%d -> PTR%d result\n", | ||
212 | ptr_id(sv3), ptr_id(*sv3)); | ||
213 | |||
214 | /* | ||
215 | * Free all the <id+1> variables, too. | ||
216 | */ | ||
217 | shadow_free_all(id + 1, shadow_dtor); /* sv3 */ | ||
218 | ret = shadow_get(obj, id); | ||
219 | if (!ret) | ||
220 | pr_info(" shadow_get() got expected NULL result\n"); | ||
221 | |||
222 | |||
223 | free_ptr_list(); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static void test_klp_shadow_vars_exit(void) | ||
229 | { | ||
230 | } | ||
231 | |||
232 | module_init(test_klp_shadow_vars_init); | ||
233 | module_exit(test_klp_shadow_vars_exit); | ||
234 | MODULE_LICENSE("GPL"); | ||
235 | MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>"); | ||
236 | MODULE_DESCRIPTION("Livepatch test: shadow variables"); | ||
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 1a2bd15c5b6e..a8ba662da827 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
@@ -21,6 +21,7 @@ TARGETS += ir | |||
21 | TARGETS += kcmp | 21 | TARGETS += kcmp |
22 | TARGETS += kvm | 22 | TARGETS += kvm |
23 | TARGETS += lib | 23 | TARGETS += lib |
24 | TARGETS += livepatch | ||
24 | TARGETS += membarrier | 25 | TARGETS += membarrier |
25 | TARGETS += memfd | 26 | TARGETS += memfd |
26 | TARGETS += memory-hotplug | 27 | TARGETS += memory-hotplug |
diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile new file mode 100644 index 000000000000..af4aee79bebb --- /dev/null +++ b/tools/testing/selftests/livepatch/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | |||
3 | TEST_GEN_PROGS := \ | ||
4 | test-livepatch.sh \ | ||
5 | test-callbacks.sh \ | ||
6 | test-shadow-vars.sh | ||
7 | |||
8 | include ../lib.mk | ||
diff --git a/tools/testing/selftests/livepatch/README b/tools/testing/selftests/livepatch/README new file mode 100644 index 000000000000..b73cd0e2dd51 --- /dev/null +++ b/tools/testing/selftests/livepatch/README | |||
@@ -0,0 +1,43 @@ | |||
1 | ==================== | ||
2 | Livepatch Self Tests | ||
3 | ==================== | ||
4 | |||
5 | This is a small set of sanity tests for the kernel livepatching. | ||
6 | |||
7 | The test suite loads and unloads several test kernel modules to verify | ||
8 | livepatch behavior. Debug information is logged to the kernel's message | ||
9 | buffer and parsed for expected messages. (Note: the tests will clear | ||
10 | the message buffer between individual tests.) | ||
11 | |||
12 | |||
13 | Config | ||
14 | ------ | ||
15 | |||
16 | Set these config options and their prerequisites: | ||
17 | |||
18 | CONFIG_LIVEPATCH=y | ||
19 | CONFIG_TEST_LIVEPATCH=m | ||
20 | |||
21 | |||
22 | Running the tests | ||
23 | ----------------- | ||
24 | |||
25 | Test kernel modules are built as part of lib/ (make modules) and need to | ||
26 | be installed (make modules_install) as the test scripts will modprobe | ||
27 | them. | ||
28 | |||
29 | To run the livepatch selftests, from the top of the kernel source tree: | ||
30 | |||
31 | % make -C tools/testing/selftests TARGETS=livepatch run_tests | ||
32 | |||
33 | |||
34 | Adding tests | ||
35 | ------------ | ||
36 | |||
37 | See the common functions.sh file for the existing collection of utility | ||
38 | functions, most importantly set_dynamic_debug() and check_result(). The | ||
39 | latter function greps the kernel's ring buffer for "livepatch:" and | ||
40 | "test_klp" strings, so tests be sure to include one of those strings for | ||
41 | result comparison. Other utility functions include general module | ||
42 | loading and livepatch loading helpers (waiting for patch transitions, | ||
43 | sysfs entries, etc.) | ||
diff --git a/tools/testing/selftests/livepatch/config b/tools/testing/selftests/livepatch/config new file mode 100644 index 000000000000..0dd7700464a8 --- /dev/null +++ b/tools/testing/selftests/livepatch/config | |||
@@ -0,0 +1 @@ | |||
CONFIG_TEST_LIVEPATCH=m | |||
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh new file mode 100644 index 000000000000..c7b9fb45d7c9 --- /dev/null +++ b/tools/testing/selftests/livepatch/functions.sh | |||
@@ -0,0 +1,203 @@ | |||
1 | #!/bin/bash | ||
2 | # SPDX-License-Identifier: GPL-2.0 | ||
3 | # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
4 | |||
5 | # Shell functions for the rest of the scripts. | ||
6 | |||
7 | MAX_RETRIES=600 | ||
8 | RETRY_INTERVAL=".1" # seconds | ||
9 | |||
10 | # log(msg) - write message to kernel log | ||
11 | # msg - insightful words | ||
12 | function log() { | ||
13 | echo "$1" > /dev/kmsg | ||
14 | } | ||
15 | |||
16 | # die(msg) - game over, man | ||
17 | # msg - dying words | ||
18 | function die() { | ||
19 | log "ERROR: $1" | ||
20 | echo "ERROR: $1" >&2 | ||
21 | exit 1 | ||
22 | } | ||
23 | |||
24 | # set_dynamic_debug() - setup kernel dynamic debug | ||
25 | # TODO - push and pop this config? | ||
26 | function set_dynamic_debug() { | ||
27 | cat << EOF > /sys/kernel/debug/dynamic_debug/control | ||
28 | file kernel/livepatch/* +p | ||
29 | func klp_try_switch_task -p | ||
30 | EOF | ||
31 | } | ||
32 | |||
33 | # loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES, | ||
34 | # sleep $RETRY_INTERVAL between attempts | ||
35 | # cmd - command and its arguments to run | ||
36 | function loop_until() { | ||
37 | local cmd="$*" | ||
38 | local i=0 | ||
39 | while true; do | ||
40 | eval "$cmd" && return 0 | ||
41 | [[ $((i++)) -eq $MAX_RETRIES ]] && return 1 | ||
42 | sleep $RETRY_INTERVAL | ||
43 | done | ||
44 | } | ||
45 | |||
46 | function is_livepatch_mod() { | ||
47 | local mod="$1" | ||
48 | |||
49 | if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then | ||
50 | return 0 | ||
51 | fi | ||
52 | |||
53 | return 1 | ||
54 | } | ||
55 | |||
56 | function __load_mod() { | ||
57 | local mod="$1"; shift | ||
58 | local args="$*" | ||
59 | |||
60 | local msg="% modprobe $mod $args" | ||
61 | log "${msg%% }" | ||
62 | ret=$(modprobe "$mod" "$args" 2>&1) | ||
63 | if [[ "$ret" != "" ]]; then | ||
64 | die "$ret" | ||
65 | fi | ||
66 | |||
67 | # Wait for module in sysfs ... | ||
68 | loop_until '[[ -e "/sys/module/$mod" ]]' || | ||
69 | die "failed to load module $mod" | ||
70 | } | ||
71 | |||
72 | |||
73 | # load_mod(modname, params) - load a kernel module | ||
74 | # modname - module name to load | ||
75 | # params - module parameters to pass to modprobe | ||
76 | function load_mod() { | ||
77 | local mod="$1"; shift | ||
78 | local args="$*" | ||
79 | |||
80 | is_livepatch_mod "$mod" && | ||
81 | die "use load_lp() to load the livepatch module $mod" | ||
82 | |||
83 | __load_mod "$mod" "$args" | ||
84 | } | ||
85 | |||
86 | # load_lp_nowait(modname, params) - load a kernel module with a livepatch | ||
87 | # but do not wait on until the transition finishes | ||
88 | # modname - module name to load | ||
89 | # params - module parameters to pass to modprobe | ||
90 | function load_lp_nowait() { | ||
91 | local mod="$1"; shift | ||
92 | local args="$*" | ||
93 | |||
94 | is_livepatch_mod "$mod" || | ||
95 | die "module $mod is not a livepatch" | ||
96 | |||
97 | __load_mod "$mod" "$args" | ||
98 | |||
99 | # Wait for livepatch in sysfs ... | ||
100 | loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' || | ||
101 | die "failed to load module $mod (sysfs)" | ||
102 | } | ||
103 | |||
104 | # load_lp(modname, params) - load a kernel module with a livepatch | ||
105 | # modname - module name to load | ||
106 | # params - module parameters to pass to modprobe | ||
107 | function load_lp() { | ||
108 | local mod="$1"; shift | ||
109 | local args="$*" | ||
110 | |||
111 | load_lp_nowait "$mod" "$args" | ||
112 | |||
113 | # Wait until the transition finishes ... | ||
114 | loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' || | ||
115 | die "failed to complete transition" | ||
116 | } | ||
117 | |||
118 | # load_failing_mod(modname, params) - load a kernel module, expect to fail | ||
119 | # modname - module name to load | ||
120 | # params - module parameters to pass to modprobe | ||
121 | function load_failing_mod() { | ||
122 | local mod="$1"; shift | ||
123 | local args="$*" | ||
124 | |||
125 | local msg="% modprobe $mod $args" | ||
126 | log "${msg%% }" | ||
127 | ret=$(modprobe "$mod" "$args" 2>&1) | ||
128 | if [[ "$ret" == "" ]]; then | ||
129 | die "$mod unexpectedly loaded" | ||
130 | fi | ||
131 | log "$ret" | ||
132 | } | ||
133 | |||
134 | # unload_mod(modname) - unload a kernel module | ||
135 | # modname - module name to unload | ||
136 | function unload_mod() { | ||
137 | local mod="$1" | ||
138 | |||
139 | # Wait for module reference count to clear ... | ||
140 | loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' || | ||
141 | die "failed to unload module $mod (refcnt)" | ||
142 | |||
143 | log "% rmmod $mod" | ||
144 | ret=$(rmmod "$mod" 2>&1) | ||
145 | if [[ "$ret" != "" ]]; then | ||
146 | die "$ret" | ||
147 | fi | ||
148 | |||
149 | # Wait for module in sysfs ... | ||
150 | loop_until '[[ ! -e "/sys/module/$mod" ]]' || | ||
151 | die "failed to unload module $mod (/sys/module)" | ||
152 | } | ||
153 | |||
154 | # unload_lp(modname) - unload a kernel module with a livepatch | ||
155 | # modname - module name to unload | ||
156 | function unload_lp() { | ||
157 | unload_mod "$1" | ||
158 | } | ||
159 | |||
160 | # disable_lp(modname) - disable a livepatch | ||
161 | # modname - module name to unload | ||
162 | function disable_lp() { | ||
163 | local mod="$1" | ||
164 | |||
165 | log "% echo 0 > /sys/kernel/livepatch/$mod/enabled" | ||
166 | echo 0 > /sys/kernel/livepatch/"$mod"/enabled | ||
167 | |||
168 | # Wait until the transition finishes and the livepatch gets | ||
169 | # removed from sysfs... | ||
170 | loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' || | ||
171 | die "failed to disable livepatch $mod" | ||
172 | } | ||
173 | |||
174 | # set_pre_patch_ret(modname, pre_patch_ret) | ||
175 | # modname - module name to set | ||
176 | # pre_patch_ret - new pre_patch_ret value | ||
177 | function set_pre_patch_ret { | ||
178 | local mod="$1"; shift | ||
179 | local ret="$1" | ||
180 | |||
181 | log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret" | ||
182 | echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret | ||
183 | |||
184 | # Wait for sysfs value to hold ... | ||
185 | loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' || | ||
186 | die "failed to set pre_patch_ret parameter for $mod module" | ||
187 | } | ||
188 | |||
189 | # check_result() - verify dmesg output | ||
190 | # TODO - better filter, out of order msgs, etc? | ||
191 | function check_result { | ||
192 | local expect="$*" | ||
193 | local result | ||
194 | |||
195 | result=$(dmesg | grep -v 'tainting' | grep -e 'livepatch:' -e 'test_klp' | sed 's/^\[[ 0-9.]*\] //') | ||
196 | |||
197 | if [[ "$expect" == "$result" ]] ; then | ||
198 | echo "ok" | ||
199 | else | ||
200 | echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n" | ||
201 | die "livepatch kselftest(s) failed" | ||
202 | fi | ||
203 | } | ||
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh new file mode 100755 index 000000000000..e97a9dcb73c7 --- /dev/null +++ b/tools/testing/selftests/livepatch/test-callbacks.sh | |||
@@ -0,0 +1,587 @@ | |||
1 | #!/bin/bash | ||
2 | # SPDX-License-Identifier: GPL-2.0 | ||
3 | # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
4 | |||
5 | . $(dirname $0)/functions.sh | ||
6 | |||
7 | MOD_LIVEPATCH=test_klp_callbacks_demo | ||
8 | MOD_LIVEPATCH2=test_klp_callbacks_demo2 | ||
9 | MOD_TARGET=test_klp_callbacks_mod | ||
10 | MOD_TARGET_BUSY=test_klp_callbacks_busy | ||
11 | |||
12 | set_dynamic_debug | ||
13 | |||
14 | |||
15 | # TEST: target module before livepatch | ||
16 | # | ||
17 | # Test a combination of loading a kernel module and a livepatch that | ||
18 | # patches a function in the first module. Load the target module | ||
19 | # before the livepatch module. Unload them in the same order. | ||
20 | # | ||
21 | # - On livepatch enable, before the livepatch transition starts, | ||
22 | # pre-patch callbacks are executed for vmlinux and $MOD_TARGET (those | ||
23 | # klp_objects currently loaded). After klp_objects are patched | ||
24 | # according to the klp_patch, their post-patch callbacks run and the | ||
25 | # transition completes. | ||
26 | # | ||
27 | # - Similarly, on livepatch disable, pre-patch callbacks run before the | ||
28 | # unpatching transition starts. klp_objects are reverted, post-patch | ||
29 | # callbacks execute and the transition completes. | ||
30 | |||
31 | echo -n "TEST: target module before livepatch ... " | ||
32 | dmesg -C | ||
33 | |||
34 | load_mod $MOD_TARGET | ||
35 | load_lp $MOD_LIVEPATCH | ||
36 | disable_lp $MOD_LIVEPATCH | ||
37 | unload_lp $MOD_LIVEPATCH | ||
38 | unload_mod $MOD_TARGET | ||
39 | |||
40 | check_result "% modprobe $MOD_TARGET | ||
41 | $MOD_TARGET: ${MOD_TARGET}_init | ||
42 | % modprobe $MOD_LIVEPATCH | ||
43 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
44 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
45 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
46 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
47 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
48 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
49 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
50 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
51 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
52 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
53 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
54 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
55 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
56 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
57 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
58 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
59 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
60 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
61 | % rmmod $MOD_LIVEPATCH | ||
62 | % rmmod $MOD_TARGET | ||
63 | $MOD_TARGET: ${MOD_TARGET}_exit" | ||
64 | |||
65 | |||
66 | # TEST: module_coming notifier | ||
67 | # | ||
68 | # This test is similar to the previous test, but (un)load the livepatch | ||
69 | # module before the target kernel module. This tests the livepatch | ||
70 | # core's module_coming handler. | ||
71 | # | ||
72 | # - On livepatch enable, only pre/post-patch callbacks are executed for | ||
73 | # currently loaded klp_objects, in this case, vmlinux. | ||
74 | # | ||
75 | # - When a targeted module is subsequently loaded, only its | ||
76 | # pre/post-patch callbacks are executed. | ||
77 | # | ||
78 | # - On livepatch disable, all currently loaded klp_objects' (vmlinux and | ||
79 | # $MOD_TARGET) pre/post-unpatch callbacks are executed. | ||
80 | |||
81 | echo -n "TEST: module_coming notifier ... " | ||
82 | dmesg -C | ||
83 | |||
84 | load_lp $MOD_LIVEPATCH | ||
85 | load_mod $MOD_TARGET | ||
86 | disable_lp $MOD_LIVEPATCH | ||
87 | unload_lp $MOD_LIVEPATCH | ||
88 | unload_mod $MOD_TARGET | ||
89 | |||
90 | check_result "% modprobe $MOD_LIVEPATCH | ||
91 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
92 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
93 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
94 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
95 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
96 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
97 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
98 | % modprobe $MOD_TARGET | ||
99 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
100 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
101 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
102 | $MOD_TARGET: ${MOD_TARGET}_init | ||
103 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
104 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
105 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
106 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
107 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
108 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
109 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
110 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
111 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
112 | % rmmod $MOD_LIVEPATCH | ||
113 | % rmmod $MOD_TARGET | ||
114 | $MOD_TARGET: ${MOD_TARGET}_exit" | ||
115 | |||
116 | |||
117 | # TEST: module_going notifier | ||
118 | # | ||
119 | # Test loading the livepatch after a targeted kernel module, then unload | ||
120 | # the kernel module before disabling the livepatch. This tests the | ||
121 | # livepatch core's module_going handler. | ||
122 | # | ||
123 | # - First load a target module, then the livepatch. | ||
124 | # | ||
125 | # - When a target module is unloaded, the livepatch is only reverted | ||
126 | # from that klp_object ($MOD_TARGET). As such, only its pre and | ||
127 | # post-unpatch callbacks are executed when this occurs. | ||
128 | # | ||
129 | # - When the livepatch is disabled, pre and post-unpatch callbacks are | ||
130 | # run for the remaining klp_object, vmlinux. | ||
131 | |||
132 | echo -n "TEST: module_going notifier ... " | ||
133 | dmesg -C | ||
134 | |||
135 | load_mod $MOD_TARGET | ||
136 | load_lp $MOD_LIVEPATCH | ||
137 | unload_mod $MOD_TARGET | ||
138 | disable_lp $MOD_LIVEPATCH | ||
139 | unload_lp $MOD_LIVEPATCH | ||
140 | |||
141 | check_result "% modprobe $MOD_TARGET | ||
142 | $MOD_TARGET: ${MOD_TARGET}_init | ||
143 | % modprobe $MOD_LIVEPATCH | ||
144 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
145 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
146 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
147 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
148 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
149 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
150 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
151 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
152 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
153 | % rmmod $MOD_TARGET | ||
154 | $MOD_TARGET: ${MOD_TARGET}_exit | ||
155 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
156 | livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET' | ||
157 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
158 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
159 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
160 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
161 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
162 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
163 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
164 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
165 | % rmmod $MOD_LIVEPATCH" | ||
166 | |||
167 | |||
168 | # TEST: module_coming and module_going notifiers | ||
169 | # | ||
170 | # This test is similar to the previous test, however the livepatch is | ||
171 | # loaded first. This tests the livepatch core's module_coming and | ||
172 | # module_going handlers. | ||
173 | # | ||
174 | # - First load the livepatch. | ||
175 | # | ||
176 | # - When a targeted kernel module is subsequently loaded, only its | ||
177 | # pre/post-patch callbacks are executed. | ||
178 | # | ||
179 | # - When the target module is unloaded, the livepatch is only reverted | ||
180 | # from the $MOD_TARGET klp_object. As such, only pre and | ||
181 | # post-unpatch callbacks are executed when this occurs. | ||
182 | |||
183 | echo -n "TEST: module_coming and module_going notifiers ... " | ||
184 | dmesg -C | ||
185 | |||
186 | load_lp $MOD_LIVEPATCH | ||
187 | load_mod $MOD_TARGET | ||
188 | unload_mod $MOD_TARGET | ||
189 | disable_lp $MOD_LIVEPATCH | ||
190 | unload_lp $MOD_LIVEPATCH | ||
191 | |||
192 | check_result "% modprobe $MOD_LIVEPATCH | ||
193 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
194 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
195 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
196 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
197 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
198 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
199 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
200 | % modprobe $MOD_TARGET | ||
201 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
202 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
203 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
204 | $MOD_TARGET: ${MOD_TARGET}_init | ||
205 | % rmmod $MOD_TARGET | ||
206 | $MOD_TARGET: ${MOD_TARGET}_exit | ||
207 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
208 | livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET' | ||
209 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
210 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
211 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
212 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
213 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
214 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
215 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
216 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
217 | % rmmod $MOD_LIVEPATCH" | ||
218 | |||
219 | |||
220 | # TEST: target module not present | ||
221 | # | ||
222 | # A simple test of loading a livepatch without one of its patch target | ||
223 | # klp_objects ever loaded ($MOD_TARGET). | ||
224 | # | ||
225 | # - Load the livepatch. | ||
226 | # | ||
227 | # - As expected, only pre/post-(un)patch handlers are executed for | ||
228 | # vmlinux. | ||
229 | |||
230 | echo -n "TEST: target module not present ... " | ||
231 | dmesg -C | ||
232 | |||
233 | load_lp $MOD_LIVEPATCH | ||
234 | disable_lp $MOD_LIVEPATCH | ||
235 | unload_lp $MOD_LIVEPATCH | ||
236 | |||
237 | check_result "% modprobe $MOD_LIVEPATCH | ||
238 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
239 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
240 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
241 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
242 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
243 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
244 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
245 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
246 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
247 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
248 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
249 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
250 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
251 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
252 | % rmmod $MOD_LIVEPATCH" | ||
253 | |||
254 | |||
255 | # TEST: pre-patch callback -ENODEV | ||
256 | # | ||
257 | # Test a scenario where a vmlinux pre-patch callback returns a non-zero | ||
258 | # status (ie, failure). | ||
259 | # | ||
260 | # - First load a target module. | ||
261 | # | ||
262 | # - Load the livepatch module, setting its 'pre_patch_ret' value to -19 | ||
263 | # (-ENODEV). When its vmlinux pre-patch callback executes, this | ||
264 | # status code will propagate back to the module-loading subsystem. | ||
265 | # The result is that the insmod command refuses to load the livepatch | ||
266 | # module. | ||
267 | |||
268 | echo -n "TEST: pre-patch callback -ENODEV ... " | ||
269 | dmesg -C | ||
270 | |||
271 | load_mod $MOD_TARGET | ||
272 | load_failing_mod $MOD_LIVEPATCH pre_patch_ret=-19 | ||
273 | unload_mod $MOD_TARGET | ||
274 | |||
275 | check_result "% modprobe $MOD_TARGET | ||
276 | $MOD_TARGET: ${MOD_TARGET}_init | ||
277 | % modprobe $MOD_LIVEPATCH pre_patch_ret=-19 | ||
278 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
279 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
280 | test_klp_callbacks_demo: pre_patch_callback: vmlinux | ||
281 | livepatch: pre-patch callback failed for object 'vmlinux' | ||
282 | livepatch: failed to enable patch '$MOD_LIVEPATCH' | ||
283 | livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch | ||
284 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
285 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
286 | modprobe: ERROR: could not insert '$MOD_LIVEPATCH': No such device | ||
287 | % rmmod $MOD_TARGET | ||
288 | $MOD_TARGET: ${MOD_TARGET}_exit" | ||
289 | |||
290 | |||
291 | # TEST: module_coming + pre-patch callback -ENODEV | ||
292 | # | ||
293 | # Similar to the previous test, setup a livepatch such that its vmlinux | ||
294 | # pre-patch callback returns success. However, when a targeted kernel | ||
295 | # module is later loaded, have the livepatch return a failing status | ||
296 | # code. | ||
297 | # | ||
298 | # - Load the livepatch, vmlinux pre-patch callback succeeds. | ||
299 | # | ||
300 | # - Set a trap so subsequent pre-patch callbacks to this livepatch will | ||
301 | # return -ENODEV. | ||
302 | # | ||
303 | # - The livepatch pre-patch callback for subsequently loaded target | ||
304 | # modules will return failure, so the module loader refuses to load | ||
305 | # the kernel module. No post-patch or pre/post-unpatch callbacks are | ||
306 | # executed for this klp_object. | ||
307 | # | ||
308 | # - Pre/post-unpatch callbacks are run for the vmlinux klp_object. | ||
309 | |||
310 | echo -n "TEST: module_coming + pre-patch callback -ENODEV ... " | ||
311 | dmesg -C | ||
312 | |||
313 | load_lp $MOD_LIVEPATCH | ||
314 | set_pre_patch_ret $MOD_LIVEPATCH -19 | ||
315 | load_failing_mod $MOD_TARGET | ||
316 | disable_lp $MOD_LIVEPATCH | ||
317 | unload_lp $MOD_LIVEPATCH | ||
318 | |||
319 | check_result "% modprobe $MOD_LIVEPATCH | ||
320 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
321 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
322 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
323 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
324 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
325 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
326 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
327 | % echo -19 > /sys/module/$MOD_LIVEPATCH/parameters/pre_patch_ret | ||
328 | % modprobe $MOD_TARGET | ||
329 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
330 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
331 | livepatch: pre-patch callback failed for object '$MOD_TARGET' | ||
332 | livepatch: patch '$MOD_LIVEPATCH' failed for module '$MOD_TARGET', refusing to load module '$MOD_TARGET' | ||
333 | modprobe: ERROR: could not insert '$MOD_TARGET': No such device | ||
334 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
335 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
336 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
337 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
338 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
339 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
340 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
341 | % rmmod $MOD_LIVEPATCH" | ||
342 | |||
343 | |||
344 | # TEST: multiple target modules | ||
345 | # | ||
346 | # Test loading multiple targeted kernel modules. This test-case is | ||
347 | # mainly for comparing with the next test-case. | ||
348 | # | ||
349 | # - Load a target "busy" kernel module which kicks off a worker function | ||
350 | # that immediately exits. | ||
351 | # | ||
352 | # - Proceed with loading the livepatch and another ordinary target | ||
353 | # module. Post-patch callbacks are executed and the transition | ||
354 | # completes quickly. | ||
355 | |||
356 | echo -n "TEST: multiple target modules ... " | ||
357 | dmesg -C | ||
358 | |||
359 | load_mod $MOD_TARGET_BUSY sleep_secs=0 | ||
360 | # give $MOD_TARGET_BUSY::busymod_work_func() a chance to run | ||
361 | sleep 5 | ||
362 | load_lp $MOD_LIVEPATCH | ||
363 | load_mod $MOD_TARGET | ||
364 | unload_mod $MOD_TARGET | ||
365 | disable_lp $MOD_LIVEPATCH | ||
366 | unload_lp $MOD_LIVEPATCH | ||
367 | unload_mod $MOD_TARGET_BUSY | ||
368 | |||
369 | check_result "% modprobe $MOD_TARGET_BUSY sleep_secs=0 | ||
370 | $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init | ||
371 | $MOD_TARGET_BUSY: busymod_work_func, sleeping 0 seconds ... | ||
372 | $MOD_TARGET_BUSY: busymod_work_func exit | ||
373 | % modprobe $MOD_LIVEPATCH | ||
374 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
375 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
376 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
377 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
378 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
379 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
380 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
381 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
382 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
383 | % modprobe $MOD_TARGET | ||
384 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
385 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
386 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
387 | $MOD_TARGET: ${MOD_TARGET}_init | ||
388 | % rmmod $MOD_TARGET | ||
389 | $MOD_TARGET: ${MOD_TARGET}_exit | ||
390 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
391 | livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET' | ||
392 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
393 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
394 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
395 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
396 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
397 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
398 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
399 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
400 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
401 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
402 | % rmmod $MOD_LIVEPATCH | ||
403 | % rmmod $MOD_TARGET_BUSY | ||
404 | $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit" | ||
405 | |||
406 | |||
407 | |||
408 | # TEST: busy target module | ||
409 | # | ||
410 | # A similar test as the previous one, but force the "busy" kernel module | ||
411 | # to do longer work. | ||
412 | # | ||
413 | # The livepatching core will refuse to patch a task that is currently | ||
414 | # executing a to-be-patched function -- the consistency model stalls the | ||
415 | # current patch transition until this safety-check is met. Test a | ||
416 | # scenario where one of a livepatch's target klp_objects sits on such a | ||
417 | # function for a long time. Meanwhile, load and unload other target | ||
418 | # kernel modules while the livepatch transition is in progress. | ||
419 | # | ||
420 | # - Load the "busy" kernel module, this time make it do 10 seconds worth | ||
421 | # of work. | ||
422 | # | ||
423 | # - Meanwhile, the livepatch is loaded. Notice that the patch | ||
424 | # transition does not complete as the targeted "busy" module is | ||
425 | # sitting on a to-be-patched function. | ||
426 | # | ||
427 | # - Load a second target module (this one is an ordinary idle kernel | ||
428 | # module). Note that *no* post-patch callbacks will be executed while | ||
429 | # the livepatch is still in transition. | ||
430 | # | ||
431 | # - Request an unload of the simple kernel module. The patch is still | ||
432 | # transitioning, so its pre-unpatch callbacks are skipped. | ||
433 | # | ||
434 | # - Finally the livepatch is disabled. Since none of the patch's | ||
435 | # klp_object's post-patch callbacks executed, the remaining | ||
436 | # klp_object's pre-unpatch callbacks are skipped. | ||
437 | |||
438 | echo -n "TEST: busy target module ... " | ||
439 | dmesg -C | ||
440 | |||
441 | load_mod $MOD_TARGET_BUSY sleep_secs=10 | ||
442 | load_lp_nowait $MOD_LIVEPATCH | ||
443 | # Don't wait for transition, load $MOD_TARGET while the transition | ||
444 | # is still stalled in $MOD_TARGET_BUSY::busymod_work_func() | ||
445 | sleep 5 | ||
446 | load_mod $MOD_TARGET | ||
447 | unload_mod $MOD_TARGET | ||
448 | disable_lp $MOD_LIVEPATCH | ||
449 | unload_lp $MOD_LIVEPATCH | ||
450 | unload_mod $MOD_TARGET_BUSY | ||
451 | |||
452 | check_result "% modprobe $MOD_TARGET_BUSY sleep_secs=10 | ||
453 | $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init | ||
454 | $MOD_TARGET_BUSY: busymod_work_func, sleeping 10 seconds ... | ||
455 | % modprobe $MOD_LIVEPATCH | ||
456 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
457 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
458 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
459 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
460 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
461 | % modprobe $MOD_TARGET | ||
462 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
463 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
464 | $MOD_TARGET: ${MOD_TARGET}_init | ||
465 | % rmmod $MOD_TARGET | ||
466 | $MOD_TARGET: ${MOD_TARGET}_exit | ||
467 | livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET' | ||
468 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
469 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
470 | livepatch: '$MOD_LIVEPATCH': reversing transition from patching to unpatching | ||
471 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
472 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
473 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
474 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
475 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
476 | % rmmod $MOD_LIVEPATCH | ||
477 | % rmmod $MOD_TARGET_BUSY | ||
478 | $MOD_TARGET_BUSY: busymod_work_func exit | ||
479 | $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit" | ||
480 | |||
481 | |||
482 | # TEST: multiple livepatches | ||
483 | # | ||
484 | # Test loading multiple livepatches. This test-case is mainly for comparing | ||
485 | # with the next test-case. | ||
486 | # | ||
487 | # - Load and unload two livepatches, pre and post (un)patch callbacks | ||
488 | # execute as each patch progresses through its (un)patching | ||
489 | # transition. | ||
490 | |||
491 | echo -n "TEST: multiple livepatches ... " | ||
492 | dmesg -C | ||
493 | |||
494 | load_lp $MOD_LIVEPATCH | ||
495 | load_lp $MOD_LIVEPATCH2 | ||
496 | disable_lp $MOD_LIVEPATCH2 | ||
497 | disable_lp $MOD_LIVEPATCH | ||
498 | unload_lp $MOD_LIVEPATCH2 | ||
499 | unload_lp $MOD_LIVEPATCH | ||
500 | |||
501 | check_result "% modprobe $MOD_LIVEPATCH | ||
502 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
503 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
504 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
505 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
506 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
507 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
508 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
509 | % modprobe $MOD_LIVEPATCH2 | ||
510 | livepatch: enabling patch '$MOD_LIVEPATCH2' | ||
511 | livepatch: '$MOD_LIVEPATCH2': initializing patching transition | ||
512 | $MOD_LIVEPATCH2: pre_patch_callback: vmlinux | ||
513 | livepatch: '$MOD_LIVEPATCH2': starting patching transition | ||
514 | livepatch: '$MOD_LIVEPATCH2': completing patching transition | ||
515 | $MOD_LIVEPATCH2: post_patch_callback: vmlinux | ||
516 | livepatch: '$MOD_LIVEPATCH2': patching complete | ||
517 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled | ||
518 | livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition | ||
519 | $MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux | ||
520 | livepatch: '$MOD_LIVEPATCH2': starting unpatching transition | ||
521 | livepatch: '$MOD_LIVEPATCH2': completing unpatching transition | ||
522 | $MOD_LIVEPATCH2: post_unpatch_callback: vmlinux | ||
523 | livepatch: '$MOD_LIVEPATCH2': unpatching complete | ||
524 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
525 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
526 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
527 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
528 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
529 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
530 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
531 | % rmmod $MOD_LIVEPATCH2 | ||
532 | % rmmod $MOD_LIVEPATCH" | ||
533 | |||
534 | |||
535 | # TEST: atomic replace | ||
536 | # | ||
537 | # Load multiple livepatches, but the second as an 'atomic-replace' | ||
538 | # patch. When the latter loads, the original livepatch should be | ||
539 | # disabled and *none* of its pre/post-unpatch callbacks executed. On | ||
540 | # the other hand, when the atomic-replace livepatch is disabled, its | ||
541 | # pre/post-unpatch callbacks *should* be executed. | ||
542 | # | ||
543 | # - Load and unload two livepatches, the second of which has its | ||
544 | # .replace flag set true. | ||
545 | # | ||
546 | # - Pre and post patch callbacks are executed for both livepatches. | ||
547 | # | ||
548 | # - Once the atomic replace module is loaded, only its pre and post | ||
549 | # unpatch callbacks are executed. | ||
550 | |||
551 | echo -n "TEST: atomic replace ... " | ||
552 | dmesg -C | ||
553 | |||
554 | load_lp $MOD_LIVEPATCH | ||
555 | load_lp $MOD_LIVEPATCH2 replace=1 | ||
556 | disable_lp $MOD_LIVEPATCH2 | ||
557 | unload_lp $MOD_LIVEPATCH2 | ||
558 | unload_lp $MOD_LIVEPATCH | ||
559 | |||
560 | check_result "% modprobe $MOD_LIVEPATCH | ||
561 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
562 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
563 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
564 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
565 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
566 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
567 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
568 | % modprobe $MOD_LIVEPATCH2 replace=1 | ||
569 | livepatch: enabling patch '$MOD_LIVEPATCH2' | ||
570 | livepatch: '$MOD_LIVEPATCH2': initializing patching transition | ||
571 | $MOD_LIVEPATCH2: pre_patch_callback: vmlinux | ||
572 | livepatch: '$MOD_LIVEPATCH2': starting patching transition | ||
573 | livepatch: '$MOD_LIVEPATCH2': completing patching transition | ||
574 | $MOD_LIVEPATCH2: post_patch_callback: vmlinux | ||
575 | livepatch: '$MOD_LIVEPATCH2': patching complete | ||
576 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled | ||
577 | livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition | ||
578 | $MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux | ||
579 | livepatch: '$MOD_LIVEPATCH2': starting unpatching transition | ||
580 | livepatch: '$MOD_LIVEPATCH2': completing unpatching transition | ||
581 | $MOD_LIVEPATCH2: post_unpatch_callback: vmlinux | ||
582 | livepatch: '$MOD_LIVEPATCH2': unpatching complete | ||
583 | % rmmod $MOD_LIVEPATCH2 | ||
584 | % rmmod $MOD_LIVEPATCH" | ||
585 | |||
586 | |||
587 | exit 0 | ||
diff --git a/tools/testing/selftests/livepatch/test-livepatch.sh b/tools/testing/selftests/livepatch/test-livepatch.sh new file mode 100755 index 000000000000..f05268aea859 --- /dev/null +++ b/tools/testing/selftests/livepatch/test-livepatch.sh | |||
@@ -0,0 +1,168 @@ | |||
1 | #!/bin/bash | ||
2 | # SPDX-License-Identifier: GPL-2.0 | ||
3 | # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
4 | |||
5 | . $(dirname $0)/functions.sh | ||
6 | |||
7 | MOD_LIVEPATCH=test_klp_livepatch | ||
8 | MOD_REPLACE=test_klp_atomic_replace | ||
9 | |||
10 | set_dynamic_debug | ||
11 | |||
12 | |||
13 | # TEST: basic function patching | ||
14 | # - load a livepatch that modifies the output from /proc/cmdline and | ||
15 | # verify correct behavior | ||
16 | # - unload the livepatch and make sure the patch was removed | ||
17 | |||
18 | echo -n "TEST: basic function patching ... " | ||
19 | dmesg -C | ||
20 | |||
21 | load_lp $MOD_LIVEPATCH | ||
22 | |||
23 | if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then | ||
24 | echo -e "FAIL\n\n" | ||
25 | die "livepatch kselftest(s) failed" | ||
26 | fi | ||
27 | |||
28 | disable_lp $MOD_LIVEPATCH | ||
29 | unload_lp $MOD_LIVEPATCH | ||
30 | |||
31 | if [[ "$(cat /proc/cmdline)" == "$MOD_LIVEPATCH: this has been live patched" ]] ; then | ||
32 | echo -e "FAIL\n\n" | ||
33 | die "livepatch kselftest(s) failed" | ||
34 | fi | ||
35 | |||
36 | check_result "% modprobe $MOD_LIVEPATCH | ||
37 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
38 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
39 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
40 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
41 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
42 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
43 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
44 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
45 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
46 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
47 | % rmmod $MOD_LIVEPATCH" | ||
48 | |||
49 | |||
50 | # TEST: multiple livepatches | ||
51 | # - load a livepatch that modifies the output from /proc/cmdline and | ||
52 | # verify correct behavior | ||
53 | # - load another livepatch and verify that both livepatches are active | ||
54 | # - unload the second livepatch and verify that the first is still active | ||
55 | # - unload the first livepatch and verify none are active | ||
56 | |||
57 | echo -n "TEST: multiple livepatches ... " | ||
58 | dmesg -C | ||
59 | |||
60 | load_lp $MOD_LIVEPATCH | ||
61 | |||
62 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
63 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
64 | |||
65 | load_lp $MOD_REPLACE replace=0 | ||
66 | |||
67 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
68 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
69 | |||
70 | disable_lp $MOD_REPLACE | ||
71 | unload_lp $MOD_REPLACE | ||
72 | |||
73 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
74 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
75 | |||
76 | disable_lp $MOD_LIVEPATCH | ||
77 | unload_lp $MOD_LIVEPATCH | ||
78 | |||
79 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
80 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
81 | |||
82 | check_result "% modprobe $MOD_LIVEPATCH | ||
83 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
84 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
85 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
86 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
87 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
88 | $MOD_LIVEPATCH: this has been live patched | ||
89 | % modprobe $MOD_REPLACE replace=0 | ||
90 | livepatch: enabling patch '$MOD_REPLACE' | ||
91 | livepatch: '$MOD_REPLACE': initializing patching transition | ||
92 | livepatch: '$MOD_REPLACE': starting patching transition | ||
93 | livepatch: '$MOD_REPLACE': completing patching transition | ||
94 | livepatch: '$MOD_REPLACE': patching complete | ||
95 | $MOD_LIVEPATCH: this has been live patched | ||
96 | $MOD_REPLACE: this has been live patched | ||
97 | % echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled | ||
98 | livepatch: '$MOD_REPLACE': initializing unpatching transition | ||
99 | livepatch: '$MOD_REPLACE': starting unpatching transition | ||
100 | livepatch: '$MOD_REPLACE': completing unpatching transition | ||
101 | livepatch: '$MOD_REPLACE': unpatching complete | ||
102 | % rmmod $MOD_REPLACE | ||
103 | $MOD_LIVEPATCH: this has been live patched | ||
104 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
105 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
106 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
107 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
108 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
109 | % rmmod $MOD_LIVEPATCH" | ||
110 | |||
111 | |||
112 | # TEST: atomic replace livepatch | ||
113 | # - load a livepatch that modifies the output from /proc/cmdline and | ||
114 | # verify correct behavior | ||
115 | # - load an atomic replace livepatch and verify that only the second is active | ||
116 | # - remove the first livepatch and verify that the atomic replace livepatch | ||
117 | # is still active | ||
118 | # - remove the atomic replace livepatch and verify that none are active | ||
119 | |||
120 | echo -n "TEST: atomic replace livepatch ... " | ||
121 | dmesg -C | ||
122 | |||
123 | load_lp $MOD_LIVEPATCH | ||
124 | |||
125 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
126 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
127 | |||
128 | load_lp $MOD_REPLACE replace=1 | ||
129 | |||
130 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
131 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
132 | |||
133 | unload_lp $MOD_LIVEPATCH | ||
134 | |||
135 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
136 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
137 | |||
138 | disable_lp $MOD_REPLACE | ||
139 | unload_lp $MOD_REPLACE | ||
140 | |||
141 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
142 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
143 | |||
144 | check_result "% modprobe $MOD_LIVEPATCH | ||
145 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
146 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
147 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
148 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
149 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
150 | $MOD_LIVEPATCH: this has been live patched | ||
151 | % modprobe $MOD_REPLACE replace=1 | ||
152 | livepatch: enabling patch '$MOD_REPLACE' | ||
153 | livepatch: '$MOD_REPLACE': initializing patching transition | ||
154 | livepatch: '$MOD_REPLACE': starting patching transition | ||
155 | livepatch: '$MOD_REPLACE': completing patching transition | ||
156 | livepatch: '$MOD_REPLACE': patching complete | ||
157 | $MOD_REPLACE: this has been live patched | ||
158 | % rmmod $MOD_LIVEPATCH | ||
159 | $MOD_REPLACE: this has been live patched | ||
160 | % echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled | ||
161 | livepatch: '$MOD_REPLACE': initializing unpatching transition | ||
162 | livepatch: '$MOD_REPLACE': starting unpatching transition | ||
163 | livepatch: '$MOD_REPLACE': completing unpatching transition | ||
164 | livepatch: '$MOD_REPLACE': unpatching complete | ||
165 | % rmmod $MOD_REPLACE" | ||
166 | |||
167 | |||
168 | exit 0 | ||
diff --git a/tools/testing/selftests/livepatch/test-shadow-vars.sh b/tools/testing/selftests/livepatch/test-shadow-vars.sh new file mode 100755 index 000000000000..04a37831e204 --- /dev/null +++ b/tools/testing/selftests/livepatch/test-shadow-vars.sh | |||
@@ -0,0 +1,60 @@ | |||
1 | #!/bin/bash | ||
2 | # SPDX-License-Identifier: GPL-2.0 | ||
3 | # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
4 | |||
5 | . $(dirname $0)/functions.sh | ||
6 | |||
7 | MOD_TEST=test_klp_shadow_vars | ||
8 | |||
9 | set_dynamic_debug | ||
10 | |||
11 | |||
12 | # TEST: basic shadow variable API | ||
13 | # - load a module that exercises the shadow variable API | ||
14 | |||
15 | echo -n "TEST: basic shadow variable API ... " | ||
16 | dmesg -C | ||
17 | |||
18 | load_mod $MOD_TEST | ||
19 | unload_mod $MOD_TEST | ||
20 | |||
21 | check_result "% modprobe $MOD_TEST | ||
22 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0 | ||
23 | $MOD_TEST: got expected NULL result | ||
24 | $MOD_TEST: shadow_ctor: PTR6 -> PTR1 | ||
25 | $MOD_TEST: klp_shadow_alloc(obj=PTR5, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR1 = PTR6 | ||
26 | $MOD_TEST: shadow_ctor: PTR8 -> PTR2 | ||
27 | $MOD_TEST: klp_shadow_alloc(obj=PTR9, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR2 = PTR8 | ||
28 | $MOD_TEST: shadow_ctor: PTR10 -> PTR3 | ||
29 | $MOD_TEST: klp_shadow_alloc(obj=PTR5, id=0x1235, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR3 = PTR10 | ||
30 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR6 | ||
31 | $MOD_TEST: got expected PTR6 -> PTR1 result | ||
32 | $MOD_TEST: klp_shadow_get(obj=PTR9, id=0x1234) = PTR8 | ||
33 | $MOD_TEST: got expected PTR8 -> PTR2 result | ||
34 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1235) = PTR10 | ||
35 | $MOD_TEST: got expected PTR10 -> PTR3 result | ||
36 | $MOD_TEST: shadow_ctor: PTR11 -> PTR4 | ||
37 | $MOD_TEST: klp_shadow_get_or_alloc(obj=PTR12, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR4 = PTR11 | ||
38 | $MOD_TEST: klp_shadow_get_or_alloc(obj=PTR12, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR4 = PTR11 | ||
39 | $MOD_TEST: got expected PTR11 -> PTR4 result | ||
40 | $MOD_TEST: shadow_dtor(obj=PTR5, shadow_data=PTR6) | ||
41 | $MOD_TEST: klp_shadow_free(obj=PTR5, id=0x1234, dtor=PTR13) | ||
42 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0 | ||
43 | $MOD_TEST: got expected NULL result | ||
44 | $MOD_TEST: shadow_dtor(obj=PTR9, shadow_data=PTR8) | ||
45 | $MOD_TEST: klp_shadow_free(obj=PTR9, id=0x1234, dtor=PTR13) | ||
46 | $MOD_TEST: klp_shadow_get(obj=PTR9, id=0x1234) = PTR0 | ||
47 | $MOD_TEST: got expected NULL result | ||
48 | $MOD_TEST: shadow_dtor(obj=PTR12, shadow_data=PTR11) | ||
49 | $MOD_TEST: klp_shadow_free(obj=PTR12, id=0x1234, dtor=PTR13) | ||
50 | $MOD_TEST: klp_shadow_get(obj=PTR12, id=0x1234) = PTR0 | ||
51 | $MOD_TEST: got expected NULL result | ||
52 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1235) = PTR10 | ||
53 | $MOD_TEST: got expected PTR10 -> PTR3 result | ||
54 | $MOD_TEST: shadow_dtor(obj=PTR5, shadow_data=PTR10) | ||
55 | $MOD_TEST: klp_shadow_free_all(id=0x1235, dtor=PTR13) | ||
56 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0 | ||
57 | $MOD_TEST: shadow_get() got expected NULL result | ||
58 | % rmmod test_klp_shadow_vars" | ||
59 | |||
60 | exit 0 | ||