diff options
| author | Joe Lawrence <joe.lawrence@redhat.com> | 2019-01-09 07:43:29 -0500 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2019-01-11 14:51:24 -0500 |
| commit | a2818ee4dce575b299d8a7f46b393bc2b02ef1f4 (patch) | |
| tree | 0f616177ebf9e8d2f00023f8db89a89374e65c94 /tools/testing/selftests | |
| parent | d67a53720966f2ef5be5c8f238d13512b8260868 (diff) | |
selftests/livepatch: introduce tests
Add a few livepatch modules and simple target modules that the included
regression suite can run tests against:
- basic livepatching (multiple patches, atomic replace)
- pre/post (un)patch callbacks
- shadow variable API
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Petr Mladek <pmladek@suse.com>
Tested-by: Miroslav Benes <mbenes@suse.cz>
Tested-by: Alice Ferrazzi <alice.ferrazzi@gmail.com>
Acked-by: Joe Lawrence <joe.lawrence@redhat.com>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'tools/testing/selftests')
| -rw-r--r-- | tools/testing/selftests/Makefile | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/livepatch/Makefile | 8 | ||||
| -rw-r--r-- | tools/testing/selftests/livepatch/README | 43 | ||||
| -rw-r--r-- | tools/testing/selftests/livepatch/config | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/livepatch/functions.sh | 203 | ||||
| -rwxr-xr-x | tools/testing/selftests/livepatch/test-callbacks.sh | 587 | ||||
| -rwxr-xr-x | tools/testing/selftests/livepatch/test-livepatch.sh | 168 | ||||
| -rwxr-xr-x | tools/testing/selftests/livepatch/test-shadow-vars.sh | 60 |
8 files changed, 1071 insertions, 0 deletions
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 1a2bd15c5b6e..a8ba662da827 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
| @@ -21,6 +21,7 @@ TARGETS += ir | |||
| 21 | TARGETS += kcmp | 21 | TARGETS += kcmp |
| 22 | TARGETS += kvm | 22 | TARGETS += kvm |
| 23 | TARGETS += lib | 23 | TARGETS += lib |
| 24 | TARGETS += livepatch | ||
| 24 | TARGETS += membarrier | 25 | TARGETS += membarrier |
| 25 | TARGETS += memfd | 26 | TARGETS += memfd |
| 26 | TARGETS += memory-hotplug | 27 | TARGETS += memory-hotplug |
diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile new file mode 100644 index 000000000000..af4aee79bebb --- /dev/null +++ b/tools/testing/selftests/livepatch/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | TEST_GEN_PROGS := \ | ||
| 4 | test-livepatch.sh \ | ||
| 5 | test-callbacks.sh \ | ||
| 6 | test-shadow-vars.sh | ||
| 7 | |||
| 8 | include ../lib.mk | ||
diff --git a/tools/testing/selftests/livepatch/README b/tools/testing/selftests/livepatch/README new file mode 100644 index 000000000000..b73cd0e2dd51 --- /dev/null +++ b/tools/testing/selftests/livepatch/README | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | ==================== | ||
| 2 | Livepatch Self Tests | ||
| 3 | ==================== | ||
| 4 | |||
| 5 | This is a small set of sanity tests for the kernel livepatching. | ||
| 6 | |||
| 7 | The test suite loads and unloads several test kernel modules to verify | ||
| 8 | livepatch behavior. Debug information is logged to the kernel's message | ||
| 9 | buffer and parsed for expected messages. (Note: the tests will clear | ||
| 10 | the message buffer between individual tests.) | ||
| 11 | |||
| 12 | |||
| 13 | Config | ||
| 14 | ------ | ||
| 15 | |||
| 16 | Set these config options and their prerequisites: | ||
| 17 | |||
| 18 | CONFIG_LIVEPATCH=y | ||
| 19 | CONFIG_TEST_LIVEPATCH=m | ||
| 20 | |||
| 21 | |||
| 22 | Running the tests | ||
| 23 | ----------------- | ||
| 24 | |||
| 25 | Test kernel modules are built as part of lib/ (make modules) and need to | ||
| 26 | be installed (make modules_install) as the test scripts will modprobe | ||
| 27 | them. | ||
| 28 | |||
| 29 | To run the livepatch selftests, from the top of the kernel source tree: | ||
| 30 | |||
| 31 | % make -C tools/testing/selftests TARGETS=livepatch run_tests | ||
| 32 | |||
| 33 | |||
| 34 | Adding tests | ||
| 35 | ------------ | ||
| 36 | |||
| 37 | See the common functions.sh file for the existing collection of utility | ||
| 38 | functions, most importantly set_dynamic_debug() and check_result(). The | ||
| 39 | latter function greps the kernel's ring buffer for "livepatch:" and | ||
| 40 | "test_klp" strings, so tests be sure to include one of those strings for | ||
| 41 | result comparison. Other utility functions include general module | ||
| 42 | loading and livepatch loading helpers (waiting for patch transitions, | ||
| 43 | sysfs entries, etc.) | ||
diff --git a/tools/testing/selftests/livepatch/config b/tools/testing/selftests/livepatch/config new file mode 100644 index 000000000000..0dd7700464a8 --- /dev/null +++ b/tools/testing/selftests/livepatch/config | |||
| @@ -0,0 +1 @@ | |||
| CONFIG_TEST_LIVEPATCH=m | |||
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh new file mode 100644 index 000000000000..c7b9fb45d7c9 --- /dev/null +++ b/tools/testing/selftests/livepatch/functions.sh | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # SPDX-License-Identifier: GPL-2.0 | ||
| 3 | # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
| 4 | |||
| 5 | # Shell functions for the rest of the scripts. | ||
| 6 | |||
| 7 | MAX_RETRIES=600 | ||
| 8 | RETRY_INTERVAL=".1" # seconds | ||
| 9 | |||
| 10 | # log(msg) - write message to kernel log | ||
| 11 | # msg - insightful words | ||
| 12 | function log() { | ||
| 13 | echo "$1" > /dev/kmsg | ||
| 14 | } | ||
| 15 | |||
| 16 | # die(msg) - game over, man | ||
| 17 | # msg - dying words | ||
| 18 | function die() { | ||
| 19 | log "ERROR: $1" | ||
| 20 | echo "ERROR: $1" >&2 | ||
| 21 | exit 1 | ||
| 22 | } | ||
| 23 | |||
| 24 | # set_dynamic_debug() - setup kernel dynamic debug | ||
| 25 | # TODO - push and pop this config? | ||
| 26 | function set_dynamic_debug() { | ||
| 27 | cat << EOF > /sys/kernel/debug/dynamic_debug/control | ||
| 28 | file kernel/livepatch/* +p | ||
| 29 | func klp_try_switch_task -p | ||
| 30 | EOF | ||
| 31 | } | ||
| 32 | |||
| 33 | # loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES, | ||
| 34 | # sleep $RETRY_INTERVAL between attempts | ||
| 35 | # cmd - command and its arguments to run | ||
| 36 | function loop_until() { | ||
| 37 | local cmd="$*" | ||
| 38 | local i=0 | ||
| 39 | while true; do | ||
| 40 | eval "$cmd" && return 0 | ||
| 41 | [[ $((i++)) -eq $MAX_RETRIES ]] && return 1 | ||
| 42 | sleep $RETRY_INTERVAL | ||
| 43 | done | ||
| 44 | } | ||
| 45 | |||
| 46 | function is_livepatch_mod() { | ||
| 47 | local mod="$1" | ||
| 48 | |||
| 49 | if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then | ||
| 50 | return 0 | ||
| 51 | fi | ||
| 52 | |||
| 53 | return 1 | ||
| 54 | } | ||
| 55 | |||
| 56 | function __load_mod() { | ||
| 57 | local mod="$1"; shift | ||
| 58 | local args="$*" | ||
| 59 | |||
| 60 | local msg="% modprobe $mod $args" | ||
| 61 | log "${msg%% }" | ||
| 62 | ret=$(modprobe "$mod" "$args" 2>&1) | ||
| 63 | if [[ "$ret" != "" ]]; then | ||
| 64 | die "$ret" | ||
| 65 | fi | ||
| 66 | |||
| 67 | # Wait for module in sysfs ... | ||
| 68 | loop_until '[[ -e "/sys/module/$mod" ]]' || | ||
| 69 | die "failed to load module $mod" | ||
| 70 | } | ||
| 71 | |||
| 72 | |||
| 73 | # load_mod(modname, params) - load a kernel module | ||
| 74 | # modname - module name to load | ||
| 75 | # params - module parameters to pass to modprobe | ||
| 76 | function load_mod() { | ||
| 77 | local mod="$1"; shift | ||
| 78 | local args="$*" | ||
| 79 | |||
| 80 | is_livepatch_mod "$mod" && | ||
| 81 | die "use load_lp() to load the livepatch module $mod" | ||
| 82 | |||
| 83 | __load_mod "$mod" "$args" | ||
| 84 | } | ||
| 85 | |||
| 86 | # load_lp_nowait(modname, params) - load a kernel module with a livepatch | ||
| 87 | # but do not wait on until the transition finishes | ||
| 88 | # modname - module name to load | ||
| 89 | # params - module parameters to pass to modprobe | ||
| 90 | function load_lp_nowait() { | ||
| 91 | local mod="$1"; shift | ||
| 92 | local args="$*" | ||
| 93 | |||
| 94 | is_livepatch_mod "$mod" || | ||
| 95 | die "module $mod is not a livepatch" | ||
| 96 | |||
| 97 | __load_mod "$mod" "$args" | ||
| 98 | |||
| 99 | # Wait for livepatch in sysfs ... | ||
| 100 | loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' || | ||
| 101 | die "failed to load module $mod (sysfs)" | ||
| 102 | } | ||
| 103 | |||
| 104 | # load_lp(modname, params) - load a kernel module with a livepatch | ||
| 105 | # modname - module name to load | ||
| 106 | # params - module parameters to pass to modprobe | ||
| 107 | function load_lp() { | ||
| 108 | local mod="$1"; shift | ||
| 109 | local args="$*" | ||
| 110 | |||
| 111 | load_lp_nowait "$mod" "$args" | ||
| 112 | |||
| 113 | # Wait until the transition finishes ... | ||
| 114 | loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' || | ||
| 115 | die "failed to complete transition" | ||
| 116 | } | ||
| 117 | |||
| 118 | # load_failing_mod(modname, params) - load a kernel module, expect to fail | ||
| 119 | # modname - module name to load | ||
| 120 | # params - module parameters to pass to modprobe | ||
| 121 | function load_failing_mod() { | ||
| 122 | local mod="$1"; shift | ||
| 123 | local args="$*" | ||
| 124 | |||
| 125 | local msg="% modprobe $mod $args" | ||
| 126 | log "${msg%% }" | ||
| 127 | ret=$(modprobe "$mod" "$args" 2>&1) | ||
| 128 | if [[ "$ret" == "" ]]; then | ||
| 129 | die "$mod unexpectedly loaded" | ||
| 130 | fi | ||
| 131 | log "$ret" | ||
| 132 | } | ||
| 133 | |||
| 134 | # unload_mod(modname) - unload a kernel module | ||
| 135 | # modname - module name to unload | ||
| 136 | function unload_mod() { | ||
| 137 | local mod="$1" | ||
| 138 | |||
| 139 | # Wait for module reference count to clear ... | ||
| 140 | loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' || | ||
| 141 | die "failed to unload module $mod (refcnt)" | ||
| 142 | |||
| 143 | log "% rmmod $mod" | ||
| 144 | ret=$(rmmod "$mod" 2>&1) | ||
| 145 | if [[ "$ret" != "" ]]; then | ||
| 146 | die "$ret" | ||
| 147 | fi | ||
| 148 | |||
| 149 | # Wait for module in sysfs ... | ||
| 150 | loop_until '[[ ! -e "/sys/module/$mod" ]]' || | ||
| 151 | die "failed to unload module $mod (/sys/module)" | ||
| 152 | } | ||
| 153 | |||
| 154 | # unload_lp(modname) - unload a kernel module with a livepatch | ||
| 155 | # modname - module name to unload | ||
| 156 | function unload_lp() { | ||
| 157 | unload_mod "$1" | ||
| 158 | } | ||
| 159 | |||
| 160 | # disable_lp(modname) - disable a livepatch | ||
| 161 | # modname - module name to unload | ||
| 162 | function disable_lp() { | ||
| 163 | local mod="$1" | ||
| 164 | |||
| 165 | log "% echo 0 > /sys/kernel/livepatch/$mod/enabled" | ||
| 166 | echo 0 > /sys/kernel/livepatch/"$mod"/enabled | ||
| 167 | |||
| 168 | # Wait until the transition finishes and the livepatch gets | ||
| 169 | # removed from sysfs... | ||
| 170 | loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' || | ||
| 171 | die "failed to disable livepatch $mod" | ||
| 172 | } | ||
| 173 | |||
| 174 | # set_pre_patch_ret(modname, pre_patch_ret) | ||
| 175 | # modname - module name to set | ||
| 176 | # pre_patch_ret - new pre_patch_ret value | ||
| 177 | function set_pre_patch_ret { | ||
| 178 | local mod="$1"; shift | ||
| 179 | local ret="$1" | ||
| 180 | |||
| 181 | log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret" | ||
| 182 | echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret | ||
| 183 | |||
| 184 | # Wait for sysfs value to hold ... | ||
| 185 | loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' || | ||
| 186 | die "failed to set pre_patch_ret parameter for $mod module" | ||
| 187 | } | ||
| 188 | |||
| 189 | # check_result() - verify dmesg output | ||
| 190 | # TODO - better filter, out of order msgs, etc? | ||
| 191 | function check_result { | ||
| 192 | local expect="$*" | ||
| 193 | local result | ||
| 194 | |||
| 195 | result=$(dmesg | grep -v 'tainting' | grep -e 'livepatch:' -e 'test_klp' | sed 's/^\[[ 0-9.]*\] //') | ||
| 196 | |||
| 197 | if [[ "$expect" == "$result" ]] ; then | ||
| 198 | echo "ok" | ||
| 199 | else | ||
| 200 | echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n" | ||
| 201 | die "livepatch kselftest(s) failed" | ||
| 202 | fi | ||
| 203 | } | ||
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh new file mode 100755 index 000000000000..e97a9dcb73c7 --- /dev/null +++ b/tools/testing/selftests/livepatch/test-callbacks.sh | |||
| @@ -0,0 +1,587 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # SPDX-License-Identifier: GPL-2.0 | ||
| 3 | # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
| 4 | |||
| 5 | . $(dirname $0)/functions.sh | ||
| 6 | |||
| 7 | MOD_LIVEPATCH=test_klp_callbacks_demo | ||
| 8 | MOD_LIVEPATCH2=test_klp_callbacks_demo2 | ||
| 9 | MOD_TARGET=test_klp_callbacks_mod | ||
| 10 | MOD_TARGET_BUSY=test_klp_callbacks_busy | ||
| 11 | |||
| 12 | set_dynamic_debug | ||
| 13 | |||
| 14 | |||
| 15 | # TEST: target module before livepatch | ||
| 16 | # | ||
| 17 | # Test a combination of loading a kernel module and a livepatch that | ||
| 18 | # patches a function in the first module. Load the target module | ||
| 19 | # before the livepatch module. Unload them in the same order. | ||
| 20 | # | ||
| 21 | # - On livepatch enable, before the livepatch transition starts, | ||
| 22 | # pre-patch callbacks are executed for vmlinux and $MOD_TARGET (those | ||
| 23 | # klp_objects currently loaded). After klp_objects are patched | ||
| 24 | # according to the klp_patch, their post-patch callbacks run and the | ||
| 25 | # transition completes. | ||
| 26 | # | ||
| 27 | # - Similarly, on livepatch disable, pre-patch callbacks run before the | ||
| 28 | # unpatching transition starts. klp_objects are reverted, post-patch | ||
| 29 | # callbacks execute and the transition completes. | ||
| 30 | |||
| 31 | echo -n "TEST: target module before livepatch ... " | ||
| 32 | dmesg -C | ||
| 33 | |||
| 34 | load_mod $MOD_TARGET | ||
| 35 | load_lp $MOD_LIVEPATCH | ||
| 36 | disable_lp $MOD_LIVEPATCH | ||
| 37 | unload_lp $MOD_LIVEPATCH | ||
| 38 | unload_mod $MOD_TARGET | ||
| 39 | |||
| 40 | check_result "% modprobe $MOD_TARGET | ||
| 41 | $MOD_TARGET: ${MOD_TARGET}_init | ||
| 42 | % modprobe $MOD_LIVEPATCH | ||
| 43 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 44 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 45 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 46 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
| 47 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 48 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 49 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 50 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
| 51 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 52 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 53 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 54 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
| 55 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
| 56 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 57 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 58 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 59 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
| 60 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 61 | % rmmod $MOD_LIVEPATCH | ||
| 62 | % rmmod $MOD_TARGET | ||
| 63 | $MOD_TARGET: ${MOD_TARGET}_exit" | ||
| 64 | |||
| 65 | |||
| 66 | # TEST: module_coming notifier | ||
| 67 | # | ||
| 68 | # This test is similar to the previous test, but (un)load the livepatch | ||
| 69 | # module before the target kernel module. This tests the livepatch | ||
| 70 | # core's module_coming handler. | ||
| 71 | # | ||
| 72 | # - On livepatch enable, only pre/post-patch callbacks are executed for | ||
| 73 | # currently loaded klp_objects, in this case, vmlinux. | ||
| 74 | # | ||
| 75 | # - When a targeted module is subsequently loaded, only its | ||
| 76 | # pre/post-patch callbacks are executed. | ||
| 77 | # | ||
| 78 | # - On livepatch disable, all currently loaded klp_objects' (vmlinux and | ||
| 79 | # $MOD_TARGET) pre/post-unpatch callbacks are executed. | ||
| 80 | |||
| 81 | echo -n "TEST: module_coming notifier ... " | ||
| 82 | dmesg -C | ||
| 83 | |||
| 84 | load_lp $MOD_LIVEPATCH | ||
| 85 | load_mod $MOD_TARGET | ||
| 86 | disable_lp $MOD_LIVEPATCH | ||
| 87 | unload_lp $MOD_LIVEPATCH | ||
| 88 | unload_mod $MOD_TARGET | ||
| 89 | |||
| 90 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 91 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 92 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 93 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 94 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 95 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 96 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 97 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 98 | % modprobe $MOD_TARGET | ||
| 99 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
| 100 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
| 101 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
| 102 | $MOD_TARGET: ${MOD_TARGET}_init | ||
| 103 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 104 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 105 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
| 106 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
| 107 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 108 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 109 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 110 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
| 111 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 112 | % rmmod $MOD_LIVEPATCH | ||
| 113 | % rmmod $MOD_TARGET | ||
| 114 | $MOD_TARGET: ${MOD_TARGET}_exit" | ||
| 115 | |||
| 116 | |||
| 117 | # TEST: module_going notifier | ||
| 118 | # | ||
| 119 | # Test loading the livepatch after a targeted kernel module, then unload | ||
| 120 | # the kernel module before disabling the livepatch. This tests the | ||
| 121 | # livepatch core's module_going handler. | ||
| 122 | # | ||
| 123 | # - First load a target module, then the livepatch. | ||
| 124 | # | ||
| 125 | # - When a target module is unloaded, the livepatch is only reverted | ||
| 126 | # from that klp_object ($MOD_TARGET). As such, only its pre and | ||
| 127 | # post-unpatch callbacks are executed when this occurs. | ||
| 128 | # | ||
| 129 | # - When the livepatch is disabled, pre and post-unpatch callbacks are | ||
| 130 | # run for the remaining klp_object, vmlinux. | ||
| 131 | |||
| 132 | echo -n "TEST: module_going notifier ... " | ||
| 133 | dmesg -C | ||
| 134 | |||
| 135 | load_mod $MOD_TARGET | ||
| 136 | load_lp $MOD_LIVEPATCH | ||
| 137 | unload_mod $MOD_TARGET | ||
| 138 | disable_lp $MOD_LIVEPATCH | ||
| 139 | unload_lp $MOD_LIVEPATCH | ||
| 140 | |||
| 141 | check_result "% modprobe $MOD_TARGET | ||
| 142 | $MOD_TARGET: ${MOD_TARGET}_init | ||
| 143 | % modprobe $MOD_LIVEPATCH | ||
| 144 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 145 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 146 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 147 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
| 148 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 149 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 150 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 151 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_LIVE] Normal state | ||
| 152 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 153 | % rmmod $MOD_TARGET | ||
| 154 | $MOD_TARGET: ${MOD_TARGET}_exit | ||
| 155 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
| 156 | livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET' | ||
| 157 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
| 158 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 159 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 160 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
| 161 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 162 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 163 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 164 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 165 | % rmmod $MOD_LIVEPATCH" | ||
| 166 | |||
| 167 | |||
| 168 | # TEST: module_coming and module_going notifiers | ||
| 169 | # | ||
| 170 | # This test is similar to the previous test, however the livepatch is | ||
| 171 | # loaded first. This tests the livepatch core's module_coming and | ||
| 172 | # module_going handlers. | ||
| 173 | # | ||
| 174 | # - First load the livepatch. | ||
| 175 | # | ||
| 176 | # - When a targeted kernel module is subsequently loaded, only its | ||
| 177 | # pre/post-patch callbacks are executed. | ||
| 178 | # | ||
| 179 | # - When the target module is unloaded, the livepatch is only reverted | ||
| 180 | # from the $MOD_TARGET klp_object. As such, only pre and | ||
| 181 | # post-unpatch callbacks are executed when this occurs. | ||
| 182 | |||
| 183 | echo -n "TEST: module_coming and module_going notifiers ... " | ||
| 184 | dmesg -C | ||
| 185 | |||
| 186 | load_lp $MOD_LIVEPATCH | ||
| 187 | load_mod $MOD_TARGET | ||
| 188 | unload_mod $MOD_TARGET | ||
| 189 | disable_lp $MOD_LIVEPATCH | ||
| 190 | unload_lp $MOD_LIVEPATCH | ||
| 191 | |||
| 192 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 193 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 194 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 195 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 196 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 197 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 198 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 199 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 200 | % modprobe $MOD_TARGET | ||
| 201 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
| 202 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
| 203 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
| 204 | $MOD_TARGET: ${MOD_TARGET}_init | ||
| 205 | % rmmod $MOD_TARGET | ||
| 206 | $MOD_TARGET: ${MOD_TARGET}_exit | ||
| 207 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
| 208 | livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET' | ||
| 209 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
| 210 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 211 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 212 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
| 213 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 214 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 215 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 216 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 217 | % rmmod $MOD_LIVEPATCH" | ||
| 218 | |||
| 219 | |||
| 220 | # TEST: target module not present | ||
| 221 | # | ||
| 222 | # A simple test of loading a livepatch without one of its patch target | ||
| 223 | # klp_objects ever loaded ($MOD_TARGET). | ||
| 224 | # | ||
| 225 | # - Load the livepatch. | ||
| 226 | # | ||
| 227 | # - As expected, only pre/post-(un)patch handlers are executed for | ||
| 228 | # vmlinux. | ||
| 229 | |||
| 230 | echo -n "TEST: target module not present ... " | ||
| 231 | dmesg -C | ||
| 232 | |||
| 233 | load_lp $MOD_LIVEPATCH | ||
| 234 | disable_lp $MOD_LIVEPATCH | ||
| 235 | unload_lp $MOD_LIVEPATCH | ||
| 236 | |||
| 237 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 238 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 239 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 240 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 241 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 242 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 243 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 244 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 245 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 246 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 247 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
| 248 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 249 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 250 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 251 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 252 | % rmmod $MOD_LIVEPATCH" | ||
| 253 | |||
| 254 | |||
| 255 | # TEST: pre-patch callback -ENODEV | ||
| 256 | # | ||
| 257 | # Test a scenario where a vmlinux pre-patch callback returns a non-zero | ||
| 258 | # status (ie, failure). | ||
| 259 | # | ||
| 260 | # - First load a target module. | ||
| 261 | # | ||
| 262 | # - Load the livepatch module, setting its 'pre_patch_ret' value to -19 | ||
| 263 | # (-ENODEV). When its vmlinux pre-patch callback executes, this | ||
| 264 | # status code will propagate back to the module-loading subsystem. | ||
| 265 | # The result is that the insmod command refuses to load the livepatch | ||
| 266 | # module. | ||
| 267 | |||
| 268 | echo -n "TEST: pre-patch callback -ENODEV ... " | ||
| 269 | dmesg -C | ||
| 270 | |||
| 271 | load_mod $MOD_TARGET | ||
| 272 | load_failing_mod $MOD_LIVEPATCH pre_patch_ret=-19 | ||
| 273 | unload_mod $MOD_TARGET | ||
| 274 | |||
| 275 | check_result "% modprobe $MOD_TARGET | ||
| 276 | $MOD_TARGET: ${MOD_TARGET}_init | ||
| 277 | % modprobe $MOD_LIVEPATCH pre_patch_ret=-19 | ||
| 278 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 279 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 280 | test_klp_callbacks_demo: pre_patch_callback: vmlinux | ||
| 281 | livepatch: pre-patch callback failed for object 'vmlinux' | ||
| 282 | livepatch: failed to enable patch '$MOD_LIVEPATCH' | ||
| 283 | livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch | ||
| 284 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 285 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 286 | modprobe: ERROR: could not insert '$MOD_LIVEPATCH': No such device | ||
| 287 | % rmmod $MOD_TARGET | ||
| 288 | $MOD_TARGET: ${MOD_TARGET}_exit" | ||
| 289 | |||
| 290 | |||
| 291 | # TEST: module_coming + pre-patch callback -ENODEV | ||
| 292 | # | ||
| 293 | # Similar to the previous test, setup a livepatch such that its vmlinux | ||
| 294 | # pre-patch callback returns success. However, when a targeted kernel | ||
| 295 | # module is later loaded, have the livepatch return a failing status | ||
| 296 | # code. | ||
| 297 | # | ||
| 298 | # - Load the livepatch, vmlinux pre-patch callback succeeds. | ||
| 299 | # | ||
| 300 | # - Set a trap so subsequent pre-patch callbacks to this livepatch will | ||
| 301 | # return -ENODEV. | ||
| 302 | # | ||
| 303 | # - The livepatch pre-patch callback for subsequently loaded target | ||
| 304 | # modules will return failure, so the module loader refuses to load | ||
| 305 | # the kernel module. No post-patch or pre/post-unpatch callbacks are | ||
| 306 | # executed for this klp_object. | ||
| 307 | # | ||
| 308 | # - Pre/post-unpatch callbacks are run for the vmlinux klp_object. | ||
| 309 | |||
| 310 | echo -n "TEST: module_coming + pre-patch callback -ENODEV ... " | ||
| 311 | dmesg -C | ||
| 312 | |||
| 313 | load_lp $MOD_LIVEPATCH | ||
| 314 | set_pre_patch_ret $MOD_LIVEPATCH -19 | ||
| 315 | load_failing_mod $MOD_TARGET | ||
| 316 | disable_lp $MOD_LIVEPATCH | ||
| 317 | unload_lp $MOD_LIVEPATCH | ||
| 318 | |||
| 319 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 320 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 321 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 322 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 323 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 324 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 325 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 326 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 327 | % echo -19 > /sys/module/$MOD_LIVEPATCH/parameters/pre_patch_ret | ||
| 328 | % modprobe $MOD_TARGET | ||
| 329 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
| 330 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
| 331 | livepatch: pre-patch callback failed for object '$MOD_TARGET' | ||
| 332 | livepatch: patch '$MOD_LIVEPATCH' failed for module '$MOD_TARGET', refusing to load module '$MOD_TARGET' | ||
| 333 | modprobe: ERROR: could not insert '$MOD_TARGET': No such device | ||
| 334 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 335 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 336 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
| 337 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 338 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 339 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 340 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 341 | % rmmod $MOD_LIVEPATCH" | ||
| 342 | |||
| 343 | |||
| 344 | # TEST: multiple target modules | ||
| 345 | # | ||
| 346 | # Test loading multiple targeted kernel modules. This test-case is | ||
| 347 | # mainly for comparing with the next test-case. | ||
| 348 | # | ||
| 349 | # - Load a target "busy" kernel module which kicks off a worker function | ||
| 350 | # that immediately exits. | ||
| 351 | # | ||
| 352 | # - Proceed with loading the livepatch and another ordinary target | ||
| 353 | # module. Post-patch callbacks are executed and the transition | ||
| 354 | # completes quickly. | ||
| 355 | |||
| 356 | echo -n "TEST: multiple target modules ... " | ||
| 357 | dmesg -C | ||
| 358 | |||
| 359 | load_mod $MOD_TARGET_BUSY sleep_secs=0 | ||
| 360 | # give $MOD_TARGET_BUSY::busymod_work_func() a chance to run | ||
| 361 | sleep 5 | ||
| 362 | load_lp $MOD_LIVEPATCH | ||
| 363 | load_mod $MOD_TARGET | ||
| 364 | unload_mod $MOD_TARGET | ||
| 365 | disable_lp $MOD_LIVEPATCH | ||
| 366 | unload_lp $MOD_LIVEPATCH | ||
| 367 | unload_mod $MOD_TARGET_BUSY | ||
| 368 | |||
| 369 | check_result "% modprobe $MOD_TARGET_BUSY sleep_secs=0 | ||
| 370 | $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init | ||
| 371 | $MOD_TARGET_BUSY: busymod_work_func, sleeping 0 seconds ... | ||
| 372 | $MOD_TARGET_BUSY: busymod_work_func exit | ||
| 373 | % modprobe $MOD_LIVEPATCH | ||
| 374 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 375 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 376 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 377 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
| 378 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 379 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 380 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 381 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
| 382 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 383 | % modprobe $MOD_TARGET | ||
| 384 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
| 385 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
| 386 | $MOD_LIVEPATCH: post_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
| 387 | $MOD_TARGET: ${MOD_TARGET}_init | ||
| 388 | % rmmod $MOD_TARGET | ||
| 389 | $MOD_TARGET: ${MOD_TARGET}_exit | ||
| 390 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
| 391 | livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET' | ||
| 392 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
| 393 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 394 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 395 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
| 396 | $MOD_LIVEPATCH: pre_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
| 397 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 398 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 399 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 400 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
| 401 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 402 | % rmmod $MOD_LIVEPATCH | ||
| 403 | % rmmod $MOD_TARGET_BUSY | ||
| 404 | $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit" | ||
| 405 | |||
| 406 | |||
| 407 | |||
| 408 | # TEST: busy target module | ||
| 409 | # | ||
| 410 | # A similar test as the previous one, but force the "busy" kernel module | ||
| 411 | # to do longer work. | ||
| 412 | # | ||
| 413 | # The livepatching core will refuse to patch a task that is currently | ||
| 414 | # executing a to-be-patched function -- the consistency model stalls the | ||
| 415 | # current patch transition until this safety-check is met. Test a | ||
| 416 | # scenario where one of a livepatch's target klp_objects sits on such a | ||
| 417 | # function for a long time. Meanwhile, load and unload other target | ||
| 418 | # kernel modules while the livepatch transition is in progress. | ||
| 419 | # | ||
| 420 | # - Load the "busy" kernel module, this time make it do 10 seconds worth | ||
| 421 | # of work. | ||
| 422 | # | ||
| 423 | # - Meanwhile, the livepatch is loaded. Notice that the patch | ||
| 424 | # transition does not complete as the targeted "busy" module is | ||
| 425 | # sitting on a to-be-patched function. | ||
| 426 | # | ||
| 427 | # - Load a second target module (this one is an ordinary idle kernel | ||
| 428 | # module). Note that *no* post-patch callbacks will be executed while | ||
| 429 | # the livepatch is still in transition. | ||
| 430 | # | ||
| 431 | # - Request an unload of the simple kernel module. The patch is still | ||
| 432 | # transitioning, so its pre-unpatch callbacks are skipped. | ||
| 433 | # | ||
| 434 | # - Finally the livepatch is disabled. Since none of the patch's | ||
| 435 | # klp_object's post-patch callbacks executed, the remaining | ||
| 436 | # klp_object's pre-unpatch callbacks are skipped. | ||
| 437 | |||
| 438 | echo -n "TEST: busy target module ... " | ||
| 439 | dmesg -C | ||
| 440 | |||
| 441 | load_mod $MOD_TARGET_BUSY sleep_secs=10 | ||
| 442 | load_lp_nowait $MOD_LIVEPATCH | ||
| 443 | # Don't wait for transition, load $MOD_TARGET while the transition | ||
| 444 | # is still stalled in $MOD_TARGET_BUSY::busymod_work_func() | ||
| 445 | sleep 5 | ||
| 446 | load_mod $MOD_TARGET | ||
| 447 | unload_mod $MOD_TARGET | ||
| 448 | disable_lp $MOD_LIVEPATCH | ||
| 449 | unload_lp $MOD_LIVEPATCH | ||
| 450 | unload_mod $MOD_TARGET_BUSY | ||
| 451 | |||
| 452 | check_result "% modprobe $MOD_TARGET_BUSY sleep_secs=10 | ||
| 453 | $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init | ||
| 454 | $MOD_TARGET_BUSY: busymod_work_func, sleeping 10 seconds ... | ||
| 455 | % modprobe $MOD_LIVEPATCH | ||
| 456 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 457 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 458 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 459 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
| 460 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 461 | % modprobe $MOD_TARGET | ||
| 462 | livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET' | ||
| 463 | $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init | ||
| 464 | $MOD_TARGET: ${MOD_TARGET}_init | ||
| 465 | % rmmod $MOD_TARGET | ||
| 466 | $MOD_TARGET: ${MOD_TARGET}_exit | ||
| 467 | livepatch: reverting patch '$MOD_LIVEPATCH' on unloading module '$MOD_TARGET' | ||
| 468 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET -> [MODULE_STATE_GOING] Going away | ||
| 469 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 470 | livepatch: '$MOD_LIVEPATCH': reversing transition from patching to unpatching | ||
| 471 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 472 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 473 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 474 | $MOD_LIVEPATCH: post_unpatch_callback: $MOD_TARGET_BUSY -> [MODULE_STATE_LIVE] Normal state | ||
| 475 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 476 | % rmmod $MOD_LIVEPATCH | ||
| 477 | % rmmod $MOD_TARGET_BUSY | ||
| 478 | $MOD_TARGET_BUSY: busymod_work_func exit | ||
| 479 | $MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit" | ||
| 480 | |||
| 481 | |||
| 482 | # TEST: multiple livepatches | ||
| 483 | # | ||
| 484 | # Test loading multiple livepatches. This test-case is mainly for comparing | ||
| 485 | # with the next test-case. | ||
| 486 | # | ||
| 487 | # - Load and unload two livepatches, pre and post (un)patch callbacks | ||
| 488 | # execute as each patch progresses through its (un)patching | ||
| 489 | # transition. | ||
| 490 | |||
| 491 | echo -n "TEST: multiple livepatches ... " | ||
| 492 | dmesg -C | ||
| 493 | |||
| 494 | load_lp $MOD_LIVEPATCH | ||
| 495 | load_lp $MOD_LIVEPATCH2 | ||
| 496 | disable_lp $MOD_LIVEPATCH2 | ||
| 497 | disable_lp $MOD_LIVEPATCH | ||
| 498 | unload_lp $MOD_LIVEPATCH2 | ||
| 499 | unload_lp $MOD_LIVEPATCH | ||
| 500 | |||
| 501 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 502 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 503 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 504 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 505 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 506 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 507 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 508 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 509 | % modprobe $MOD_LIVEPATCH2 | ||
| 510 | livepatch: enabling patch '$MOD_LIVEPATCH2' | ||
| 511 | livepatch: '$MOD_LIVEPATCH2': initializing patching transition | ||
| 512 | $MOD_LIVEPATCH2: pre_patch_callback: vmlinux | ||
| 513 | livepatch: '$MOD_LIVEPATCH2': starting patching transition | ||
| 514 | livepatch: '$MOD_LIVEPATCH2': completing patching transition | ||
| 515 | $MOD_LIVEPATCH2: post_patch_callback: vmlinux | ||
| 516 | livepatch: '$MOD_LIVEPATCH2': patching complete | ||
| 517 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled | ||
| 518 | livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition | ||
| 519 | $MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux | ||
| 520 | livepatch: '$MOD_LIVEPATCH2': starting unpatching transition | ||
| 521 | livepatch: '$MOD_LIVEPATCH2': completing unpatching transition | ||
| 522 | $MOD_LIVEPATCH2: post_unpatch_callback: vmlinux | ||
| 523 | livepatch: '$MOD_LIVEPATCH2': unpatching complete | ||
| 524 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 525 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 526 | $MOD_LIVEPATCH: pre_unpatch_callback: vmlinux | ||
| 527 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 528 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 529 | $MOD_LIVEPATCH: post_unpatch_callback: vmlinux | ||
| 530 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 531 | % rmmod $MOD_LIVEPATCH2 | ||
| 532 | % rmmod $MOD_LIVEPATCH" | ||
| 533 | |||
| 534 | |||
| 535 | # TEST: atomic replace | ||
| 536 | # | ||
| 537 | # Load multiple livepatches, but the second as an 'atomic-replace' | ||
| 538 | # patch. When the latter loads, the original livepatch should be | ||
| 539 | # disabled and *none* of its pre/post-unpatch callbacks executed. On | ||
| 540 | # the other hand, when the atomic-replace livepatch is disabled, its | ||
| 541 | # pre/post-unpatch callbacks *should* be executed. | ||
| 542 | # | ||
| 543 | # - Load and unload two livepatches, the second of which has its | ||
| 544 | # .replace flag set true. | ||
| 545 | # | ||
| 546 | # - Pre and post patch callbacks are executed for both livepatches. | ||
| 547 | # | ||
| 548 | # - Once the atomic replace module is loaded, only its pre and post | ||
| 549 | # unpatch callbacks are executed. | ||
| 550 | |||
| 551 | echo -n "TEST: atomic replace ... " | ||
| 552 | dmesg -C | ||
| 553 | |||
| 554 | load_lp $MOD_LIVEPATCH | ||
| 555 | load_lp $MOD_LIVEPATCH2 replace=1 | ||
| 556 | disable_lp $MOD_LIVEPATCH2 | ||
| 557 | unload_lp $MOD_LIVEPATCH2 | ||
| 558 | unload_lp $MOD_LIVEPATCH | ||
| 559 | |||
| 560 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 561 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 562 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 563 | $MOD_LIVEPATCH: pre_patch_callback: vmlinux | ||
| 564 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 565 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 566 | $MOD_LIVEPATCH: post_patch_callback: vmlinux | ||
| 567 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 568 | % modprobe $MOD_LIVEPATCH2 replace=1 | ||
| 569 | livepatch: enabling patch '$MOD_LIVEPATCH2' | ||
| 570 | livepatch: '$MOD_LIVEPATCH2': initializing patching transition | ||
| 571 | $MOD_LIVEPATCH2: pre_patch_callback: vmlinux | ||
| 572 | livepatch: '$MOD_LIVEPATCH2': starting patching transition | ||
| 573 | livepatch: '$MOD_LIVEPATCH2': completing patching transition | ||
| 574 | $MOD_LIVEPATCH2: post_patch_callback: vmlinux | ||
| 575 | livepatch: '$MOD_LIVEPATCH2': patching complete | ||
| 576 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled | ||
| 577 | livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition | ||
| 578 | $MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux | ||
| 579 | livepatch: '$MOD_LIVEPATCH2': starting unpatching transition | ||
| 580 | livepatch: '$MOD_LIVEPATCH2': completing unpatching transition | ||
| 581 | $MOD_LIVEPATCH2: post_unpatch_callback: vmlinux | ||
| 582 | livepatch: '$MOD_LIVEPATCH2': unpatching complete | ||
| 583 | % rmmod $MOD_LIVEPATCH2 | ||
| 584 | % rmmod $MOD_LIVEPATCH" | ||
| 585 | |||
| 586 | |||
| 587 | exit 0 | ||
diff --git a/tools/testing/selftests/livepatch/test-livepatch.sh b/tools/testing/selftests/livepatch/test-livepatch.sh new file mode 100755 index 000000000000..f05268aea859 --- /dev/null +++ b/tools/testing/selftests/livepatch/test-livepatch.sh | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # SPDX-License-Identifier: GPL-2.0 | ||
| 3 | # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
| 4 | |||
| 5 | . $(dirname $0)/functions.sh | ||
| 6 | |||
| 7 | MOD_LIVEPATCH=test_klp_livepatch | ||
| 8 | MOD_REPLACE=test_klp_atomic_replace | ||
| 9 | |||
| 10 | set_dynamic_debug | ||
| 11 | |||
| 12 | |||
| 13 | # TEST: basic function patching | ||
| 14 | # - load a livepatch that modifies the output from /proc/cmdline and | ||
| 15 | # verify correct behavior | ||
| 16 | # - unload the livepatch and make sure the patch was removed | ||
| 17 | |||
| 18 | echo -n "TEST: basic function patching ... " | ||
| 19 | dmesg -C | ||
| 20 | |||
| 21 | load_lp $MOD_LIVEPATCH | ||
| 22 | |||
| 23 | if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then | ||
| 24 | echo -e "FAIL\n\n" | ||
| 25 | die "livepatch kselftest(s) failed" | ||
| 26 | fi | ||
| 27 | |||
| 28 | disable_lp $MOD_LIVEPATCH | ||
| 29 | unload_lp $MOD_LIVEPATCH | ||
| 30 | |||
| 31 | if [[ "$(cat /proc/cmdline)" == "$MOD_LIVEPATCH: this has been live patched" ]] ; then | ||
| 32 | echo -e "FAIL\n\n" | ||
| 33 | die "livepatch kselftest(s) failed" | ||
| 34 | fi | ||
| 35 | |||
| 36 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 37 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 38 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 39 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 40 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 41 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 42 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 43 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 44 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 45 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 46 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 47 | % rmmod $MOD_LIVEPATCH" | ||
| 48 | |||
| 49 | |||
| 50 | # TEST: multiple livepatches | ||
| 51 | # - load a livepatch that modifies the output from /proc/cmdline and | ||
| 52 | # verify correct behavior | ||
| 53 | # - load another livepatch and verify that both livepatches are active | ||
| 54 | # - unload the second livepatch and verify that the first is still active | ||
| 55 | # - unload the first livepatch and verify none are active | ||
| 56 | |||
| 57 | echo -n "TEST: multiple livepatches ... " | ||
| 58 | dmesg -C | ||
| 59 | |||
| 60 | load_lp $MOD_LIVEPATCH | ||
| 61 | |||
| 62 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
| 63 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
| 64 | |||
| 65 | load_lp $MOD_REPLACE replace=0 | ||
| 66 | |||
| 67 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
| 68 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
| 69 | |||
| 70 | disable_lp $MOD_REPLACE | ||
| 71 | unload_lp $MOD_REPLACE | ||
| 72 | |||
| 73 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
| 74 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
| 75 | |||
| 76 | disable_lp $MOD_LIVEPATCH | ||
| 77 | unload_lp $MOD_LIVEPATCH | ||
| 78 | |||
| 79 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
| 80 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
| 81 | |||
| 82 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 83 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 84 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 85 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 86 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 87 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 88 | $MOD_LIVEPATCH: this has been live patched | ||
| 89 | % modprobe $MOD_REPLACE replace=0 | ||
| 90 | livepatch: enabling patch '$MOD_REPLACE' | ||
| 91 | livepatch: '$MOD_REPLACE': initializing patching transition | ||
| 92 | livepatch: '$MOD_REPLACE': starting patching transition | ||
| 93 | livepatch: '$MOD_REPLACE': completing patching transition | ||
| 94 | livepatch: '$MOD_REPLACE': patching complete | ||
| 95 | $MOD_LIVEPATCH: this has been live patched | ||
| 96 | $MOD_REPLACE: this has been live patched | ||
| 97 | % echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled | ||
| 98 | livepatch: '$MOD_REPLACE': initializing unpatching transition | ||
| 99 | livepatch: '$MOD_REPLACE': starting unpatching transition | ||
| 100 | livepatch: '$MOD_REPLACE': completing unpatching transition | ||
| 101 | livepatch: '$MOD_REPLACE': unpatching complete | ||
| 102 | % rmmod $MOD_REPLACE | ||
| 103 | $MOD_LIVEPATCH: this has been live patched | ||
| 104 | % echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled | ||
| 105 | livepatch: '$MOD_LIVEPATCH': initializing unpatching transition | ||
| 106 | livepatch: '$MOD_LIVEPATCH': starting unpatching transition | ||
| 107 | livepatch: '$MOD_LIVEPATCH': completing unpatching transition | ||
| 108 | livepatch: '$MOD_LIVEPATCH': unpatching complete | ||
| 109 | % rmmod $MOD_LIVEPATCH" | ||
| 110 | |||
| 111 | |||
| 112 | # TEST: atomic replace livepatch | ||
| 113 | # - load a livepatch that modifies the output from /proc/cmdline and | ||
| 114 | # verify correct behavior | ||
| 115 | # - load an atomic replace livepatch and verify that only the second is active | ||
| 116 | # - remove the first livepatch and verify that the atomic replace livepatch | ||
| 117 | # is still active | ||
| 118 | # - remove the atomic replace livepatch and verify that none are active | ||
| 119 | |||
| 120 | echo -n "TEST: atomic replace livepatch ... " | ||
| 121 | dmesg -C | ||
| 122 | |||
| 123 | load_lp $MOD_LIVEPATCH | ||
| 124 | |||
| 125 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
| 126 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
| 127 | |||
| 128 | load_lp $MOD_REPLACE replace=1 | ||
| 129 | |||
| 130 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
| 131 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
| 132 | |||
| 133 | unload_lp $MOD_LIVEPATCH | ||
| 134 | |||
| 135 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
| 136 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
| 137 | |||
| 138 | disable_lp $MOD_REPLACE | ||
| 139 | unload_lp $MOD_REPLACE | ||
| 140 | |||
| 141 | grep 'live patched' /proc/cmdline > /dev/kmsg | ||
| 142 | grep 'live patched' /proc/meminfo > /dev/kmsg | ||
| 143 | |||
| 144 | check_result "% modprobe $MOD_LIVEPATCH | ||
| 145 | livepatch: enabling patch '$MOD_LIVEPATCH' | ||
| 146 | livepatch: '$MOD_LIVEPATCH': initializing patching transition | ||
| 147 | livepatch: '$MOD_LIVEPATCH': starting patching transition | ||
| 148 | livepatch: '$MOD_LIVEPATCH': completing patching transition | ||
| 149 | livepatch: '$MOD_LIVEPATCH': patching complete | ||
| 150 | $MOD_LIVEPATCH: this has been live patched | ||
| 151 | % modprobe $MOD_REPLACE replace=1 | ||
| 152 | livepatch: enabling patch '$MOD_REPLACE' | ||
| 153 | livepatch: '$MOD_REPLACE': initializing patching transition | ||
| 154 | livepatch: '$MOD_REPLACE': starting patching transition | ||
| 155 | livepatch: '$MOD_REPLACE': completing patching transition | ||
| 156 | livepatch: '$MOD_REPLACE': patching complete | ||
| 157 | $MOD_REPLACE: this has been live patched | ||
| 158 | % rmmod $MOD_LIVEPATCH | ||
| 159 | $MOD_REPLACE: this has been live patched | ||
| 160 | % echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled | ||
| 161 | livepatch: '$MOD_REPLACE': initializing unpatching transition | ||
| 162 | livepatch: '$MOD_REPLACE': starting unpatching transition | ||
| 163 | livepatch: '$MOD_REPLACE': completing unpatching transition | ||
| 164 | livepatch: '$MOD_REPLACE': unpatching complete | ||
| 165 | % rmmod $MOD_REPLACE" | ||
| 166 | |||
| 167 | |||
| 168 | exit 0 | ||
diff --git a/tools/testing/selftests/livepatch/test-shadow-vars.sh b/tools/testing/selftests/livepatch/test-shadow-vars.sh new file mode 100755 index 000000000000..04a37831e204 --- /dev/null +++ b/tools/testing/selftests/livepatch/test-shadow-vars.sh | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # SPDX-License-Identifier: GPL-2.0 | ||
| 3 | # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> | ||
| 4 | |||
| 5 | . $(dirname $0)/functions.sh | ||
| 6 | |||
| 7 | MOD_TEST=test_klp_shadow_vars | ||
| 8 | |||
| 9 | set_dynamic_debug | ||
| 10 | |||
| 11 | |||
| 12 | # TEST: basic shadow variable API | ||
| 13 | # - load a module that exercises the shadow variable API | ||
| 14 | |||
| 15 | echo -n "TEST: basic shadow variable API ... " | ||
| 16 | dmesg -C | ||
| 17 | |||
| 18 | load_mod $MOD_TEST | ||
| 19 | unload_mod $MOD_TEST | ||
| 20 | |||
| 21 | check_result "% modprobe $MOD_TEST | ||
| 22 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0 | ||
| 23 | $MOD_TEST: got expected NULL result | ||
| 24 | $MOD_TEST: shadow_ctor: PTR6 -> PTR1 | ||
| 25 | $MOD_TEST: klp_shadow_alloc(obj=PTR5, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR1 = PTR6 | ||
| 26 | $MOD_TEST: shadow_ctor: PTR8 -> PTR2 | ||
| 27 | $MOD_TEST: klp_shadow_alloc(obj=PTR9, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR2 = PTR8 | ||
| 28 | $MOD_TEST: shadow_ctor: PTR10 -> PTR3 | ||
| 29 | $MOD_TEST: klp_shadow_alloc(obj=PTR5, id=0x1235, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR3 = PTR10 | ||
| 30 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR6 | ||
| 31 | $MOD_TEST: got expected PTR6 -> PTR1 result | ||
| 32 | $MOD_TEST: klp_shadow_get(obj=PTR9, id=0x1234) = PTR8 | ||
| 33 | $MOD_TEST: got expected PTR8 -> PTR2 result | ||
| 34 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1235) = PTR10 | ||
| 35 | $MOD_TEST: got expected PTR10 -> PTR3 result | ||
| 36 | $MOD_TEST: shadow_ctor: PTR11 -> PTR4 | ||
| 37 | $MOD_TEST: klp_shadow_get_or_alloc(obj=PTR12, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR4 = PTR11 | ||
| 38 | $MOD_TEST: klp_shadow_get_or_alloc(obj=PTR12, id=0x1234, size=8, gfp_flags=GFP_KERNEL), ctor=PTR7, ctor_data=PTR4 = PTR11 | ||
| 39 | $MOD_TEST: got expected PTR11 -> PTR4 result | ||
| 40 | $MOD_TEST: shadow_dtor(obj=PTR5, shadow_data=PTR6) | ||
| 41 | $MOD_TEST: klp_shadow_free(obj=PTR5, id=0x1234, dtor=PTR13) | ||
| 42 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0 | ||
| 43 | $MOD_TEST: got expected NULL result | ||
| 44 | $MOD_TEST: shadow_dtor(obj=PTR9, shadow_data=PTR8) | ||
| 45 | $MOD_TEST: klp_shadow_free(obj=PTR9, id=0x1234, dtor=PTR13) | ||
| 46 | $MOD_TEST: klp_shadow_get(obj=PTR9, id=0x1234) = PTR0 | ||
| 47 | $MOD_TEST: got expected NULL result | ||
| 48 | $MOD_TEST: shadow_dtor(obj=PTR12, shadow_data=PTR11) | ||
| 49 | $MOD_TEST: klp_shadow_free(obj=PTR12, id=0x1234, dtor=PTR13) | ||
| 50 | $MOD_TEST: klp_shadow_get(obj=PTR12, id=0x1234) = PTR0 | ||
| 51 | $MOD_TEST: got expected NULL result | ||
| 52 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1235) = PTR10 | ||
| 53 | $MOD_TEST: got expected PTR10 -> PTR3 result | ||
| 54 | $MOD_TEST: shadow_dtor(obj=PTR5, shadow_data=PTR10) | ||
| 55 | $MOD_TEST: klp_shadow_free_all(id=0x1235, dtor=PTR13) | ||
| 56 | $MOD_TEST: klp_shadow_get(obj=PTR5, id=0x1234) = PTR0 | ||
| 57 | $MOD_TEST: shadow_get() got expected NULL result | ||
| 58 | % rmmod test_klp_shadow_vars" | ||
| 59 | |||
| 60 | exit 0 | ||
