2018-04-10 15:21:12

by Joe Lawrence

[permalink] [raw]
Subject: [PATCH v2] Add livepatch kselftests

Round two cleans up a few misc script and build items, adds a shadow
variable test, and reduces the total livepatch kselftest runtime to ~45
seconds.

The tests run on top of Petr's v11 atomic replace feature and v2 of the
shadow variable enhancement patchsets:

[PATCH 0/8] livepatch: Atomic replace feature
https://lkml.kernel.org/r/[email protected]

[PATCH v2 0/2] livepatch: Allocate and free shadow variables more safely
https://lkml.kernel.org/r/[email protected]

Questions for v3:

- Should we split off the atomic replace and shadow variable update
tests so that the this patchset could be merged before the ones
listed above?

- I didn't remove any of the sample modules. If anyone thinks any of
them should go, let me know. They serve as nice, simple examples so
I thought they should all stay.

- Module naming convention: to make the test script easier to grep
module names and filenames, I broke with livepatch convention and
used underscores instead of dashes. I didn't think it worth the
regex foo to flip back and forth in the test script.

- More tests from Libor and Nicolai would be welcome!
-- Maybe we separate quicktests (like these) from longer tests if
needed?

Here's a sample output from a successful test run:

% time make -C tools/testing/selftests TARGETS=livepatch run_tests
make: Entering directory `/root/linux/tools/testing/selftests'
make[1]: Entering directory `/root/linux/tools/testing/selftests/livepatch'
make[1]: Nothing to be done for `all'.
make[1]: Leaving directory `/root/linux/tools/testing/selftests/livepatch'
make[1]: Entering directory `/root/linux/tools/testing/selftests/livepatch'
TAP version 13
selftests: test-livepatch.sh
========================================
TEST: basic function patching ... ok
TEST: multiple livepatches ... ok
TEST: atomic replace livepatch ... ok
ok 1..1 selftests: test-livepatch.sh [PASS]
selftests: test-callbacks.sh
========================================
TEST: target module before livepatch ... ok
TEST: module_coming notifier ... ok
TEST: module_going notifier ... ok
TEST: module_coming and module_going notifiers ... ok
TEST: target module not present ... ok
TEST: pre-patch callback -ENODEV ... ok
TEST: module_coming + pre-patch callback -ENODEV ... ok
TEST: multiple target modules ... ok
TEST: busy target module ... ok
TEST: multiple livepatches ... ok
TEST: atomic replace ... ok
ok 1..2 selftests: test-callbacks.sh [PASS]
selftests: test-shadow-vars.sh
========================================
TEST: basic shadow variable API ... ok
ok 1..3 selftests: test-shadow-vars.sh [PASS]
make[1]: Leaving directory `/root/linux/tools/testing/selftests/livepatch'
make: Leaving directory `/root/linux/tools/testing/selftests'

real 0m46.166s
user 0m0.436s
sys 0m1.244s


changes from v1:
- Only add $(CC_FLAGS_FTRACE) for target modules
- Remove between test delay
- Reduce RETRY_INTERVAL to .1 sec
- Reduce test_callback_mod's busymod_work_func delay from 60 to 10 sec
- s/PASS/ok and s/FAIL/not ok for test output
- Move test descriptions from Documentation/livepatch/callbacks.txt
into tools/testing/selftests/livepatch/test-callbacks.sh
- Add a shadow variable test script and module
- Add a short tools/testing/selftests/livepatch/README
- to += [email protected]
- cc += Libor, Nicolai, Artem

change from rfc:
- SPDX-License-Identifiers
- Moved livepatch test modules into lib/livepatch
- Renamed livepatch.sh (filename suffix)
- Reduced between-test delay time
- Split off common functions.sh file
- Split into separate livepatch, callbacks, and shadow-vars scrips
- Gave the tests short descriptions instead of TEST1, TEST2, etc.

Joe Lawrence (1):
selftests/livepatch: introduce tests

Documentation/livepatch/callbacks.txt | 487 -----------------
lib/Kconfig.debug | 12 +
lib/Makefile | 2 +
lib/livepatch/Makefile | 15 +
lib/livepatch/test_klp_atomic_replace.c | 69 +++
lib/livepatch/test_klp_callbacks_busy.c | 43 ++
lib/livepatch/test_klp_callbacks_demo.c | 132 +++++
lib/livepatch/test_klp_callbacks_demo2.c | 104 ++++
lib/livepatch/test_klp_callbacks_mod.c | 24 +
lib/livepatch/test_klp_livepatch.c | 62 +++
lib/livepatch/test_klp_shadow_vars.c | 235 ++++++++
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/livepatch/Makefile | 8 +
tools/testing/selftests/livepatch/config | 1 +
tools/testing/selftests/livepatch/functions.sh | 196 +++++++
.../testing/selftests/livepatch/test-callbacks.sh | 607 +++++++++++++++++++++
.../testing/selftests/livepatch/test-livepatch.sh | 173 ++++++
.../selftests/livepatch/test-shadow-vars.sh | 60 ++
18 files changed, 1744 insertions(+), 487 deletions(-)
create mode 100644 lib/livepatch/Makefile
create mode 100644 lib/livepatch/test_klp_atomic_replace.c
create mode 100644 lib/livepatch/test_klp_callbacks_busy.c
create mode 100644 lib/livepatch/test_klp_callbacks_demo.c
create mode 100644 lib/livepatch/test_klp_callbacks_demo2.c
create mode 100644 lib/livepatch/test_klp_callbacks_mod.c
create mode 100644 lib/livepatch/test_klp_livepatch.c
create mode 100644 lib/livepatch/test_klp_shadow_vars.c
create mode 100644 tools/testing/selftests/livepatch/Makefile
create mode 100644 tools/testing/selftests/livepatch/config
create mode 100644 tools/testing/selftests/livepatch/functions.sh
create mode 100755 tools/testing/selftests/livepatch/test-callbacks.sh
create mode 100755 tools/testing/selftests/livepatch/test-livepatch.sh
create mode 100755 tools/testing/selftests/livepatch/test-shadow-vars.sh

--
1.8.3.1



2018-04-10 15:23:09

by Joe Lawrence

[permalink] [raw]
Subject: [PATCH v2] selftests/livepatch: introduce tests

Add a few livepatch modules and simple target modules that the included
regression suite can run tests against.

Signed-off-by: Joe Lawrence <[email protected]>
---
Documentation/livepatch/callbacks.txt | 487 -----------------
lib/Kconfig.debug | 12 +
lib/Makefile | 2 +
lib/livepatch/Makefile | 15 +
lib/livepatch/test_klp_atomic_replace.c | 69 +++
lib/livepatch/test_klp_callbacks_busy.c | 43 ++
lib/livepatch/test_klp_callbacks_demo.c | 132 +++++
lib/livepatch/test_klp_callbacks_demo2.c | 104 ++++
lib/livepatch/test_klp_callbacks_mod.c | 24 +
lib/livepatch/test_klp_livepatch.c | 62 +++
lib/livepatch/test_klp_shadow_vars.c | 235 ++++++++
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/livepatch/Makefile | 8 +
tools/testing/selftests/livepatch/config | 1 +
tools/testing/selftests/livepatch/functions.sh | 196 +++++++
.../testing/selftests/livepatch/test-callbacks.sh | 607 +++++++++++++++++++++
.../testing/selftests/livepatch/test-livepatch.sh | 173 ++++++
.../selftests/livepatch/test-shadow-vars.sh | 60 ++
18 files changed, 1744 insertions(+), 487 deletions(-)
create mode 100644 lib/livepatch/Makefile
create mode 100644 lib/livepatch/test_klp_atomic_replace.c
create mode 100644 lib/livepatch/test_klp_callbacks_busy.c
create mode 100644 lib/livepatch/test_klp_callbacks_demo.c
create mode 100644 lib/livepatch/test_klp_callbacks_demo2.c
create mode 100644 lib/livepatch/test_klp_callbacks_mod.c
create mode 100644 lib/livepatch/test_klp_livepatch.c
create mode 100644 lib/livepatch/test_klp_shadow_vars.c
create mode 100644 tools/testing/selftests/livepatch/Makefile
create mode 100644 tools/testing/selftests/livepatch/config
create mode 100644 tools/testing/selftests/livepatch/functions.sh
create mode 100755 tools/testing/selftests/livepatch/test-callbacks.sh
create mode 100755 tools/testing/selftests/livepatch/test-livepatch.sh
create mode 100755 tools/testing/selftests/livepatch/test-shadow-vars.sh

diff --git a/Documentation/livepatch/callbacks.txt b/Documentation/livepatch/callbacks.txt
index c9776f48e458..6ca2801a6bb9 100644
--- a/Documentation/livepatch/callbacks.txt
+++ b/Documentation/livepatch/callbacks.txt
@@ -116,490 +116,3 @@ virtnet_probe() initialized its driver's net_device features. A
pre/post-patch callback could iterate over all such devices, making a
similar change to their hw_features value. (Client functions of the
value may need to be updated accordingly.)
-
-
-Test cases
-==========
-
-What follows is not an exhaustive test suite of every possible livepatch
-pre/post-(un)patch combination, but a selection that demonstrates a few
-important concepts. Each test case uses the kernel modules located in
-the samples/livepatch/ and assumes that no livepatches are loaded at the
-beginning of the test.
-
-
-Test 1
-------
-
-Test a combination of loading a kernel module and a livepatch that
-patches a function in the first module. (Un)load the target module
-before the livepatch module:
-
-- load target module
-- load livepatch
-- disable livepatch
-- unload target module
-- unload livepatch
-
-First load a target module:
-
- % insmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 34.475708] livepatch_callbacks_mod: livepatch_callbacks_mod_init
-
-On livepatch enable, before the livepatch transition starts, pre-patch
-callbacks are executed for vmlinux and livepatch_callbacks_mod (those
-klp_objects currently loaded). After klp_objects are patched according
-to the klp_patch, their post-patch callbacks run and the transition
-completes:
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko
- [ 36.503719] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 36.504213] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 36.504238] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 36.504721] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state
- [ 36.505849] livepatch: 'livepatch_callbacks_demo': starting patching transition
- [ 37.727133] livepatch: 'livepatch_callbacks_demo': completing patching transition
- [ 37.727232] livepatch_callbacks_demo: post_patch_callback: vmlinux
- [ 37.727860] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state
- [ 37.728792] livepatch: 'livepatch_callbacks_demo': patching complete
-
-Similarly, on livepatch disable, pre-patch callbacks run before the
-unpatching transition starts. klp_objects are reverted, post-patch
-callbacks execute and the transition completes:
-
- % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- [ 38.510209] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition
- [ 38.510234] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux
- [ 38.510982] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state
- [ 38.512209] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
- [ 39.711132] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 39.711210] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
- [ 39.711779] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state
- [ 39.712735] livepatch: 'livepatch_callbacks_demo': unpatching complete
-
- % rmmod samples/livepatch/livepatch-callbacks-demo.ko
- % rmmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 42.534183] livepatch_callbacks_mod: livepatch_callbacks_mod_exit
-
-
-Test 2
-------
-
-This test is similar to the previous test, but (un)load the livepatch
-module before the target kernel module. This tests the livepatch core's
-module_coming handler:
-
-- load livepatch
-- load target module
-- disable livepatch
-- unload livepatch
-- unload target module
-
-
-On livepatch enable, only pre/post-patch callbacks are executed for
-currently loaded klp_objects, in this case, vmlinux:
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko
- [ 44.553328] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 44.553997] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 44.554049] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 44.554845] livepatch: 'livepatch_callbacks_demo': starting patching transition
- [ 45.727128] livepatch: 'livepatch_callbacks_demo': completing patching transition
- [ 45.727212] livepatch_callbacks_demo: post_patch_callback: vmlinux
- [ 45.727961] livepatch: 'livepatch_callbacks_demo': patching complete
-
-When a targeted module is subsequently loaded, only its pre/post-patch
-callbacks are executed:
-
- % insmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 46.560845] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod'
- [ 46.561988] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
- [ 46.563452] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
- [ 46.565495] livepatch_callbacks_mod: livepatch_callbacks_mod_init
-
-On livepatch disable, all currently loaded klp_objects' (vmlinux and
-livepatch_callbacks_mod) pre/post-unpatch callbacks are executed:
-
- % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- [ 48.568885] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition
- [ 48.568910] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux
- [ 48.569441] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state
- [ 48.570502] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
- [ 49.759091] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 49.759171] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
- [ 49.759742] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state
- [ 49.760690] livepatch: 'livepatch_callbacks_demo': unpatching complete
-
- % rmmod samples/livepatch/livepatch-callbacks-demo.ko
- % rmmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 52.592283] livepatch_callbacks_mod: livepatch_callbacks_mod_exit
-
-
-Test 3
-------
-
-Test loading the livepatch after a targeted kernel module, then unload
-the kernel module before disabling the livepatch. This tests the
-livepatch core's module_going handler:
-
-- load target module
-- load livepatch
-- unload target module
-- disable livepatch
-- unload livepatch
-
-First load a target module, then the livepatch:
-
- % insmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 54.607948] livepatch_callbacks_mod: livepatch_callbacks_mod_init
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko
- [ 56.613919] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 56.614411] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 56.614436] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 56.614818] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state
- [ 56.615656] livepatch: 'livepatch_callbacks_demo': starting patching transition
- [ 57.759070] livepatch: 'livepatch_callbacks_demo': completing patching transition
- [ 57.759147] livepatch_callbacks_demo: post_patch_callback: vmlinux
- [ 57.759621] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state
- [ 57.760307] livepatch: 'livepatch_callbacks_demo': patching complete
-
-When a target module is unloaded, the livepatch is only reverted from
-that klp_object (livepatch_callbacks_mod). As such, only its pre and
-post-unpatch callbacks are executed when this occurs:
-
- % rmmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 58.623409] livepatch_callbacks_mod: livepatch_callbacks_mod_exit
- [ 58.623903] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away
- [ 58.624658] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod'
- [ 58.625305] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away
-
-When the livepatch is disabled, pre and post-unpatch callbacks are run
-for the remaining klp_object, vmlinux:
-
- % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- [ 60.638420] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition
- [ 60.638444] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux
- [ 60.638996] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
- [ 61.727088] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 61.727165] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
- [ 61.727985] livepatch: 'livepatch_callbacks_demo': unpatching complete
-
- % rmmod samples/livepatch/livepatch-callbacks-demo.ko
-
-
-Test 4
-------
-
-This test is similar to the previous test, however the livepatch is
-loaded first. This tests the livepatch core's module_coming and
-module_going handlers:
-
-- load livepatch
-- load target module
-- unload target module
-- disable livepatch
-- unload livepatch
-
-First load the livepatch:
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko
- [ 64.661552] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 64.662147] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 64.662175] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 64.662850] livepatch: 'livepatch_callbacks_demo': starting patching transition
- [ 65.695056] livepatch: 'livepatch_callbacks_demo': completing patching transition
- [ 65.695147] livepatch_callbacks_demo: post_patch_callback: vmlinux
- [ 65.695561] livepatch: 'livepatch_callbacks_demo': patching complete
-
-When a targeted kernel module is subsequently loaded, only its
-pre/post-patch callbacks are executed:
-
- % insmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 66.669196] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod'
- [ 66.669882] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
- [ 66.670744] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
- [ 66.672873] livepatch_callbacks_mod: livepatch_callbacks_mod_init
-
-When the target module is unloaded, the livepatch is only reverted from
-the livepatch_callbacks_mod klp_object. As such, only pre and
-post-unpatch callbacks are executed when this occurs:
-
- % rmmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 68.680065] livepatch_callbacks_mod: livepatch_callbacks_mod_exit
- [ 68.680688] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away
- [ 68.681452] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod'
- [ 68.682094] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away
-
- % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- [ 70.689225] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition
- [ 70.689256] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux
- [ 70.689882] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
- [ 71.711080] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 71.711481] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
- [ 71.711988] livepatch: 'livepatch_callbacks_demo': unpatching complete
-
- % rmmod samples/livepatch/livepatch-callbacks-demo.ko
-
-
-Test 5
-------
-
-A simple test of loading a livepatch without one of its patch target
-klp_objects ever loaded (livepatch_callbacks_mod):
-
-- load livepatch
-- disable livepatch
-- unload livepatch
-
-Load the livepatch:
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko
- [ 74.711081] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 74.711595] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 74.711639] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 74.712272] livepatch: 'livepatch_callbacks_demo': starting patching transition
- [ 75.743137] livepatch: 'livepatch_callbacks_demo': completing patching transition
- [ 75.743219] livepatch_callbacks_demo: post_patch_callback: vmlinux
- [ 75.743867] livepatch: 'livepatch_callbacks_demo': patching complete
-
-As expected, only pre/post-(un)patch handlers are executed for vmlinux:
-
- % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- [ 76.716254] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition
- [ 76.716278] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux
- [ 76.716666] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
- [ 77.727089] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 77.727194] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
- [ 77.727907] livepatch: 'livepatch_callbacks_demo': unpatching complete
-
- % rmmod samples/livepatch/livepatch-callbacks-demo.ko
-
-
-Test 6
-------
-
-Test a scenario where a vmlinux pre-patch callback returns a non-zero
-status (ie, failure):
-
-- load target module
-- load livepatch -ENODEV
-- unload target module
-
-First load a target module:
-
- % insmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 80.740520] livepatch_callbacks_mod: livepatch_callbacks_mod_init
-
-Load the livepatch module, setting its 'pre_patch_ret' value to -19
-(-ENODEV). When its vmlinux pre-patch callback executed, this status
-code will propagate back to the module-loading subsystem. The result is
-that the insmod command refuses to load the livepatch module:
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko pre_patch_ret=-19
- [ 82.747326] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 82.747743] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 82.747767] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 82.748237] livepatch: pre-patch callback failed for object 'vmlinux'
- [ 82.748637] livepatch: failed to enable patch 'livepatch_callbacks_demo'
- [ 82.749059] livepatch: 'livepatch_callbacks_demo': canceling transition, going to unpatch
- [ 82.749060] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 82.749868] livepatch: 'livepatch_callbacks_demo': unpatching complete
- [ 82.765809] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-demo.ko: No such device
-
- % rmmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 84.774238] livepatch_callbacks_mod: livepatch_callbacks_mod_exit
-
-
-Test 7
-------
-
-Similar to the previous test, setup a livepatch such that its vmlinux
-pre-patch callback returns success. However, when a targeted kernel
-module is later loaded, have the livepatch return a failing status code:
-
-- load livepatch
-- setup -ENODEV
-- load target module
-- disable livepatch
-- unload livepatch
-
-Load the livepatch, notice vmlinux pre-patch callback succeeds:
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko
- [ 86.787845] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 86.788325] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 86.788427] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 86.788821] livepatch: 'livepatch_callbacks_demo': starting patching transition
- [ 87.711069] livepatch: 'livepatch_callbacks_demo': completing patching transition
- [ 87.711143] livepatch_callbacks_demo: post_patch_callback: vmlinux
- [ 87.711886] livepatch: 'livepatch_callbacks_demo': patching complete
-
-Set a trap so subsequent pre-patch callbacks to this livepatch will
-return -ENODEV:
-
- % echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret
-
-The livepatch pre-patch callback for subsequently loaded target modules
-will return failure, so the module loader refuses to load the kernel
-module. Notice that no post-patch or pre/post-unpatch callbacks are
-executed for this klp_object:
-
- % insmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 90.796976] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod'
- [ 90.797834] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
- [ 90.798900] livepatch: pre-patch callback failed for object 'livepatch_callbacks_mod'
- [ 90.799652] livepatch: patch 'livepatch_callbacks_demo' failed for module 'livepatch_callbacks_mod', refusing to load module 'livepatch_callbacks_mod'
- [ 90.819737] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device
-
-However, pre/post-unpatch callbacks run for the vmlinux klp_object:
-
- % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- [ 92.823547] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition
- [ 92.823573] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux
- [ 92.824331] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
- [ 93.727128] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 93.727327] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
- [ 93.727861] livepatch: 'livepatch_callbacks_demo': unpatching complete
-
- % rmmod samples/livepatch/livepatch-callbacks-demo.ko
-
-
-Test 8
-------
-
-Test loading multiple targeted kernel modules. This test-case is
-mainly for comparing with the next test-case.
-
-- load busy target module (0s sleep),
-- load livepatch
-- load target module
-- unload target module
-- disable livepatch
-- unload livepatch
-- unload busy target module
-
-
-Load a target "busy" kernel module which kicks off a worker function
-that immediately exits:
-
- % insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=0
- [ 96.910107] livepatch_callbacks_busymod: livepatch_callbacks_mod_init
- [ 96.910600] livepatch_callbacks_busymod: busymod_work_func, sleeping 0 seconds ...
- [ 96.913024] livepatch_callbacks_busymod: busymod_work_func exit
-
-Proceed with loading the livepatch and another ordinary target module,
-notice that the post-patch callbacks are executed and the transition
-completes quickly:
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko
- [ 98.917892] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 98.918426] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 98.918453] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 98.918955] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state
- [ 98.923835] livepatch: 'livepatch_callbacks_demo': starting patching transition
- [ 99.743104] livepatch: 'livepatch_callbacks_demo': completing patching transition
- [ 99.743156] livepatch_callbacks_demo: post_patch_callback: vmlinux
- [ 99.743679] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state
- [ 99.744616] livepatch: 'livepatch_callbacks_demo': patching complete
-
- % insmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 100.930955] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod'
- [ 100.931668] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
- [ 100.932645] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
- [ 100.934125] livepatch_callbacks_mod: livepatch_callbacks_mod_init
-
- % rmmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 102.942805] livepatch_callbacks_mod: livepatch_callbacks_mod_exit
- [ 102.943640] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away
- [ 102.944585] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod'
- [ 102.945455] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away
-
- % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- [ 104.953815] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition
- [ 104.953838] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux
- [ 104.954431] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state
- [ 104.955426] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
- [ 106.719073] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 106.722633] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
- [ 106.723282] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state
- [ 106.724279] livepatch: 'livepatch_callbacks_demo': unpatching complete
-
- % rmmod samples/livepatch/livepatch-callbacks-demo.ko
- % rmmod samples/livepatch/livepatch-callbacks-busymod.ko
- [ 108.975660] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit
-
-
-Test 9
-------
-
-A similar test as the previous one, but force the "busy" kernel module
-to do longer work.
-
-The livepatching core will refuse to patch a task that is currently
-executing a to-be-patched function -- the consistency model stalls the
-current patch transition until this safety-check is met. Test a
-scenario where one of a livepatch's target klp_objects sits on such a
-function for a long time. Meanwhile, load and unload other target
-kernel modules while the livepatch transition is in progress.
-
-- load busy target module (30s sleep)
-- load livepatch
-- load target module
-- unload target module
-- disable livepatch
-- unload livepatch
-- unload busy target module
-
-
-Load the "busy" kernel module, this time make it do 30 seconds worth of
-work:
-
- % insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30
- [ 110.993362] livepatch_callbacks_busymod: livepatch_callbacks_mod_init
- [ 110.994059] livepatch_callbacks_busymod: busymod_work_func, sleeping 30 seconds ...
-
-Meanwhile, the livepatch is loaded. Notice that the patch transition
-does not complete as the targeted "busy" module is sitting on a
-to-be-patched function:
-
- % insmod samples/livepatch/livepatch-callbacks-demo.ko
- [ 113.000309] livepatch: enabling patch 'livepatch_callbacks_demo'
- [ 113.000764] livepatch: 'livepatch_callbacks_demo': initializing patching transition
- [ 113.000791] livepatch_callbacks_demo: pre_patch_callback: vmlinux
- [ 113.001289] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state
- [ 113.005208] livepatch: 'livepatch_callbacks_demo': starting patching transition
-
-Load a second target module (this one is an ordinary idle kernel
-module). Note that *no* post-patch callbacks will be executed while the
-livepatch is still in transition:
-
- % insmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 115.012740] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod'
- [ 115.013406] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
- [ 115.015315] livepatch_callbacks_mod: livepatch_callbacks_mod_init
-
-Request an unload of the simple kernel module. The patch is still
-transitioning, so its pre-unpatch callbacks are skipped:
-
- % rmmod samples/livepatch/livepatch-callbacks-mod.ko
- [ 117.022626] livepatch_callbacks_mod: livepatch_callbacks_mod_exit
- [ 117.023376] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod'
- [ 117.024533] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away
-
-Finally the livepatch is disabled. Since none of the patch's
-klp_object's post-patch callbacks executed, the remaining klp_object's
-pre-unpatch callbacks are skipped:
-
- % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled
- [ 119.035408] livepatch: 'livepatch_callbacks_demo': reversing transition from patching to unpatching
- [ 119.035485] livepatch: 'livepatch_callbacks_demo': starting unpatching transition
- [ 119.711166] livepatch: 'livepatch_callbacks_demo': completing unpatching transition
- [ 119.714179] livepatch_callbacks_demo: post_unpatch_callback: vmlinux
- [ 119.714653] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state
- [ 119.715437] livepatch: 'livepatch_callbacks_demo': unpatching complete
-
- % rmmod samples/livepatch/livepatch-callbacks-demo.ko
- % rmmod samples/livepatch/livepatch-callbacks-busymod.ko
- [ 141.279111] livepatch_callbacks_busymod: busymod_work_func exit
- [ 141.279760] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 64155e310a9f..e4a0e81542ff 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1932,6 +1932,18 @@ config TEST_DEBUG_VIRTUAL

If unsure, say N.

+config TEST_LIVEPATCH
+ tristate "Test livepatching"
+ default n
+ depends on LIVEPATCH
+ depends on m
+ help
+ Test various kernel livepatching features for correctness.
+ The tests will load test modules that will be livepatched
+ in various scenarios.
+
+ If unsure, say N.
+
endif # RUNTIME_TESTING_MENU

config MEMTEST
diff --git a/lib/Makefile b/lib/Makefile
index a90d4fcd748f..98a38441afb0 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -67,6 +67,8 @@ obj-$(CONFIG_TEST_PARMAN) += test_parman.o
obj-$(CONFIG_TEST_KMOD) += test_kmod.o
obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o

+obj-$(CONFIG_TEST_LIVEPATCH) += livepatch/
+
ifeq ($(CONFIG_DEBUG_KOBJECT),y)
CFLAGS_kobject.o += -DDEBUG
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 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for livepatch test code.
+
+obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
+ test_klp_callbacks_demo.o \
+ test_klp_callbacks_demo2.o \
+ test_klp_callbacks_busy.o \
+ test_klp_callbacks_mod.o \
+ test_klp_livepatch.o \
+ test_klp_shadow_vars.o
+
+# Target modules to be livepatched require CC_FLAGS_FTRACE
+CFLAGS_test_klp_callbacks_busy.o += $(CC_FLAGS_FTRACE)
+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..d1bcf6b0cfb3
--- /dev/null
+++ b/lib/livepatch/test_klp_atomic_replace.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+static int replace;
+module_param(replace, int, 0644);
+MODULE_PARM_DESC(replace, "replace (default=0)");
+
+#include <linux/seq_file.h>
+static int livepatch_meminfo_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%s: %s\n", THIS_MODULE->name,
+ "this has been live patched");
+ return 0;
+}
+
+static struct klp_func funcs[] = {
+ {
+ .old_name = "meminfo_proc_show",
+ .new_func = livepatch_meminfo_proc_show,
+ }, { }
+};
+
+static struct klp_object objs[] = {
+ {
+ /* name being NULL means vmlinux */
+ .funcs = funcs,
+ }, { }
+};
+
+static struct klp_patch patch = {
+ .mod = THIS_MODULE,
+ .objs = objs,
+ /* set .replace in the init function below for demo purposes */
+};
+
+static int test_klp_atomic_replace_init(void)
+{
+ int ret;
+
+ patch.replace = replace;
+
+ ret = klp_register_patch(&patch);
+ if (ret)
+ return ret;
+ ret = klp_enable_patch(&patch);
+ if (ret) {
+ WARN_ON(klp_unregister_patch(&patch));
+ return ret;
+ }
+ return 0;
+}
+
+static void test_klp_atomic_replace_exit(void)
+{
+ WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(test_klp_atomic_replace_init);
+module_exit(test_klp_atomic_replace_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Joe Lawrence <[email protected]>");
+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 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+static int sleep_secs;
+module_param(sleep_secs, int, 0644);
+MODULE_PARM_DESC(sleep_secs, "sleep_secs (default=0)");
+
+static void busymod_work_func(struct work_struct *work);
+static DECLARE_DELAYED_WORK(work, busymod_work_func);
+
+static void busymod_work_func(struct work_struct *work)
+{
+ pr_info("%s, sleeping %d seconds ...\n", __func__, sleep_secs);
+ msleep(sleep_secs * 1000);
+ pr_info("%s exit\n", __func__);
+}
+
+static int test_klp_callbacks_busy_init(void)
+{
+ pr_info("%s\n", __func__);
+ schedule_delayed_work(&work,
+ msecs_to_jiffies(1000 * 0));
+ return 0;
+}
+
+static void test_klp_callbacks_busy_exit(void)
+{
+ cancel_delayed_work_sync(&work);
+ pr_info("%s\n", __func__);
+}
+
+module_init(test_klp_callbacks_busy_init);
+module_exit(test_klp_callbacks_busy_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joe Lawrence <[email protected]>");
+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..87fc61445c5f
--- /dev/null
+++ b/lib/livepatch/test_klp_callbacks_demo.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+static int pre_patch_ret;
+module_param(pre_patch_ret, int, 0644);
+MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)");
+
+static const char *const module_state[] = {
+ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
+ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",
+ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",
+ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
+};
+
+static void callback_info(const char *callback, struct klp_object *obj)
+{
+ if (obj->mod)
+ pr_info("%s: %s -> %s\n", callback, obj->mod->name,
+ module_state[obj->mod->state]);
+ else
+ pr_info("%s: vmlinux\n", callback);
+}
+
+/* Executed on object patching (ie, patch enablement) */
+static int pre_patch_callback(struct klp_object *obj)
+{
+ callback_info(__func__, obj);
+ return pre_patch_ret;
+}
+
+/* Executed on object unpatching (ie, patch disablement) */
+static void post_patch_callback(struct klp_object *obj)
+{
+ callback_info(__func__, obj);
+}
+
+/* Executed on object unpatching (ie, patch disablement) */
+static void pre_unpatch_callback(struct klp_object *obj)
+{
+ callback_info(__func__, obj);
+}
+
+/* Executed on object unpatching (ie, patch disablement) */
+static void post_unpatch_callback(struct klp_object *obj)
+{
+ callback_info(__func__, obj);
+}
+
+static void patched_work_func(struct work_struct *work)
+{
+ pr_info("%s\n", __func__);
+}
+
+static struct klp_func no_funcs[] = {
+ { }
+};
+
+static struct klp_func busymod_funcs[] = {
+ {
+ .old_name = "busymod_work_func",
+ .new_func = patched_work_func,
+ }, { }
+};
+
+static struct klp_object objs[] = {
+ {
+ .name = NULL, /* vmlinux */
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
+ }, {
+ .name = "test_klp_callbacks_mod",
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
+ }, {
+ .name = "test_klp_callbacks_busy",
+ .funcs = busymod_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
+ }, { }
+};
+
+static struct klp_patch patch = {
+ .mod = THIS_MODULE,
+ .objs = objs,
+};
+
+static int test_klp_callbacks_demo_init(void)
+{
+ int ret;
+
+ ret = klp_register_patch(&patch);
+ if (ret)
+ return ret;
+ ret = klp_enable_patch(&patch);
+ if (ret) {
+ WARN_ON(klp_unregister_patch(&patch));
+ return ret;
+ }
+ return 0;
+}
+
+static void test_klp_callbacks_demo_exit(void)
+{
+ WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(test_klp_callbacks_demo_init);
+module_exit(test_klp_callbacks_demo_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Joe Lawrence <[email protected]>");
+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..c2a6c7b40424
--- /dev/null
+++ b/lib/livepatch/test_klp_callbacks_demo2.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+static int replace;
+module_param(replace, int, 0644);
+MODULE_PARM_DESC(replace, "replace (default=0)");
+
+static const char *const module_state[] = {
+ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state",
+ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init",
+ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away",
+ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up",
+};
+
+static void callback_info(const char *callback, struct klp_object *obj)
+{
+ if (obj->mod)
+ pr_info("%s: %s -> %s\n", callback, obj->mod->name,
+ module_state[obj->mod->state]);
+ else
+ pr_info("%s: vmlinux\n", callback);
+}
+
+/* Executed on object patching (ie, patch enablement) */
+static int pre_patch_callback(struct klp_object *obj)
+{
+ callback_info(__func__, obj);
+ return 0;
+}
+
+/* Executed on object unpatching (ie, patch disablement) */
+static void post_patch_callback(struct klp_object *obj)
+{
+ callback_info(__func__, obj);
+}
+
+/* Executed on object unpatching (ie, patch disablement) */
+static void pre_unpatch_callback(struct klp_object *obj)
+{
+ callback_info(__func__, obj);
+}
+
+/* Executed on object unpatching (ie, patch disablement) */
+static void post_unpatch_callback(struct klp_object *obj)
+{
+ callback_info(__func__, obj);
+}
+
+static struct klp_func no_funcs[] = {
+ { }
+};
+
+static struct klp_object objs[] = {
+ {
+ .name = NULL, /* vmlinux */
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
+ }, { }
+};
+
+static struct klp_patch patch = {
+ .mod = THIS_MODULE,
+ .objs = objs,
+};
+
+static int test_klp_callbacks_demo2_init(void)
+{
+ int ret;
+
+ patch.replace = replace;
+
+ ret = klp_register_patch(&patch);
+ if (ret)
+ return ret;
+ ret = klp_enable_patch(&patch);
+ if (ret) {
+ WARN_ON(klp_unregister_patch(&patch));
+ return ret;
+ }
+ return 0;
+}
+
+static void test_klp_callbacks_demo2_exit(void)
+{
+ WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(test_klp_callbacks_demo2_init);
+module_exit(test_klp_callbacks_demo2_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Joe Lawrence <[email protected]>");
+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 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+static int test_klp_callbacks_mod_init(void)
+{
+ pr_info("%s\n", __func__);
+ return 0;
+}
+
+static void test_klp_callbacks_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+}
+
+module_init(test_klp_callbacks_mod_init);
+module_exit(test_klp_callbacks_mod_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joe Lawrence <[email protected]>");
+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..4014754c8da8
--- /dev/null
+++ b/lib/livepatch/test_klp_livepatch.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2014 Seth Jennings <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+#include <linux/seq_file.h>
+static int livepatch_cmdline_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%s: %s\n", THIS_MODULE->name,
+ "this has been live patched");
+ return 0;
+}
+
+static struct klp_func funcs[] = {
+ {
+ .old_name = "cmdline_proc_show",
+ .new_func = livepatch_cmdline_proc_show,
+ }, { }
+};
+
+static struct klp_object objs[] = {
+ {
+ /* name being NULL means vmlinux */
+ .funcs = funcs,
+ }, { }
+};
+
+static struct klp_patch patch = {
+ .mod = THIS_MODULE,
+ .objs = objs,
+};
+
+static int test_klp_livepatch_init(void)
+{
+ int ret;
+
+ ret = klp_register_patch(&patch);
+ if (ret)
+ return ret;
+ ret = klp_enable_patch(&patch);
+ if (ret) {
+ WARN_ON(klp_unregister_patch(&patch));
+ return ret;
+ }
+ return 0;
+}
+
+static void test_klp_livepatch_exit(void)
+{
+ WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(test_klp_livepatch_init);
+module_exit(test_klp_livepatch_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Seth Jennings <[email protected]>");
+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..05f45747c081
--- /dev/null
+++ b/lib/livepatch/test_klp_shadow_vars.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+
+/*
+ * Keep a small list of pointers so that we can print address-agnostic
+ * pointer values. Use a rolling integer count to differentiate the values.
+ * Ironically we could have used the shadow variable API to do this, but
+ * let's not lean too heavily on the very code we're testing.
+ */
+static LIST_HEAD(ptr_list);
+struct shadow_ptr {
+ void *ptr;
+ int id;
+ struct list_head list;
+};
+
+void free_ptr_list(void)
+{
+ struct shadow_ptr *sp, *tmp_sp;
+
+ list_for_each_entry_safe(sp, tmp_sp, &ptr_list, list) {
+ list_del(&sp->list);
+ kfree(sp);
+ }
+}
+
+int ptr_id(void *ptr)
+{
+ struct shadow_ptr *sp;
+ static int count;
+
+ list_for_each_entry(sp, &ptr_list, list) {
+ if (sp->ptr == ptr)
+ return sp->id;
+ }
+
+ sp = kmalloc(sizeof(*sp), GFP_KERNEL);
+ if (!sp)
+ return -1;
+ sp->ptr = ptr;
+ sp->id = count++;
+
+ list_add(&sp->list, &ptr_list);
+
+ return sp->id;
+}
+
+/*
+ * Shadow variable wrapper functions that echo the function and arguments
+ * to the kernel log for testing verification. Don't display raw pointers,
+ * but use the ptr_id() value instead.
+ */
+void *shadow_get(void *obj, unsigned long id)
+{
+ void *ret = klp_shadow_get(obj, id);
+
+ pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n",
+ __func__, ptr_id(obj), id, ptr_id(ret));
+
+ return ret;
+}
+
+void *shadow_alloc(void *obj, unsigned long id, size_t size, gfp_t gfp_flags,
+ klp_shadow_ctor_t ctor, void *ctor_data)
+{
+ void *ret = klp_shadow_alloc(obj, id, size, gfp_flags, ctor,
+ ctor_data);
+ 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",
+ __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
+ ptr_id(ctor_data), ptr_id(ret));
+ return ret;
+}
+
+void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size,
+ gfp_t gfp_flags, klp_shadow_ctor_t ctor,
+ void *ctor_data)
+{
+ void *ret = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor,
+ ctor_data);
+ 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",
+ __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
+ ptr_id(ctor_data), ptr_id(ret));
+ return ret;
+}
+
+void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
+{
+ klp_shadow_free(obj, id, dtor);
+ pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n",
+ __func__, ptr_id(obj), id, ptr_id(dtor));
+}
+
+void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
+{
+ klp_shadow_free_all(id, dtor);
+ pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n",
+ __func__, id, ptr_id(dtor));
+}
+
+
+/* Shadow variable constructor - remember simple pointer data */
+static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
+{
+ int **shadow_int = shadow_data;
+ *shadow_int = ctor_data;
+ pr_info("%s: PTR%d -> PTR%d\n",
+ __func__, ptr_id(shadow_int), ptr_id(ctor_data));
+
+ return 0;
+}
+
+static void shadow_dtor(void *obj, void *shadow_data)
+{
+ pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n",
+ __func__, ptr_id(obj), ptr_id(shadow_data));
+}
+
+static int test_klp_shadow_vars_init(void)
+{
+ void *obj = THIS_MODULE;
+ int id = 0x1234;
+ size_t size = sizeof(int *);
+ gfp_t gfp_flags = GFP_KERNEL;
+
+ int var1, var2, var3, var4;
+ int **sv1, **sv2, **sv3, **sv4;
+
+ void *ret;
+
+ ptr_id(0);
+ ptr_id(&var1);
+ ptr_id(&var2);
+ ptr_id(&var3);
+ ptr_id(&var4);
+
+ /*
+ * With an empty shadow variable hash table, expect not to find
+ * any matches.
+ */
+ ret = shadow_get(obj, id);
+ if (!ret)
+ pr_info(" got expected NULL result\n");
+
+ /*
+ * Allocate a few shadow variables with different <obj> and <id>.
+ */
+ sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
+ sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2);
+ sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3);
+
+ /*
+ * Verify we can find our new shadow variables and that they point
+ * to expected data.
+ */
+ ret = shadow_get(obj, id);
+ if (ret == sv1 && *sv1 == &var1)
+ pr_info(" got expected PTR%d -> PTR%d result\n",
+ ptr_id(sv1), ptr_id(*sv1));
+ ret = shadow_get(obj + 1, id);
+ if (ret == sv2 && *sv2 == &var2)
+ pr_info(" got expected PTR%d -> PTR%d result\n",
+ ptr_id(sv2), ptr_id(*sv2));
+ ret = shadow_get(obj, id + 1);
+ if (ret == sv3 && *sv3 == &var3)
+ pr_info(" got expected PTR%d -> PTR%d result\n",
+ ptr_id(sv3), ptr_id(*sv3));
+
+ /*
+ * Allocate or get a few more, this time with the same <obj>, <id>.
+ * The second invocation should return the same shadow var.
+ */
+ sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
+ ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
+ if (ret == sv4 && *sv4 == &var4)
+ pr_info(" got expected PTR%d -> PTR%d result\n",
+ ptr_id(sv4), ptr_id(*sv4));
+
+ /*
+ * Free the <obj=*, id> shadow variables and check that we can no
+ * longer find them.
+ */
+ shadow_free(obj, id, shadow_dtor); /* sv1 */
+ ret = shadow_get(obj, id);
+ if (!ret)
+ pr_info(" got expected NULL result\n");
+
+ shadow_free(obj + 1, id, shadow_dtor); /* sv2 */
+ ret = shadow_get(obj + 1, id);
+ if (!ret)
+ pr_info(" got expected NULL result\n");
+
+ shadow_free(obj + 2, id, shadow_dtor); /* sv4 */
+ ret = shadow_get(obj + 2, id);
+ if (!ret)
+ pr_info(" got expected NULL result\n");
+
+ /*
+ * We should still find an <id+1> variable.
+ */
+ ret = shadow_get(obj, id + 1);
+ if (ret == sv3 && *sv3 == &var3)
+ pr_info(" got expected PTR%d -> PTR%d result\n",
+ ptr_id(sv3), ptr_id(*sv3));
+
+ /*
+ * Free all the <id+1> variables, too.
+ */
+ shadow_free_all(id + 1, shadow_dtor); /* sv3 */
+ ret = shadow_get(obj, id);
+ if (!ret)
+ pr_info(" shadow_get() got expected NULL result\n");
+
+
+ free_ptr_list();
+
+ return 0;
+}
+
+static void test_klp_shadow_vars_exit(void)
+{
+}
+
+module_init(test_klp_shadow_vars_init);
+module_init(test_klp_shadow_vars_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joe Lawrence <[email protected]>");
+MODULE_DESCRIPTION("Livepatch test: shadow variables");
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 7442dfb73b7f..d92c63bde9b6 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -15,6 +15,7 @@ TARGETS += intel_pstate
TARGETS += ipc
TARGETS += kcmp
TARGETS += lib
+TARGETS += livepatch
TARGETS += membarrier
TARGETS += memfd
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 @@
+# SPDX-License-Identifier: GPL-2.0
+
+TEST_GEN_PROGS := \
+ test-livepatch.sh \
+ test-callbacks.sh \
+ test-shadow-vars.sh
+
+include ../lib.mk
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..7aaef80e9edb
--- /dev/null
+++ b/tools/testing/selftests/livepatch/functions.sh
@@ -0,0 +1,196 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+# Shell functions for the rest of the scripts.
+
+MAX_RETRIES=600
+RETRY_INTERVAL=".1" # seconds
+
+# die(msg) - game over, man
+# msg - dying words
+function die() {
+ echo "ERROR: $1" >&2
+ exit 1
+}
+
+# set_dynamic_debug() - setup kernel dynamic debug
+# TODO - push and pop this config?
+function set_dynamic_debug() {
+ cat << EOF > /sys/kernel/debug/dynamic_debug/control
+file kernel/livepatch/* +p
+func klp_try_switch_task -p
+EOF
+}
+
+# wait_for_transition(modname)
+# modname - livepatch module name
+wait_for_transition() {
+ local mod="$1"; shift
+
+ # Wait for livepatch transition ...
+ local i=0
+ while [[ $(cat /sys/kernel/livepatch/"$mod"/transition) != "0" ]]; do
+ i=$((i+1))
+ if [[ $i -eq $MAX_RETRIES ]]; then
+ die "failed to complete transition for module $mod"
+ fi
+ sleep $RETRY_INTERVAL
+ done
+}
+
+# load_mod(modname, params) - load a kernel module
+# modname - module name to load
+# params - module parameters to pass to modprobe
+function load_mod() {
+ local mod="$1"; shift
+ local args="$*"
+
+ local msg="% modprobe $mod $args"
+ echo "${msg%% }" > /dev/kmsg
+ ret=$(modprobe "$mod" "$args" 2>&1)
+ if [[ "$ret" != "" ]]; then
+ echo "$ret" > /dev/kmsg
+ die "$ret"
+ fi
+
+ # Wait for module in sysfs ...
+ local i=0
+ while [ ! -e /sys/module/"$mod" ]; do
+ i=$((i+1))
+ if [[ $i -eq $MAX_RETRIES ]]; then
+ die "failed to load module $mod"
+ fi
+ sleep $RETRY_INTERVAL
+ done
+
+ # Wait for livepatch ...
+ if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then
+
+ # Wait for livepatch in sysfs ...
+ local i=0
+ while [ ! -e /sys/kernel/livepatch/"$mod" ]; do
+ i=$((i+1))
+ if [[ $i -eq $MAX_RETRIES ]]; then
+ die "failed to load module $mod (sysfs)"
+ fi
+ sleep $RETRY_INTERVAL
+ done
+ fi
+}
+
+# load_failing_mod(modname, params) - load a kernel module, expect to fail
+# modname - module name to load
+# params - module parameters to pass to modprobe
+function load_failing_mod() {
+ local mod="$1"; shift
+ local args="$*"
+
+ local msg="% modprobe $mod $args"
+ echo "${msg%% }" > /dev/kmsg
+ ret=$(modprobe "$mod" "$args" 2>&1)
+ if [[ "$ret" == "" ]]; then
+ echo "$mod unexpectedly loaded" > /dev/kmsg
+ die "$mod unexpectedly loaded"
+ fi
+ echo "$ret" > /dev/kmsg
+}
+
+# unload_mod(modname) - unload a kernel module
+# modname - module name to unload
+function unload_mod() {
+ local mod="$1"
+
+ # Wait for module reference count to clear ...
+ local i=0
+ while [[ $(cat /sys/module/"$mod"/refcnt) != "0" ]]; do
+ i=$((i+1))
+ if [[ $i -eq $MAX_RETRIES ]]; then
+ die "failed to unload module $mod (refcnt)"
+ fi
+ sleep $RETRY_INTERVAL
+ done
+
+ echo "% rmmod $mod" > /dev/kmsg
+ ret=$(rmmod "$mod" 2>&1)
+ if [[ "$ret" != "" ]]; then
+ echo "$ret" > /dev/kmsg
+ die "$ret"
+ fi
+
+ # Wait for module in sysfs ...
+ local i=0
+ while [ -e /sys/module/"$mod" ]; do
+ i=$((i+1))
+ if [[ $i -eq $MAX_RETRIES ]]; then
+ die "failed to unload module $mod (/sys/module)"
+ fi
+ sleep $RETRY_INTERVAL
+ done
+
+ # Wait for livepatch sysfs if applicable ...
+ if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then
+
+ local i=0
+ while [ -e /sys/kernel/livepatch/"$mod" ]; do
+ i=$((i+1))
+ if [[ $i -eq $MAX_RETRIES ]]; then
+ die "failed to unload module $mod (/sys/livepatch)"
+ fi
+ sleep $RETRY_INTERVAL
+ done
+ fi
+}
+
+# display_lp(modname) - disable a livepatch
+# modname - module name to unload
+function disable_lp() {
+ local mod="$1"
+
+ echo "% echo 0 > /sys/kernel/livepatch/$mod/enabled" > /dev/kmsg
+ echo 0 > /sys/kernel/livepatch/"$mod"/enabled
+
+ # Wait for livepatch enable to clear ...
+ local i=0
+ while [[ $(cat /sys/kernel/livepatch/"$mod"/enabled) != "0" ]]; do
+ i=$((i+1))
+ if [[ $i -eq $MAX_RETRIES ]]; then
+ die "failed to disable livepatch $mod"
+ fi
+ sleep $RETRY_INTERVAL
+ done
+}
+
+# set_pre_patch_ret(modname, pre_patch_ret)
+# modname - module name to set
+# pre_patch_ret - new pre_patch_ret value
+function set_pre_patch_ret {
+ local mod="$1"; shift
+ local ret="$1"
+
+ echo "% echo $1 > /sys/module/$mod/parameters/pre_patch_ret" > /dev/kmsg
+ echo "$1" > /sys/module/"$mod"/parameters/pre_patch_ret
+
+ local i=0
+ while [[ $(cat /sys/module/"$mod"/parameters/pre_patch_ret) != "$1" ]]; do
+ i=$((i+1))
+ if [[ $i -eq $MAX_RETRIES ]]; then
+ die "failed to set pre_patch_ret parameter for $mod module"
+ fi
+ sleep $RETRY_INTERVAL
+ done
+}
+
+# filter_dmesg() - print a filtered dmesg
+# TODO - better filter, out of order msgs, etc?
+function check_result {
+ local expect="$*"
+ local result=$(dmesg | grep -v 'tainting' | grep -e 'livepatch:' -e 'test_klp' | sed 's/^\[[ 0-9.]*\] //')
+
+ if [[ "$expect" == "$result" ]] ; then
+ echo "ok"
+ else
+ echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n"
+ die "livepatch kselftest(s) failed"
+ fi
+}
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh
new file mode 100755
index 000000000000..739d09bb3cff
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test-callbacks.sh
@@ -0,0 +1,607 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+. functions.sh
+
+MOD_LIVEPATCH=test_klp_callbacks_demo
+MOD_LIVEPATCH2=test_klp_callbacks_demo2
+MOD_TARGET=test_klp_callbacks_mod
+MOD_TARGET_BUSY=test_klp_callbacks_busy
+
+set_dynamic_debug
+
+
+# TEST: target module before livepatch
+#
+# Test a combination of loading a kernel module and a livepatch that
+# patches a function in the first module. Load the target module
+# before the livepatch module. Unload them in the same order.
+#
+# - On livepatch enable, before the livepatch transition starts,
+# pre-patch callbacks are executed for vmlinux and $MOD_TARGET (those
+# klp_objects currently loaded). After klp_objects are patched
+# according to the klp_patch, their post-patch callbacks run and the
+# transition completes.
+#
+# - Similarly, on livepatch disable, pre-patch callbacks run before the
+# unpatching transition starts. klp_objects are reverted, post-patch
+# callbacks execute and the transition completes.
+
+echo -n "TEST: target module before livepatch ... "
+dmesg -C
+
+load_mod $MOD_TARGET
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+disable_lp $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+
+# TEST: module_coming notifier
+#
+# This test is similar to the previous test, but (un)load the livepatch
+# module before the target kernel module. This tests the livepatch
+# core's module_coming handler.
+#
+# - On livepatch enable, only pre/post-patch callbacks are executed for
+# currently loaded klp_objects, in this case, vmlinux.
+#
+# - When a targeted module is subsequently loaded, only its
+# pre/post-patch callbacks are executed.
+#
+# - On livepatch disable, all currently loaded klp_objects' (vmlinux and
+# $MOD_TARGET) pre/post-unpatch callbacks are executed.
+
+echo -n "TEST: module_coming notifier ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+load_mod $MOD_TARGET
+disable_lp $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_TARGET
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
+$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
+$MOD_TARGET: ${MOD_TARGET}_init
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+
+# TEST: module_going notifier
+#
+# Test loading the livepatch after a targeted kernel module, then unload
+# the kernel module before disabling the livepatch. This tests the
+# livepatch core's module_going handler.
+#
+# - First load a target module, then the livepatch.
+#
+# - When a target module is unloaded, the livepatch is only reverted
+# from that klp_object ($MOD_TARGET). As such, only its pre and
+# post-unpatch callbacks are executed when this occurs.
+#
+# - When the livepatch is disabled, pre and post-unpatch callbacks are
+# run for the remaining klp_object, vmlinux.
+
+echo -n "TEST: module_going notifier ... "
+dmesg -C
+
+load_mod $MOD_TARGET
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+unload_mod $MOD_TARGET
+disable_lp $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
+$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
+
+
+# TEST: module_coming and module_going notifiers
+#
+# This test is similar to the previous test, however the livepatch is
+# loaded first. This tests the livepatch core's module_coming and
+# module_going handlers.
+#
+# - First load the livepatch.
+#
+# - When a targeted kernel module is subsequently loaded, only its
+# pre/post-patch callbacks are executed.
+#
+# - When the target module is unloaded, the livepatch is only reverted
+# from the $MOD_TARGET klp_object. As such, only pre and
+# post-unpatch callbacks are executed when this occurs.
+
+echo -n "TEST: module_coming and module_going notifiers ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+load_mod $MOD_TARGET
+unload_mod $MOD_TARGET
+disable_lp $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_TARGET
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
+$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
+$MOD_TARGET: ${MOD_TARGET}_init
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
+$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
+
+
+# TEST: target module not present
+#
+# A simple test of loading a livepatch without one of its patch target
+# klp_objects ever loaded ($MOD_TARGET).
+#
+# - Load the livepatch.
+#
+# - As expected, only pre/post-(un)patch handlers are executed for
+# vmlinux.
+
+echo -n "TEST: target module not present ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+disable_lp $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
+
+
+# TEST: pre-patch callback -ENODEV
+#
+# Test a scenario where a vmlinux pre-patch callback returns a non-zero
+# status (ie, failure).
+#
+# - First load a target module.
+#
+# - Load the livepatch module, setting its 'pre_patch_ret' value to -19
+# (-ENODEV). When its vmlinux pre-patch callback executes, this
+# status code will propagate back to the module-loading subsystem.
+# The result is that the insmod command refuses to load the livepatch
+# module.
+
+echo -n "TEST: pre-patch callback -ENODEV ... "
+dmesg -C
+
+load_mod $MOD_TARGET
+load_failing_mod $MOD_LIVEPATCH pre_patch_ret=-19
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+% modprobe $MOD_LIVEPATCH pre_patch_ret=-19
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state
+livepatch: pre-patch callback failed for object '$MOD_TARGET'
+livepatch: failed to enable patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+modprobe: ERROR: could not insert '$MOD_LIVEPATCH': No such device
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+
+# TEST: module_coming + pre-patch callback -ENODEV
+#
+# Similar to the previous test, setup a livepatch such that its vmlinux
+# pre-patch callback returns success. However, when a targeted kernel
+# module is later loaded, have the livepatch return a failing status
+# code.
+#
+# - Load the livepatch, vmlinux pre-patch callback succeeds.
+#
+# - Set a trap so subsequent pre-patch callbacks to this livepatch will
+# return -ENODEV.
+#
+# - The livepatch pre-patch callback for subsequently loaded target
+# modules will return failure, so the module loader refuses to load
+# the kernel module. No post-patch or pre/post-unpatch callbacks are
+# executed for this klp_object.
+#
+# - Pre/post-unpatch callbacks are run for the vmlinux klp_object.
+
+echo -n "TEST: module_coming + pre-patch callback -ENODEV ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+set_pre_patch_ret $MOD_LIVEPATCH -19
+load_failing_mod $MOD_TARGET
+disable_lp $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% echo -19 > /sys/module/$MOD_LIVEPATCH/parameters/pre_patch_ret
+% modprobe $MOD_TARGET
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
+livepatch: pre-patch callback failed for object '$MOD_TARGET'
+livepatch: patch '$MOD_LIVEPATCH' failed for module '$MOD_TARGET', refusing to load module '$MOD_TARGET'
+modprobe: ERROR: could not insert '$MOD_TARGET': No such device
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
+
+
+# TEST: multiple target modules
+#
+# Test loading multiple targeted kernel modules. This test-case is
+# mainly for comparing with the next test-case.
+#
+# - Load a target "busy" kernel module which kicks off a worker function
+# that immediately exits.
+#
+# - Proceed with loading the livepatch and another ordinary target
+# module. Post-patch callbacks are executed and the transition
+# completes quickly.
+
+echo -n "TEST: multiple target modules ... "
+dmesg -C
+
+load_mod $MOD_TARGET_BUSY sleep_secs=0
+# give $MOD_TARGET_BUSY::busymod_work_func() a chance to run
+sleep 5
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+load_mod $MOD_TARGET
+unload_mod $MOD_TARGET
+disable_lp $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+unload_mod $MOD_TARGET_BUSY
+
+check_result "% modprobe $MOD_TARGET_BUSY sleep_secs=0
+$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init
+$MOD_TARGET_BUSY: busymod_work_func, sleeping 0 seconds ...
+$MOD_TARGET_BUSY: busymod_work_func exit
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_TARGET
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
+$MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
+$MOD_TARGET: ${MOD_TARGET}_init
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
+$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET_BUSY
+$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit"
+
+
+
+# TEST: busy target module
+#
+# A similar test as the previous one, but force the "busy" kernel module
+# to do longer work.
+#
+# The livepatching core will refuse to patch a task that is currently
+# executing a to-be-patched function -- the consistency model stalls the
+# current patch transition until this safety-check is met. Test a
+# scenario where one of a livepatch's target klp_objects sits on such a
+# function for a long time. Meanwhile, load and unload other target
+# kernel modules while the livepatch transition is in progress.
+#
+# - Load the "busy" kernel module, this time make it do 10 seconds worth
+# of work.
+#
+# - Meanwhile, the livepatch is loaded. Notice that the patch
+# transition does not complete as the targeted "busy" module is
+# sitting on a to-be-patched function.
+#
+# - Load a second target module (this one is an ordinary idle kernel
+# module). Note that *no* post-patch callbacks will be executed while
+# the livepatch is still in transition.
+#
+# - Request an unload of the simple kernel module. The patch is still
+# transitioning, so its pre-unpatch callbacks are skipped.
+#
+# - Finally the livepatch is disabled. Since none of the patch's
+# klp_object's post-patch callbacks executed, the remaining
+# klp_object's pre-unpatch callbacks are skipped.
+
+echo -n "TEST: busy target module ... "
+dmesg -C
+
+load_mod $MOD_TARGET_BUSY sleep_secs=10
+load_mod $MOD_LIVEPATCH
+# Don't wait for transition, load $MOD_TARGET while the transition
+# is still stalled in $MOD_TARGET_BUSY::busymod_work_func()
+sleep 5
+load_mod $MOD_TARGET
+unload_mod $MOD_TARGET
+disable_lp $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+unload_mod $MOD_TARGET_BUSY
+
+check_result "% modprobe $MOD_TARGET_BUSY sleep_secs=10
+$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init
+$MOD_TARGET_BUSY: busymod_work_func, sleeping 10 seconds ...
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+% modprobe $MOD_TARGET
+livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
+$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
+$MOD_TARGET: ${MOD_TARGET}_init
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET'
+$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': reversing transition from patching to unpatching
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET_BUSY
+$MOD_TARGET_BUSY: busymod_work_func exit
+$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit"
+
+
+# TEST: multiple livepatches
+#
+# Test loading multiple livepatches. This test-case is mainly for comparing
+# with the next test-case.
+#
+# - Load and unload two livepatches, pre and post (un)patch callbacks
+# execute as each patch progresses through its (un)patching
+# transition.
+
+echo -n "TEST: multiple livepatches ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+load_mod $MOD_LIVEPATCH2
+wait_for_transition $MOD_LIVEPATCH2
+disable_lp $MOD_LIVEPATCH2
+wait_for_transition $MOD_LIVEPATCH2
+disable_lp $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH2
+unload_mod $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_LIVEPATCH2
+livepatch: enabling patch '$MOD_LIVEPATCH2'
+livepatch: '$MOD_LIVEPATCH2': initializing patching transition
+$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH2': starting patching transition
+livepatch: '$MOD_LIVEPATCH2': completing patching transition
+$MOD_LIVEPATCH2: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH2': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
+livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
+$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
+$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH2': unpatching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+$MOD_LIVEPATCH: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH2
+% rmmod $MOD_LIVEPATCH"
+
+
+# TEST: atomic replace
+#
+# Load multiple livepatches, but the second as an 'atomic-replace'
+# patch. When the latter laods, the original livepatch should be
+# disabled and *none* of its pre/post-unpatch callbacks executed. On
+# the other hand, when the atomic-replace livepatch is disabled, its
+# pre/post-unpatch callbacks *should* be executed.
+#
+# - Load and unload two livepatches, the second of which has its
+# .replace flag set true.
+#
+# - Pre and post patch callbacks are executed for both livepatches.
+#
+# - Once the atomic replace module is loaded, only its pre and post
+# unpatch callbacks are executed.
+
+echo -n "TEST: atomic replace ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+load_mod $MOD_LIVEPATCH2 replace=1
+wait_for_transition $MOD_LIVEPATCH2
+disable_lp $MOD_LIVEPATCH2
+wait_for_transition $MOD_LIVEPATCH2
+unload_mod $MOD_LIVEPATCH2
+unload_mod $MOD_LIVEPATCH
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+$MOD_LIVEPATCH: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+$MOD_LIVEPATCH: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH': patching complete
+% modprobe $MOD_LIVEPATCH2 replace=1
+livepatch: enabling patch '$MOD_LIVEPATCH2'
+livepatch: '$MOD_LIVEPATCH2': initializing patching transition
+$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH2': starting patching transition
+livepatch: '$MOD_LIVEPATCH2': completing patching transition
+$MOD_LIVEPATCH2: post_patch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH2': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
+livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
+$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
+$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
+livepatch: '$MOD_LIVEPATCH2': unpatching complete
+% rmmod $MOD_LIVEPATCH2
+% rmmod $MOD_LIVEPATCH"
+
+
+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..3e4b8072da84
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test-livepatch.sh
@@ -0,0 +1,173 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+. functions.sh
+
+MOD_LIVEPATCH=test_klp_livepatch
+MOD_REPLACE=test_klp_atomic_replace
+
+set_dynamic_debug
+
+
+# TEST: basic function patching
+# - load a livepatch that modifies the output from /proc/cmdline and
+# verify correct behavior
+# - unload the livepatch and make sure the patch was removed
+
+echo -n "TEST: basic function patching ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+
+if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then
+ echo -e "FAIL\n\n"
+ die "livepatch kselftest(s) failed"
+fi
+
+disable_lp $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+
+if [[ "$(cat /proc/cmdline)" == "$MOD_LIVEPATCH: this has been live patched" ]] ; then
+ echo -e "FAIL\n\n"
+ die "livepatch kselftest(s) failed"
+fi
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
+
+
+# TEST: multiple livepatches
+# - load a livepatch that modifies the output from /proc/cmdline and
+# verify correct behavior
+# - load another livepatch and verify that both livepatches are active
+# - unload the second livepatch and verify that the first is still active
+# - unload the first livepatch and verify none are active
+
+echo -n "TEST: multiple livepatches ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+
+grep 'live patched' /proc/cmdline > /dev/kmsg
+grep 'live patched' /proc/meminfo > /dev/kmsg
+
+load_mod $MOD_REPLACE replace=0
+wait_for_transition $MOD_REPLACE
+
+grep 'live patched' /proc/cmdline > /dev/kmsg
+grep 'live patched' /proc/meminfo > /dev/kmsg
+
+disable_lp $MOD_REPLACE
+unload_mod $MOD_REPLACE
+
+grep 'live patched' /proc/cmdline > /dev/kmsg
+grep 'live patched' /proc/meminfo > /dev/kmsg
+
+disable_lp $MOD_LIVEPATCH
+unload_mod $MOD_LIVEPATCH
+
+grep 'live patched' /proc/cmdline > /dev/kmsg
+grep 'live patched' /proc/meminfo > /dev/kmsg
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+$MOD_LIVEPATCH: this has been live patched
+% modprobe $MOD_REPLACE replace=0
+livepatch: enabling patch '$MOD_REPLACE'
+livepatch: '$MOD_REPLACE': initializing patching transition
+livepatch: '$MOD_REPLACE': starting patching transition
+livepatch: '$MOD_REPLACE': completing patching transition
+livepatch: '$MOD_REPLACE': patching complete
+$MOD_LIVEPATCH: this has been live patched
+$MOD_REPLACE: this has been live patched
+% echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled
+livepatch: '$MOD_REPLACE': initializing unpatching transition
+livepatch: '$MOD_REPLACE': starting unpatching transition
+livepatch: '$MOD_REPLACE': completing unpatching transition
+livepatch: '$MOD_REPLACE': unpatching complete
+% rmmod $MOD_REPLACE
+$MOD_LIVEPATCH: this has been live patched
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH"
+
+
+# TEST: atomic replace livepatch
+# - load a livepatch that modifies the output from /proc/cmdline and
+# verify correct behavior
+# - load an atomic replace livepatch and verify that only the second is active
+# - remove the first livepatch and verify that the atomic replace livepatch
+# is still active
+# - remove the atomic replace livepatch and verify that none are active
+
+echo -n "TEST: atomic replace livepatch ... "
+dmesg -C
+
+load_mod $MOD_LIVEPATCH
+wait_for_transition $MOD_LIVEPATCH
+
+grep 'live patched' /proc/cmdline > /dev/kmsg
+grep 'live patched' /proc/meminfo > /dev/kmsg
+
+load_mod $MOD_REPLACE replace=1
+wait_for_transition $MOD_REPLACE
+
+grep 'live patched' /proc/cmdline > /dev/kmsg
+grep 'live patched' /proc/meminfo > /dev/kmsg
+
+unload_mod $MOD_LIVEPATCH
+
+grep 'live patched' /proc/cmdline > /dev/kmsg
+grep 'live patched' /proc/meminfo > /dev/kmsg
+
+disable_lp $MOD_REPLACE
+unload_mod $MOD_REPLACE
+
+grep 'live patched' /proc/cmdline > /dev/kmsg
+grep 'live patched' /proc/meminfo > /dev/kmsg
+
+check_result "% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+$MOD_LIVEPATCH: this has been live patched
+% modprobe $MOD_REPLACE replace=1
+livepatch: enabling patch '$MOD_REPLACE'
+livepatch: '$MOD_REPLACE': initializing patching transition
+livepatch: '$MOD_REPLACE': starting patching transition
+livepatch: '$MOD_REPLACE': completing patching transition
+livepatch: '$MOD_REPLACE': patching complete
+$MOD_REPLACE: this has been live patched
+% rmmod $MOD_LIVEPATCH
+$MOD_REPLACE: this has been live patched
+% echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled
+livepatch: '$MOD_REPLACE': initializing unpatching transition
+livepatch: '$MOD_REPLACE': starting unpatching transition
+livepatch: '$MOD_REPLACE': completing unpatching transition
+livepatch: '$MOD_REPLACE': unpatching complete
+% rmmod $MOD_REPLACE"
+
+
+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..96390a21b15d
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test-shadow-vars.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+. functions.sh
+
+MOD_TEST=test_klp_shadow_vars
+
+set_dynamic_debug
+
+
+# TEST: basic shadow variable API
+# - load a module that exercises the shadow variable API
+
+echo -n "TEST: basic shadow variable API ... "
+dmesg -C
+
+load_mod $MOD_TEST
+unload_mod $MOD_TEST
+
+check_result "% modprobe $MOD_TEST
+$MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0
+$MOD_TEST: got expected NULL result
+$MOD_TEST: shadow_ctor: PTR6 -> PTR1
+$MOD_TEST: klp_shadow_alloc(obj=PTR5, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR1 = PTR6
+$MOD_TEST: shadow_ctor: PTR8 -> PTR2
+$MOD_TEST: klp_shadow_alloc(obj=PTR9, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR2 = PTR8
+$MOD_TEST: shadow_ctor: PTR10 -> PTR3
+$MOD_TEST: klp_shadow_alloc(obj=PTR5, id=0x1235, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR3 = PTR10
+$MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR6
+$MOD_TEST: got expected PTR6 -> PTR1 result
+$MOD_TEST: klp_shadow_get(obj=PTR9, id=0x1234) = PTR8
+$MOD_TEST: got expected PTR8 -> PTR2 result
+$MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1235) = PTR10
+$MOD_TEST: got expected PTR10 -> PTR3 result
+$MOD_TEST: shadow_ctor: PTR11 -> PTR4
+$MOD_TEST: klp_shadow_get_or_alloc(obj=PTR12, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR4 = PTR11
+$MOD_TEST: klp_shadow_get_or_alloc(obj=PTR12, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR4 = PTR11
+$MOD_TEST: got expected PTR11 -> PTR4 result
+$MOD_TEST: shadow_dtor(obj=PTR5, shadow_data=PTR6)
+$MOD_TEST: klp_shadow_free(obj=PTR5, id=0x1234, dtor=PTR13)
+$MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0
+$MOD_TEST: got expected NULL result
+$MOD_TEST: shadow_dtor(obj=PTR9, shadow_data=PTR8)
+$MOD_TEST: klp_shadow_free(obj=PTR9, id=0x1234, dtor=PTR13)
+$MOD_TEST: klp_shadow_get(obj=PTR9, id=0x1234) = PTR0
+$MOD_TEST: got expected NULL result
+$MOD_TEST: shadow_dtor(obj=PTR12, shadow_data=PTR11)
+$MOD_TEST: klp_shadow_free(obj=PTR12, id=0x1234, dtor=PTR13)
+$MOD_TEST: klp_shadow_get(obj=PTR12, id=0x1234) = PTR0
+$MOD_TEST: got expected NULL result
+$MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1235) = PTR10
+$MOD_TEST: got expected PTR10 -> PTR3 result
+$MOD_TEST: shadow_dtor(obj=PTR5, shadow_data=PTR10)
+$MOD_TEST: klp_shadow_free_all(id=0x1235, dtor=PTR13)
+$MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0
+$MOD_TEST: shadow_get() got expected NULL result
+% rmmod test_klp_shadow_vars"
+
+exit 0
--
1.8.3.1


2018-04-10 20:10:21

by Josh Poimboeuf

[permalink] [raw]
Subject: Re: [PATCH v2] selftests/livepatch: introduce tests

On Tue, Apr 10, 2018 at 11:15:54AM -0400, Joe Lawrence wrote:
> +static void test_klp_shadow_vars_exit(void)
> +{
> +}
> +
> +module_init(test_klp_shadow_vars_init);
> +module_init(test_klp_shadow_vars_exit);

For this last line, s/module_init/module_exit/, though I think the exit
function can just be removed altogether?

Also I get the following bug (run on latest Linus master + Petr's two
patch sets):

[ 106.302072] % modprobe test_klp_shadow_vars
[ 106.311165] test_klp_shadow_vars: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0
[ 106.313080] test_klp_shadow_vars: got expected NULL result
[ 106.314811] BUG: sleeping function called from invalid context at mm/slab.h:421
[ 106.316518] in_atomic(): 1, irqs_disabled(): 1, pid: 2254, name: modprobe
[ 106.318107] 1 lock held by modprobe/2254:
[ 106.319332] #0: 00000000d0851080 (klp_shadow_lock){....}, at: __klp_shadow_get_or_alloc+0x88/0x1b0
[ 106.321220] irq event stamp: 4408
[ 106.322176] hardirqs last enabled at (4407): [<ffffffff8114c28e>] console_unlock+0x44e/0x680
[ 106.323598] hardirqs last disabled at (4408): [<ffffffff8199e6c7>] _raw_spin_lock_irqsave+0x27/0x90
[ 106.325041] softirqs last enabled at (4404): [<ffffffff81c0039b>] __do_softirq+0x39b/0x4fc
[ 106.326469] softirqs last disabled at (4367): [<ffffffff810d8450>] irq_exit+0xe0/0xf0
[ 106.327901] Preemption disabled at:
[ 106.327905] [<ffffffff81167c28>] __klp_shadow_get_or_alloc+0x88/0x1b0
[ 106.330117] CPU: 7 PID: 2254 Comm: modprobe Tainted: G K 4.16.0+ #60
[ 106.331565] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-2.fc27 04/01/2014
[ 106.333143] Call Trace:
[ 106.334011] dump_stack+0x8e/0xd5
[ 106.334962] ___might_sleep+0x185/0x260
[ 106.335997] ? shadow_dtor+0x40/0x40 [test_klp_shadow_vars]
[ 106.337170] __might_sleep+0x4a/0x80
[ 106.338137] kmem_cache_alloc_trace+0x20b/0x300
[ 106.339183] ? ptr_id+0x5c/0xd0 [test_klp_shadow_vars]
[ 106.340341] ? shadow_dtor+0x40/0x40 [test_klp_shadow_vars]
[ 106.341506] ptr_id+0x5c/0xd0 [test_klp_shadow_vars]
[ 106.342725] shadow_ctor+0x20/0x40 [test_klp_shadow_vars]
[ 106.343998] __klp_shadow_get_or_alloc+0xc4/0x1b0
[ 106.345184] ? shadow_dtor+0x40/0x40 [test_klp_shadow_vars]
[ 106.346494] klp_shadow_alloc+0x10/0x20
[ 106.347583] shadow_alloc+0x28/0xa0 [test_klp_shadow_vars]
[ 106.348871] ? shadow_free_all+0x40/0x40 [test_klp_shadow_vars]
[ 106.350200] test_klp_shadow_vars_init+0x96/0x400 [test_klp_shadow_vars]
[ 106.351646] ? shadow_free_all+0x40/0x40 [test_klp_shadow_vars]
[ 106.352949] do_one_initcall+0x61/0x37f
[ 106.353963] ? rcu_read_lock_sched_held+0x79/0x80
[ 106.355040] ? kmem_cache_alloc_trace+0x29d/0x300
[ 106.356098] ? do_init_module+0x27/0x213
[ 106.357090] do_init_module+0x5f/0x213
[ 106.358047] load_module+0x2815/0x2e70
[ 106.358992] ? vfs_read+0x12d/0x150
[ 106.359920] SYSC_finit_module+0xfc/0x120
[ 106.360870] ? SYSC_finit_module+0xfc/0x120
[ 106.361839] SyS_finit_module+0xe/0x10
[ 106.362753] do_syscall_64+0x7e/0x240
[ 106.363645] entry_SYSCALL_64_after_hwframe+0x42/0xb7
[ 106.364711] RIP: 0033:0x7f26d0d40b19
[ 106.365799] RSP: 002b:00007ffee1de19a8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 106.367306] RAX: ffffffffffffffda RBX: 00005636550d54b0 RCX: 00007f26d0d40b19
[ 106.368573] RDX: 0000000000000000 RSI: 0000563654134186 RDI: 0000000000000003
[ 106.369950] RBP: 0000563654134186 R08: 0000000000000000 R09: 00005636550d4270
[ 106.371164] R10: 0000000000000003 R11: 0000000000000246 R12: 0000000000000000
[ 106.372422] R13: 00005636550d53b0 R14: 0000000000040000 R15: 00005636550d54b0
[ 106.373900] test_klp_shadow_vars: shadow_ctor: PTR6 -> PTR1
[ 106.375249] test_klp_shadow_vars: klp_shadow_alloc(obj=PTR5, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR1 = PTR6
[ 106.378893] test_klp_shadow_vars: shadow_ctor: PTR8 -> PTR2
[ 106.380305] test_klp_shadow_vars: klp_shadow_alloc(obj=PTR9, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR2 = PTR8
[ 106.382779] test_klp_shadow_vars: shadow_ctor: PTR10 -> PTR3
[ 106.384131] test_klp_shadow_vars: klp_shadow_alloc(obj=PTR5, id=0x1235, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR3 = PTR10
[ 106.386584] test_klp_shadow_vars: klp_shadow_get(obj=PTR5, id=0x1234) = PTR6
[ 106.387929] test_klp_shadow_vars: got expected PTR6 -> PTR1 result
[ 106.389181] test_klp_shadow_vars: klp_shadow_get(obj=PTR9, id=0x1234) = PTR8
[ 106.390488] test_klp_shadow_vars: got expected PTR8 -> PTR2 result
[ 106.391731] test_klp_shadow_vars: klp_shadow_get(obj=PTR5, id=0x1235) = PTR10
[ 106.393066] test_klp_shadow_vars: got expected PTR10 -> PTR3 result
[ 106.394347] test_klp_shadow_vars: shadow_ctor: PTR11 -> PTR4
[ 106.395523] test_klp_shadow_vars: klp_shadow_get_or_alloc(obj=PTR12, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR4 = PTR11
[ 106.398033] test_klp_shadow_vars: klp_shadow_get_or_alloc(obj=PTR12, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR4 = PTR11
[ 106.400554] test_klp_shadow_vars: got expected PTR11 -> PTR4 result
[ 106.401937] test_klp_shadow_vars: shadow_dtor(obj=PTR5, shadow_data=PTR6)
[ 106.403592] test_klp_shadow_vars: klp_shadow_free(obj=PTR5, id=0x1234, dtor=PTR13)
[ 106.405083] test_klp_shadow_vars: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0
[ 106.406500] test_klp_shadow_vars: got expected NULL result
[ 106.407735] test_klp_shadow_vars: shadow_dtor(obj=PTR9, shadow_data=PTR8)
[ 106.409252] test_klp_shadow_vars: klp_shadow_free(obj=PTR9, id=0x1234, dtor=PTR13)
[ 106.410867] test_klp_shadow_vars: klp_shadow_get(obj=PTR9, id=0x1234) = PTR0
[ 106.412232] test_klp_shadow_vars: got expected NULL result
[ 106.413461] test_klp_shadow_vars: shadow_dtor(obj=PTR12, shadow_data=PTR11)
[ 106.414888] test_klp_shadow_vars: klp_shadow_free(obj=PTR12, id=0x1234, dtor=PTR13)
[ 106.416319] test_klp_shadow_vars: klp_shadow_get(obj=PTR12, id=0x1234) = PTR0
[ 106.417681] test_klp_shadow_vars: got expected NULL result
[ 106.418947] test_klp_shadow_vars: klp_shadow_get(obj=PTR5, id=0x1235) = PTR10
[ 106.420314] test_klp_shadow_vars: got expected PTR10 -> PTR3 result
[ 106.421658] test_klp_shadow_vars: shadow_dtor(obj=PTR5, shadow_data=PTR10)
[ 106.423065] test_klp_shadow_vars: klp_shadow_free_all(id=0x1235, dtor=PTR13)
[ 106.424421] test_klp_shadow_vars: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0
[ 106.425745] test_klp_shadow_vars: shadow_get() got expected NULL result
[ 106.438719] % rmmod test_klp_shadow_vars


--
Josh

2018-04-10 20:11:46

by Josh Poimboeuf

[permalink] [raw]
Subject: Re: [PATCH v2] Add livepatch kselftests

On Tue, Apr 10, 2018 at 11:15:53AM -0400, Joe Lawrence wrote:
> Questions for v3:
>
> - Should we split off the atomic replace and shadow variable update
> tests so that the this patchset could be merged before the ones
> listed above?

Don't split it up on my account. We can just merge this one shortly
after.

--
Josh

2018-04-10 20:55:02

by Joe Lawrence

[permalink] [raw]
Subject: Re: [PATCH v2] selftests/livepatch: introduce tests

On 04/10/2018 04:00 PM, Josh Poimboeuf wrote:
> On Tue, Apr 10, 2018 at 11:15:54AM -0400, Joe Lawrence wrote:
>> +static void test_klp_shadow_vars_exit(void)
>> +{
>> +}
>> +
>> +module_init(test_klp_shadow_vars_init);
>> +module_init(test_klp_shadow_vars_exit);
>
> For this last line, s/module_init/module_exit/, though I think the exit
> function can just be removed altogether?

D'oh workspace / git user error, I posted an older version :(

But the exit function seems to be required if an init function is
provided. Here I omitted the exit function:

% modprobe test_klp_shadow_vars
% lsmod | grep test_klp_shadow_vars
test_klp_shadow_vars 16384 0
% rmmod test_klp_shadow_vars
rmmod: ERROR: could not remove 'test_klp_shadow_vars': Device or
resource busy
rmmod: ERROR: could not remove module test_klp_shadow_vars: Device or
resource busy

and from kernel/module.c

SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
unsigned int, flags)
...
/* If it has an init func, it must have an exit func to unload*/
if (mod->init && !mod->exit) {
forced = try_force_unload(flags);
if (!forced) {
/* This module can't be removed */
ret = -EBUSY;
goto out;
}
}
...

>
> Also I get the following bug (run on latest Linus master + Petr's two
> patch sets):
>
> [ 106.302072] % modprobe test_klp_shadow_vars
> [ 106.311165] test_klp_shadow_vars: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0
> [ 106.313080] test_klp_shadow_vars: got expected NULL result
> [ 106.314811] BUG: sleeping function called from invalid context at mm/slab.h:421
> [ 106.316518] in_atomic(): 1, irqs_disabled(): 1, pid: 2254, name: modprobe
> [ 106.318107] 1 lock held by modprobe/2254:
> [ 106.319332] #0: 00000000d0851080 (klp_shadow_lock){....}, at: __klp_shadow_get_or_alloc+0x88/0x1b0
> [ 106.321220] irq event stamp: 4408
> [ 106.322176] hardirqs last enabled at (4407): [<ffffffff8114c28e>] console_unlock+0x44e/0x680
> [ 106.323598] hardirqs last disabled at (4408): [<ffffffff8199e6c7>] _raw_spin_lock_irqsave+0x27/0x90
> [ 106.325041] softirqs last enabled at (4404): [<ffffffff81c0039b>] __do_softirq+0x39b/0x4fc
> [ 106.326469] softirqs last disabled at (4367): [<ffffffff810d8450>] irq_exit+0xe0/0xf0
> [ 106.327901] Preemption disabled at:
> [ 106.327905] [<ffffffff81167c28>] __klp_shadow_get_or_alloc+0x88/0x1b0
> [ 106.330117] CPU: 7 PID: 2254 Comm: modprobe Tainted: G K 4.16.0+ #60
> [ 106.331565] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-2.fc27 04/01/2014
> [ 106.333143] Call Trace:
> [ 106.334011] dump_stack+0x8e/0xd5
> [ 106.334962] ___might_sleep+0x185/0x260
> [ 106.335997] ? shadow_dtor+0x40/0x40 [test_klp_shadow_vars]
> [ 106.337170] __might_sleep+0x4a/0x80
> [ 106.338137] kmem_cache_alloc_trace+0x20b/0x300
> [ 106.339183] ? ptr_id+0x5c/0xd0 [test_klp_shadow_vars]
> [ 106.340341] ? shadow_dtor+0x40/0x40 [test_klp_shadow_vars]
> [ 106.341506] ptr_id+0x5c/0xd0 [test_klp_shadow_vars]
> [ 106.342725] shadow_ctor+0x20/0x40 [test_klp_shadow_vars]
> [ 106.343998] __klp_shadow_get_or_alloc+0xc4/0x1b0
> [ 106.345184] ? shadow_dtor+0x40/0x40 [test_klp_shadow_vars]
> [ 106.346494] klp_shadow_alloc+0x10/0x20
> [ 106.347583] shadow_alloc+0x28/0xa0 [test_klp_shadow_vars]
> [ 106.348871] ? shadow_free_all+0x40/0x40 [test_klp_shadow_vars]
> [ 106.350200] test_klp_shadow_vars_init+0x96/0x400 [test_klp_shadow_vars]
> [ 106.351646] ? shadow_free_all+0x40/0x40 [test_klp_shadow_vars]
> [ 106.352949] do_one_initcall+0x61/0x37f
> [ 106.353963] ? rcu_read_lock_sched_held+0x79/0x80
> [ 106.355040] ? kmem_cache_alloc_trace+0x29d/0x300
> [ 106.356098] ? do_init_module+0x27/0x213
> [ 106.357090] do_init_module+0x5f/0x213
> [ 106.358047] load_module+0x2815/0x2e70
> [ 106.358992] ? vfs_read+0x12d/0x150
> [ 106.359920] SYSC_finit_module+0xfc/0x120
> [ 106.360870] ? SYSC_finit_module+0xfc/0x120
> [ 106.361839] SyS_finit_module+0xe/0x10
> [ 106.362753] do_syscall_64+0x7e/0x240
> [ 106.363645] entry_SYSCALL_64_after_hwframe+0x42/0xb7
> [ 106.364711] RIP: 0033:0x7f26d0d40b19
> [ 106.365799] RSP: 002b:00007ffee1de19a8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
> [ 106.367306] RAX: ffffffffffffffda RBX: 00005636550d54b0 RCX: 00007f26d0d40b19
> [ 106.368573] RDX: 0000000000000000 RSI: 0000563654134186 RDI: 0000000000000003
> [ 106.369950] RBP: 0000563654134186 R08: 0000000000000000 R09: 00005636550d4270
> [ 106.371164] R10: 0000000000000003 R11: 0000000000000246 R12: 0000000000000000
> [ 106.372422] R13: 00005636550d53b0 R14: 0000000000040000 R15: 00005636550d54b0

I missed Petr's commit msg note "that the constructor is called under
klp_shadow_lock." I'll convert the test to use GFP_ATOMIC for this
allocation.

I'll also add CONFIG_PM_SLEEP_DEBUG=y to my test VM kernel .config. Any
other debug option suggestions?

-- Joe

2018-04-10 21:39:10

by Josh Poimboeuf

[permalink] [raw]
Subject: Re: [PATCH v2] selftests/livepatch: introduce tests

On Tue, Apr 10, 2018 at 04:50:51PM -0400, Joe Lawrence wrote:
> On 04/10/2018 04:00 PM, Josh Poimboeuf wrote:
> > On Tue, Apr 10, 2018 at 11:15:54AM -0400, Joe Lawrence wrote:
> >> +static void test_klp_shadow_vars_exit(void)
> >> +{
> >> +}
> >> +
> >> +module_init(test_klp_shadow_vars_init);
> >> +module_init(test_klp_shadow_vars_exit);
> >
> > For this last line, s/module_init/module_exit/, though I think the exit
> > function can just be removed altogether?
>
> D'oh workspace / git user error, I posted an older version :(
>
> But the exit function seems to be required if an init function is
> provided. Here I omitted the exit function:
>
> % modprobe test_klp_shadow_vars
> % lsmod | grep test_klp_shadow_vars
> test_klp_shadow_vars 16384 0
> % rmmod test_klp_shadow_vars
> rmmod: ERROR: could not remove 'test_klp_shadow_vars': Device or
> resource busy
> rmmod: ERROR: could not remove module test_klp_shadow_vars: Device or
> resource busy
>
> and from kernel/module.c
>
> SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
> unsigned int, flags)
> ...
> /* If it has an init func, it must have an exit func to unload*/
> if (mod->init && !mod->exit) {
> forced = try_force_unload(flags);
> if (!forced) {
> /* This module can't be removed */
> ret = -EBUSY;
> goto out;
> }
> }
> ...

Hm, ok, though that seems like a weird limitation...

> I missed Petr's commit msg note "that the constructor is called under
> klp_shadow_lock." I'll convert the test to use GFP_ATOMIC for this
> allocation.
>
> I'll also add CONFIG_PM_SLEEP_DEBUG=y to my test VM kernel .config. Any
> other debug option suggestions?

I think CONFIG_LOCKDEP is always a good idea. Otherwise, I dunno...
Here's my .config:

https://paste.fedoraproject.org/paste/cnM-J18uVWJrxz2DAdSchg

--
Josh

2018-04-10 21:41:58

by Jiri Kosina

[permalink] [raw]
Subject: Re: [PATCH v2] selftests/livepatch: introduce tests

On Tue, 10 Apr 2018, Josh Poimboeuf wrote:

> I think CONFIG_LOCKDEP is always a good idea.

FWIW CONFIG_LOCKDEP mostly enables the infrastructure, but
CONFIG_PROVE_LOCKING is what turns most of the cleverness on.

--
Jiri Kosina
SUSE Labs


2018-04-11 03:49:28

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2] selftests/livepatch: introduce tests

Hi Joe,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on v4.16]
[also build test ERROR on next-20180410]
[cannot apply to linus/master jikos-livepatching/for-next]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Joe-Lawrence/selftests-livepatch-introduce-tests/20180411-093112
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64

All error/warnings (new ones prefixed by >>):

lib/livepatch/test_klp_shadow_vars.c:76:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:76:9: sparse: got "\001"
lib/livepatch/test_klp_shadow_vars.c:79:9: sparse: Trying to use reserved word 'return' as identifier
lib/livepatch/test_klp_shadow_vars.c:79:16: sparse: Expected ; at end of declaration
lib/livepatch/test_klp_shadow_vars.c:79:16: sparse: got ret
lib/livepatch/test_klp_shadow_vars.c:80:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:80:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:88:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:88:9: sparse: got "\001"
lib/livepatch/test_klp_shadow_vars.c:91:9: sparse: Trying to use reserved word 'return' as identifier
lib/livepatch/test_klp_shadow_vars.c:91:16: sparse: Expected ; at end of declaration
lib/livepatch/test_klp_shadow_vars.c:91:16: sparse: got ret
lib/livepatch/test_klp_shadow_vars.c:92:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:92:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:97:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:97:9: sparse: got "\001"
lib/livepatch/test_klp_shadow_vars.c:99:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:99:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:104:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:104:9: sparse: got "\001"
lib/livepatch/test_klp_shadow_vars.c:106:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:106:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:114:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:114:9: sparse: got "\001"
lib/livepatch/test_klp_shadow_vars.c:117:9: sparse: Trying to use reserved word 'return' as identifier
lib/livepatch/test_klp_shadow_vars.c:117:16: sparse: Expected ; at end of declaration
lib/livepatch/test_klp_shadow_vars.c:117:16: sparse: got 0
lib/livepatch/test_klp_shadow_vars.c:118:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:118:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:124:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:124:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:138:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:138:16: sparse: got 0
lib/livepatch/test_klp_shadow_vars.c:139:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:139:16: sparse: got &
lib/livepatch/test_klp_shadow_vars.c:140:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:140:16: sparse: got &
lib/livepatch/test_klp_shadow_vars.c:141:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:141:16: sparse: got &
lib/livepatch/test_klp_shadow_vars.c:142:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:142:16: sparse: got &
lib/livepatch/test_klp_shadow_vars.c:149:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:149:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:149:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:164:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:164:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:164:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:168:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:168:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:168:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:172:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:172:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:172:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:182:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:182:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:182:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:192:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:192:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:192:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:195:25: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:195:25: sparse: got +
lib/livepatch/test_klp_shadow_vars.c:197:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:197:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:197:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:200:25: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:200:25: sparse: got +
lib/livepatch/test_klp_shadow_vars.c:202:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:202:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:202:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:209:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:209:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:209:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:216:28: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:216:28: sparse: got +
lib/livepatch/test_klp_shadow_vars.c:218:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:218:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:218:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:224:9: sparse: Trying to use reserved word 'return' as identifier
lib/livepatch/test_klp_shadow_vars.c:224:16: sparse: Expected ; at end of declaration
lib/livepatch/test_klp_shadow_vars.c:224:16: sparse: got 0
lib/livepatch/test_klp_shadow_vars.c:225:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:225:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:231:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:231:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:113:23: sparse: undefined identifier 'ctor_data'
lib/livepatch/test_klp_shadow_vars.c:148:26: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:148:9: sparse: symbol 'ret' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:136) - different base types
lib/livepatch/test_klp_shadow_vars.c:155:28: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:155:9: sparse: symbol 'sv1' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:134) - different base types
lib/livepatch/test_klp_shadow_vars.c:156:28: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:156:9: sparse: symbol 'sv2' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:134) - different base types
lib/livepatch/test_klp_shadow_vars.c:157:28: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:157:9: sparse: symbol 'sv3' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:134) - different base types
lib/livepatch/test_klp_shadow_vars.c:163:26: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:163:9: sparse: symbol 'ret' has multiple initializers (originally initialized at lib/livepatch/test_klp_shadow_vars.c:148)
lib/livepatch/test_klp_shadow_vars.c:163:9: sparse: symbol 'ret' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:136) - different base types
lib/livepatch/test_klp_shadow_vars.c:167:26: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:167:9: sparse: symbol 'ret' has multiple initializers (originally initialized at lib/livepatch/test_klp_shadow_vars.c:163)
lib/livepatch/test_klp_shadow_vars.c:167:9: sparse: symbol 'ret' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:136) - different base types
lib/livepatch/test_klp_shadow_vars.c:171:26: sparse: too many errors
>> lib/livepatch/test_klp_shadow_vars.c:72:6: error: unknown type name 'klp_shadow_ctor_t'
klp_shadow_ctor_t ctor, void *ctor_data)
^~~~~~~~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c:83:23: error: unknown type name 'klp_shadow_ctor_t'
gfp_t gfp_flags, klp_shadow_ctor_t ctor,
^~~~~~~~~~~~~~~~~
>> lib/livepatch/test_klp_shadow_vars.c:94:47: error: unknown type name 'klp_shadow_dtor_t'
void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
^~~~~~~~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c:101:40: error: unknown type name 'klp_shadow_dtor_t'
void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
^~~~~~~~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c: In function 'test_klp_shadow_vars_init':
>> lib/livepatch/test_klp_shadow_vars.c:155:8: error: implicit declaration of function 'shadow_alloc'; did you mean 'shadow_dtor'? [-Werror=implicit-function-declaration]
sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
^~~~~~~~~~~~
shadow_dtor
>> lib/livepatch/test_klp_shadow_vars.c:155:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
^
lib/livepatch/test_klp_shadow_vars.c:156:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2);
^
lib/livepatch/test_klp_shadow_vars.c:157:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3);
^
>> lib/livepatch/test_klp_shadow_vars.c:180:8: error: implicit declaration of function 'shadow_get_or_alloc'; did you mean 'klp_shadow_get_or_alloc'? [-Werror=implicit-function-declaration]
sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
^~~~~~~~~~~~~~~~~~~
klp_shadow_get_or_alloc
lib/livepatch/test_klp_shadow_vars.c:180:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
^
lib/livepatch/test_klp_shadow_vars.c:181:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
^
>> lib/livepatch/test_klp_shadow_vars.c:190:2: error: implicit declaration of function 'shadow_free'; did you mean 'shadow_get'? [-Werror=implicit-function-declaration]
shadow_free(obj, id, shadow_dtor); /* sv1 */
^~~~~~~~~~~
shadow_get
>> lib/livepatch/test_klp_shadow_vars.c:216:2: error: implicit declaration of function 'shadow_free_all'; did you mean 'klp_shadow_free_all'? [-Werror=implicit-function-declaration]
shadow_free_all(id + 1, shadow_dtor); /* sv3 */
^~~~~~~~~~~~~~~
klp_shadow_free_all
In file included from lib/livepatch/test_klp_shadow_vars.c:6:0:
lib/livepatch/test_klp_shadow_vars.c: At top level:
include/linux/module.h:130:42: error: redefinition of '__inittest'
static inline initcall_t __maybe_unused __inittest(void) \
^
>> lib/livepatch/test_klp_shadow_vars.c:232:1: note: in expansion of macro 'module_init'
module_init(test_klp_shadow_vars_exit);
^~~~~~~~~~~
include/linux/module.h:130:42: note: previous definition of '__inittest' was here
static inline initcall_t __maybe_unused __inittest(void) \
^
lib/livepatch/test_klp_shadow_vars.c:231:1: note: in expansion of macro 'module_init'
module_init(test_klp_shadow_vars_init);
^~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c: In function '__inittest':
>> lib/livepatch/test_klp_shadow_vars.c:232:13: error: return from incompatible pointer type [-Werror=incompatible-pointer-types]
module_init(test_klp_shadow_vars_exit);
^
include/linux/module.h:131:11: note: in definition of macro 'module_init'
{ return initfn; } \
^~~~~~
lib/livepatch/test_klp_shadow_vars.c: At top level:
include/linux/module.h:132:6: error: redefinition of 'init_module'
int init_module(void) __attribute__((alias(#initfn)));
^
>> lib/livepatch/test_klp_shadow_vars.c:232:1: note: in expansion of macro 'module_init'
module_init(test_klp_shadow_vars_exit);
^~~~~~~~~~~
include/linux/module.h:132:6: note: previous definition of 'init_module' was here
int init_module(void) __attribute__((alias(#initfn)));
^
lib/livepatch/test_klp_shadow_vars.c:231:1: note: in expansion of macro 'module_init'
module_init(test_klp_shadow_vars_init);
^~~~~~~~~~~
cc1: some warnings being treated as errors

sparse warnings: (new ones prefixed by >>)

lib/livepatch/test_klp_atomic_replace.c:46:14: sparse: no member 'replace' in struct klp_patch
>> lib/livepatch/test_klp_atomic_replace.c:46:14: sparse: generating address of non-lvalue (8)
lib/livepatch/test_klp_atomic_replace.c: In function 'test_klp_atomic_replace_init':
lib/livepatch/test_klp_atomic_replace.c:46:7: error: 'struct klp_patch' has no member named 'replace'
patch.replace = replace;
^
--
lib/livepatch/test_klp_callbacks_demo2.c:81:14: sparse: no member 'replace' in struct klp_patch
>> lib/livepatch/test_klp_callbacks_demo2.c:81:14: sparse: generating address of non-lvalue (8)
lib/livepatch/test_klp_callbacks_demo2.c: In function 'test_klp_callbacks_demo2_init':
lib/livepatch/test_klp_callbacks_demo2.c:81:7: error: 'struct klp_patch' has no member named 'replace'
patch.replace = replace;
^
--
lib/livepatch/test_klp_shadow_vars.c:72:38: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:72:38: sparse: got ctor
lib/livepatch/test_klp_shadow_vars.c:76:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:76:9: sparse: got "001"
>> lib/livepatch/test_klp_shadow_vars.c:79:9: sparse: Trying to use reserved word 'return' as identifier
lib/livepatch/test_klp_shadow_vars.c:79:16: sparse: Expected ; at end of declaration
lib/livepatch/test_klp_shadow_vars.c:79:16: sparse: got ret
lib/livepatch/test_klp_shadow_vars.c:80:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:80:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:88:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:88:9: sparse: got "001"
lib/livepatch/test_klp_shadow_vars.c:91:9: sparse: Trying to use reserved word 'return' as identifier
lib/livepatch/test_klp_shadow_vars.c:91:16: sparse: Expected ; at end of declaration
lib/livepatch/test_klp_shadow_vars.c:91:16: sparse: got ret
lib/livepatch/test_klp_shadow_vars.c:92:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:92:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:97:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:97:9: sparse: got "001"
lib/livepatch/test_klp_shadow_vars.c:99:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:99:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:104:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:104:9: sparse: got "001"
lib/livepatch/test_klp_shadow_vars.c:106:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:106:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:114:9: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:114:9: sparse: got "001"
lib/livepatch/test_klp_shadow_vars.c:117:9: sparse: Trying to use reserved word 'return' as identifier
lib/livepatch/test_klp_shadow_vars.c:117:16: sparse: Expected ; at end of declaration
lib/livepatch/test_klp_shadow_vars.c:117:16: sparse: got 0
lib/livepatch/test_klp_shadow_vars.c:118:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:118:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:124:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:124:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:138:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:138:16: sparse: got 0
lib/livepatch/test_klp_shadow_vars.c:139:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:139:16: sparse: got &
lib/livepatch/test_klp_shadow_vars.c:140:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:140:16: sparse: got &
lib/livepatch/test_klp_shadow_vars.c:141:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:141:16: sparse: got &
lib/livepatch/test_klp_shadow_vars.c:142:16: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:142:16: sparse: got &
lib/livepatch/test_klp_shadow_vars.c:149:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:149:13: sparse: got !
>> lib/livepatch/test_klp_shadow_vars.c:149:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:164:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:164:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:164:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:168:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:168:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:168:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:172:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:172:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:172:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:182:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:182:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:182:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:192:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:192:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:192:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:195:25: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:195:25: sparse: got +
lib/livepatch/test_klp_shadow_vars.c:197:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:197:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:197:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:200:25: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:200:25: sparse: got +
lib/livepatch/test_klp_shadow_vars.c:202:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:202:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:202:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:209:17: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:209:17: sparse: got ==
lib/livepatch/test_klp_shadow_vars.c:209:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:216:28: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:216:28: sparse: got +
lib/livepatch/test_klp_shadow_vars.c:218:13: sparse: Expected ) in function declarator
lib/livepatch/test_klp_shadow_vars.c:218:13: sparse: got !
lib/livepatch/test_klp_shadow_vars.c:218:9: sparse: Trying to use reserved word 'if' as identifier
lib/livepatch/test_klp_shadow_vars.c:224:9: sparse: Trying to use reserved word 'return' as identifier
lib/livepatch/test_klp_shadow_vars.c:224:16: sparse: Expected ; at end of declaration
lib/livepatch/test_klp_shadow_vars.c:224:16: sparse: got 0
lib/livepatch/test_klp_shadow_vars.c:225:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:225:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:231:1: sparse: Expected ; at the end of type declaration
lib/livepatch/test_klp_shadow_vars.c:231:1: sparse: got }
lib/livepatch/test_klp_shadow_vars.c:113:23: sparse: undefined identifier 'ctor_data'
lib/livepatch/test_klp_shadow_vars.c:148:26: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:148:9: sparse: symbol 'ret' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:136) - different base types
lib/livepatch/test_klp_shadow_vars.c:155:28: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:155:9: sparse: symbol 'sv1' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:134) - different base types
lib/livepatch/test_klp_shadow_vars.c:156:28: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:156:9: sparse: symbol 'sv2' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:134) - different base types
lib/livepatch/test_klp_shadow_vars.c:157:28: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:157:9: sparse: symbol 'sv3' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:134) - different base types
lib/livepatch/test_klp_shadow_vars.c:163:26: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:163:9: sparse: symbol 'ret' has multiple initializers (originally initialized at lib/livepatch/test_klp_shadow_vars.c:148)
lib/livepatch/test_klp_shadow_vars.c:163:9: sparse: symbol 'ret' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:136) - different base types
lib/livepatch/test_klp_shadow_vars.c:167:26: sparse: undefined identifier 'obj'
lib/livepatch/test_klp_shadow_vars.c:167:9: sparse: symbol 'ret' has multiple initializers (originally initialized at lib/livepatch/test_klp_shadow_vars.c:163)
lib/livepatch/test_klp_shadow_vars.c:167:9: sparse: symbol 'ret' redeclared with different type (originally declared at lib/livepatch/test_klp_shadow_vars.c:136) - different base types
>> lib/livepatch/test_klp_shadow_vars.c:171:26: sparse: too many errors
lib/livepatch/test_klp_shadow_vars.c:72:6: error: unknown type name 'klp_shadow_ctor_t'
klp_shadow_ctor_t ctor, void *ctor_data)
^~~~~~~~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c:83:23: error: unknown type name 'klp_shadow_ctor_t'
gfp_t gfp_flags, klp_shadow_ctor_t ctor,
^~~~~~~~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c:94:47: error: unknown type name 'klp_shadow_dtor_t'
void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
^~~~~~~~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c:101:40: error: unknown type name 'klp_shadow_dtor_t'
void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
^~~~~~~~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c: In function 'test_klp_shadow_vars_init':
lib/livepatch/test_klp_shadow_vars.c:155:8: error: implicit declaration of function 'shadow_alloc'; did you mean 'shadow_dtor'? [-Werror=implicit-function-declaration]
sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
^~~~~~~~~~~~
shadow_dtor
lib/livepatch/test_klp_shadow_vars.c:155:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
^
lib/livepatch/test_klp_shadow_vars.c:156:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2);
^
lib/livepatch/test_klp_shadow_vars.c:157:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3);
^
lib/livepatch/test_klp_shadow_vars.c:180:8: error: implicit declaration of function 'shadow_get_or_alloc'; did you mean 'klp_shadow_get_or_alloc'? [-Werror=implicit-function-declaration]
sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
^~~~~~~~~~~~~~~~~~~
klp_shadow_get_or_alloc
lib/livepatch/test_klp_shadow_vars.c:180:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
^
lib/livepatch/test_klp_shadow_vars.c:181:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
^
lib/livepatch/test_klp_shadow_vars.c:190:2: error: implicit declaration of function 'shadow_free'; did you mean 'shadow_get'? [-Werror=implicit-function-declaration]
shadow_free(obj, id, shadow_dtor); /* sv1 */
^~~~~~~~~~~
shadow_get
lib/livepatch/test_klp_shadow_vars.c:216:2: error: implicit declaration of function 'shadow_free_all'; did you mean 'klp_shadow_free_all'? [-Werror=implicit-function-declaration]
shadow_free_all(id + 1, shadow_dtor); /* sv3 */
^~~~~~~~~~~~~~~
klp_shadow_free_all
In file included from lib/livepatch/test_klp_shadow_vars.c:6:0:
lib/livepatch/test_klp_shadow_vars.c: At top level:
include/linux/module.h:130:42: error: redefinition of '__inittest'
static inline initcall_t __maybe_unused __inittest(void) 151- ^
lib/livepatch/test_klp_shadow_vars.c:232:1: note: in expansion of macro 'module_init'
module_init(test_klp_shadow_vars_exit);
^~~~~~~~~~~
include/linux/module.h:130:42: note: previous definition of '__inittest' was here
static inline initcall_t __maybe_unused __inittest(void) 157- ^
lib/livepatch/test_klp_shadow_vars.c:231:1: note: in expansion of macro 'module_init'
module_init(test_klp_shadow_vars_init);
^~~~~~~~~~~
lib/livepatch/test_klp_shadow_vars.c: In function '__inittest':
lib/livepatch/test_klp_shadow_vars.c:232:13: error: return from incompatible pointer type [-Werror=incompatible-pointer-types]
module_init(test_klp_shadow_vars_exit);
^
include/linux/module.h:131:11: note: in definition of macro 'module_init'
{ return initfn; } 167- ^~~~~~
lib/livepatch/test_klp_shadow_vars.c: At top level:
include/linux/module.h:132:6: error: redefinition of 'init_module'
int init_module(void) __attribute__((alias(#initfn)));
^
lib/livepatch/test_klp_shadow_vars.c:232:1: note: in expansion of macro 'module_init'
module_init(test_klp_shadow_vars_exit);
^~~~~~~~~~~
include/linux/module.h:132:6: note: previous definition of 'init_module' was here
int init_module(void) __attribute__((alias(#initfn)));
^
lib/livepatch/test_klp_shadow_vars.c:231:1: note: in expansion of macro 'module_init'
module_init(test_klp_shadow_vars_init);
^~~~~~~~~~~
cc1: some warnings being treated as errors

vim +/klp_shadow_ctor_t +72 lib/livepatch/test_klp_shadow_vars.c

70
71 void *shadow_alloc(void *obj, unsigned long id, size_t size, gfp_t gfp_flags,
> 72 klp_shadow_ctor_t ctor, void *ctor_data)
73 {
74 void *ret = klp_shadow_alloc(obj, id, size, gfp_flags, ctor,
75 ctor_data);
76 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",
77 __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
78 ptr_id(ctor_data), ptr_id(ret));
> 79 return ret;
80 }
81
82 void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size,
83 gfp_t gfp_flags, klp_shadow_ctor_t ctor,
84 void *ctor_data)
85 {
86 void *ret = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor,
87 ctor_data);
88 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",
89 __func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
90 ptr_id(ctor_data), ptr_id(ret));
91 return ret;
92 }
93
> 94 void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
95 {
96 klp_shadow_free(obj, id, dtor);
97 pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n",
98 __func__, ptr_id(obj), id, ptr_id(dtor));
99 }
100
> 101 void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
102 {
103 klp_shadow_free_all(id, dtor);
104 pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n",
105 __func__, id, ptr_id(dtor));
106 }
107
108
109 /* Shadow variable constructor - remember simple pointer data */
110 static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
111 {
112 int **shadow_int = shadow_data;
113 *shadow_int = ctor_data;
114 pr_info("%s: PTR%d -> PTR%d\n",
115 __func__, ptr_id(shadow_int), ptr_id(ctor_data));
116
117 return 0;
118 }
119
120 static void shadow_dtor(void *obj, void *shadow_data)
121 {
122 pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n",
123 __func__, ptr_id(obj), ptr_id(shadow_data));
124 }
125
126 static int test_klp_shadow_vars_init(void)
127 {
128 void *obj = THIS_MODULE;
129 int id = 0x1234;
130 size_t size = sizeof(int *);
131 gfp_t gfp_flags = GFP_KERNEL;
132
133 int var1, var2, var3, var4;
134 int **sv1, **sv2, **sv3, **sv4;
135
136 void *ret;
137
138 ptr_id(0);
> 139 ptr_id(&var1);
140 ptr_id(&var2);
141 ptr_id(&var3);
142 ptr_id(&var4);
143
144 /*
145 * With an empty shadow variable hash table, expect not to find
146 * any matches.
147 */
148 ret = shadow_get(obj, id);
> 149 if (!ret)
150 pr_info(" got expected NULL result\n");
151
152 /*
153 * Allocate a few shadow variables with different <obj> and <id>.
154 */
> 155 sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
> 156 sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2);
> 157 sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3);
158
159 /*
160 * Verify we can find our new shadow variables and that they point
161 * to expected data.
162 */
163 ret = shadow_get(obj, id);
164 if (ret == sv1 && *sv1 == &var1)
165 pr_info(" got expected PTR%d -> PTR%d result\n",
166 ptr_id(sv1), ptr_id(*sv1));
> 167 ret = shadow_get(obj + 1, id);
168 if (ret == sv2 && *sv2 == &var2)
169 pr_info(" got expected PTR%d -> PTR%d result\n",
170 ptr_id(sv2), ptr_id(*sv2));
> 171 ret = shadow_get(obj, id + 1);
172 if (ret == sv3 && *sv3 == &var3)
173 pr_info(" got expected PTR%d -> PTR%d result\n",
174 ptr_id(sv3), ptr_id(*sv3));
175
176 /*
177 * Allocate or get a few more, this time with the same <obj>, <id>.
178 * The second invocation should return the same shadow var.
179 */
> 180 sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
> 181 ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
182 if (ret == sv4 && *sv4 == &var4)
183 pr_info(" got expected PTR%d -> PTR%d result\n",
184 ptr_id(sv4), ptr_id(*sv4));
185
186 /*
187 * Free the <obj=*, id> shadow variables and check that we can no
188 * longer find them.
189 */
> 190 shadow_free(obj, id, shadow_dtor); /* sv1 */
191 ret = shadow_get(obj, id);
192 if (!ret)
193 pr_info(" got expected NULL result\n");
194
195 shadow_free(obj + 1, id, shadow_dtor); /* sv2 */
196 ret = shadow_get(obj + 1, id);
197 if (!ret)
198 pr_info(" got expected NULL result\n");
199
200 shadow_free(obj + 2, id, shadow_dtor); /* sv4 */
201 ret = shadow_get(obj + 2, id);
202 if (!ret)
203 pr_info(" got expected NULL result\n");
204
205 /*
206 * We should still find an <id+1> variable.
207 */
208 ret = shadow_get(obj, id + 1);
209 if (ret == sv3 && *sv3 == &var3)
210 pr_info(" got expected PTR%d -> PTR%d result\n",
211 ptr_id(sv3), ptr_id(*sv3));
212
213 /*
214 * Free all the <id+1> variables, too.
215 */
> 216 shadow_free_all(id + 1, shadow_dtor); /* sv3 */
217 ret = shadow_get(obj, id);
218 if (!ret)
219 pr_info(" shadow_get() got expected NULL result\n");
220
221
222 free_ptr_list();
223
224 return 0;
225 }
226
227 static void test_klp_shadow_vars_exit(void)
228 {
229 }
230
> 231 module_init(test_klp_shadow_vars_init);
> 232 module_init(test_klp_shadow_vars_exit);

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (35.17 kB)
.config.gz (61.38 kB)
Download all attachments

2018-04-12 13:33:19

by Miroslav Benes

[permalink] [raw]
Subject: Re: [PATCH v2] Add livepatch kselftests

> Questions for v3:
>
> - Should we split off the atomic replace and shadow variable update
> tests so that the this patchset could be merged before the ones
> listed above?

What Josh said. If we merge it almost together, there is no need to split
it.

> - I didn't remove any of the sample modules. If anyone thinks any of
> them should go, let me know. They serve as nice, simple examples so
> I thought they should all stay.

Ok

> - Module naming convention: to make the test script easier to grep
> module names and filenames, I broke with livepatch convention and
> used underscores instead of dashes. I didn't think it worth the
> regex foo to flip back and forth in the test script.

Ok

Miroslav