1 #!/bin/bash 1 #!/bin/bash 2 # SPDX-License-Identifier: GPL-2.0 2 # SPDX-License-Identifier: GPL-2.0 3 # Copyright (C) 2018 Joe Lawrence <joe.lawrence 3 # Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> 4 4 5 # Shell functions for the rest of the scripts. 5 # Shell functions for the rest of the scripts. 6 6 7 MAX_RETRIES=600 7 MAX_RETRIES=600 8 RETRY_INTERVAL=".1" # seconds 8 RETRY_INTERVAL=".1" # seconds 9 KLP_SYSFS_DIR="/sys/kernel/livepatch" 9 KLP_SYSFS_DIR="/sys/kernel/livepatch" 10 10 11 # Kselftest framework requirement - SKIP code 11 # Kselftest framework requirement - SKIP code is 4 12 ksft_skip=4 12 ksft_skip=4 13 13 14 # log(msg) - write message to kernel log 14 # log(msg) - write message to kernel log 15 # msg - insightful words 15 # msg - insightful words 16 function log() { 16 function log() { 17 echo "$1" > /dev/kmsg 17 echo "$1" > /dev/kmsg 18 } 18 } 19 19 20 # skip(msg) - testing can't proceed 20 # skip(msg) - testing can't proceed 21 # msg - explanation 21 # msg - explanation 22 function skip() { 22 function skip() { 23 log "SKIP: $1" 23 log "SKIP: $1" 24 echo "SKIP: $1" >&2 24 echo "SKIP: $1" >&2 25 exit $ksft_skip 25 exit $ksft_skip 26 } 26 } 27 27 28 # root test 28 # root test 29 function is_root() { 29 function is_root() { 30 uid=$(id -u) 30 uid=$(id -u) 31 if [ $uid -ne 0 ]; then 31 if [ $uid -ne 0 ]; then 32 echo "skip all tests: must be 32 echo "skip all tests: must be run as root" >&2 33 exit $ksft_skip 33 exit $ksft_skip 34 fi 34 fi 35 } 35 } 36 36 37 # Check if we can compile the modules before l 37 # Check if we can compile the modules before loading them 38 function has_kdir() { 38 function has_kdir() { 39 if [ -z "$KDIR" ]; then 39 if [ -z "$KDIR" ]; then 40 KDIR="/lib/modules/$(uname -r) 40 KDIR="/lib/modules/$(uname -r)/build" 41 fi 41 fi 42 42 43 if [ ! -d "$KDIR" ]; then 43 if [ ! -d "$KDIR" ]; then 44 echo "skip all tests: KDIR ($K 44 echo "skip all tests: KDIR ($KDIR) not available to compile modules." 45 exit $ksft_skip 45 exit $ksft_skip 46 fi 46 fi 47 } 47 } 48 48 49 # die(msg) - game over, man 49 # die(msg) - game over, man 50 # msg - dying words 50 # msg - dying words 51 function die() { 51 function die() { 52 log "ERROR: $1" 52 log "ERROR: $1" 53 echo "ERROR: $1" >&2 53 echo "ERROR: $1" >&2 54 exit 1 54 exit 1 55 } 55 } 56 56 57 function push_config() { 57 function push_config() { 58 DYNAMIC_DEBUG=$(grep '^kernel/livepatc 58 DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \ 59 awk -F'[: ]' '{print " 59 awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}') 60 FTRACE_ENABLED=$(sysctl --values kerne 60 FTRACE_ENABLED=$(sysctl --values kernel.ftrace_enabled) 61 } 61 } 62 62 63 function pop_config() { 63 function pop_config() { 64 if [[ -n "$DYNAMIC_DEBUG" ]]; then 64 if [[ -n "$DYNAMIC_DEBUG" ]]; then 65 echo -n "$DYNAMIC_DEBUG" > /sy 65 echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control 66 fi 66 fi 67 if [[ -n "$FTRACE_ENABLED" ]]; then 67 if [[ -n "$FTRACE_ENABLED" ]]; then 68 sysctl kernel.ftrace_enabled=" 68 sysctl kernel.ftrace_enabled="$FTRACE_ENABLED" &> /dev/null 69 fi 69 fi 70 } 70 } 71 71 72 function set_dynamic_debug() { 72 function set_dynamic_debug() { 73 cat <<-EOF > /sys/kernel/debug/dynamic 73 cat <<-EOF > /sys/kernel/debug/dynamic_debug/control 74 file kernel/livepatch/* +p 74 file kernel/livepatch/* +p 75 func klp_try_switch_task -p 75 func klp_try_switch_task -p 76 EOF 76 EOF 77 } 77 } 78 78 79 function set_ftrace_enabled() { 79 function set_ftrace_enabled() { 80 local can_fail=0 80 local can_fail=0 81 if [[ "$1" == "--fail" ]] ; then 81 if [[ "$1" == "--fail" ]] ; then 82 can_fail=1 82 can_fail=1 83 shift 83 shift 84 fi 84 fi 85 85 86 local err=$(sysctl -q kernel.ftrace_en 86 local err=$(sysctl -q kernel.ftrace_enabled="$1" 2>&1) 87 local result=$(sysctl --values kernel. 87 local result=$(sysctl --values kernel.ftrace_enabled) 88 88 89 if [[ "$result" != "$1" ]] ; then 89 if [[ "$result" != "$1" ]] ; then 90 if [[ $can_fail -eq 1 ]] ; the 90 if [[ $can_fail -eq 1 ]] ; then 91 echo "livepatch: $err" 91 echo "livepatch: $err" | sed 's#/proc/sys/kernel/#kernel.#' > /dev/kmsg 92 return 92 return 93 fi 93 fi 94 94 95 skip "failed to set kernel.ftr 95 skip "failed to set kernel.ftrace_enabled = $1" 96 fi 96 fi 97 97 98 echo "livepatch: kernel.ftrace_enabled 98 echo "livepatch: kernel.ftrace_enabled = $result" > /dev/kmsg 99 } 99 } 100 100 101 function cleanup() { 101 function cleanup() { 102 pop_config 102 pop_config 103 } 103 } 104 104 105 # setup_config - save the current config and s 105 # setup_config - save the current config and set a script exit trap that 106 # restores the original config. 106 # restores the original config. Setup the dynamic debug 107 # for verbose livepatching outp 107 # for verbose livepatching output and turn on 108 # the ftrace_enabled sysctl. 108 # the ftrace_enabled sysctl. 109 function setup_config() { 109 function setup_config() { 110 is_root 110 is_root 111 has_kdir 111 has_kdir 112 push_config 112 push_config 113 set_dynamic_debug 113 set_dynamic_debug 114 set_ftrace_enabled 1 114 set_ftrace_enabled 1 115 trap cleanup EXIT INT TERM HUP 115 trap cleanup EXIT INT TERM HUP 116 } 116 } 117 117 118 # loop_until(cmd) - loop a command until it is 118 # loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES, 119 # sleep $RETRY_INTERVAL betw 119 # sleep $RETRY_INTERVAL between attempts 120 # cmd - command and its arguments to run 120 # cmd - command and its arguments to run 121 function loop_until() { 121 function loop_until() { 122 local cmd="$*" 122 local cmd="$*" 123 local i=0 123 local i=0 124 while true; do 124 while true; do 125 eval "$cmd" && return 0 125 eval "$cmd" && return 0 126 [[ $((i++)) -eq $MAX_RETRIES ] 126 [[ $((i++)) -eq $MAX_RETRIES ]] && return 1 127 sleep $RETRY_INTERVAL 127 sleep $RETRY_INTERVAL 128 done 128 done 129 } 129 } 130 130 131 function is_livepatch_mod() { 131 function is_livepatch_mod() { 132 local mod="$1" 132 local mod="$1" 133 133 134 if [[ ! -f "test_modules/$mod.ko" ]]; 134 if [[ ! -f "test_modules/$mod.ko" ]]; then 135 die "Can't find \"test_modules 135 die "Can't find \"test_modules/$mod.ko\", try \"make\"" 136 fi 136 fi 137 137 138 if [[ $(modinfo "test_modules/$mod.ko" 138 if [[ $(modinfo "test_modules/$mod.ko" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then 139 return 0 139 return 0 140 fi 140 fi 141 141 142 return 1 142 return 1 143 } 143 } 144 144 145 function __load_mod() { 145 function __load_mod() { 146 local mod="$1"; shift 146 local mod="$1"; shift 147 147 148 local msg="% insmod test_modules/$mod. 148 local msg="% insmod test_modules/$mod.ko $*" 149 log "${msg%% }" 149 log "${msg%% }" 150 ret=$(insmod "test_modules/$mod.ko" "$ 150 ret=$(insmod "test_modules/$mod.ko" "$@" 2>&1) 151 if [[ "$ret" != "" ]]; then 151 if [[ "$ret" != "" ]]; then 152 die "$ret" 152 die "$ret" 153 fi 153 fi 154 154 155 # Wait for module in sysfs ... 155 # Wait for module in sysfs ... 156 loop_until '[[ -e "/sys/module/$mod" ] 156 loop_until '[[ -e "/sys/module/$mod" ]]' || 157 die "failed to load module $mo 157 die "failed to load module $mod" 158 } 158 } 159 159 160 160 161 # load_mod(modname, params) - load a kernel mo 161 # load_mod(modname, params) - load a kernel module 162 # modname - module name to load 162 # modname - module name to load 163 # params - module parameters to pass to 163 # params - module parameters to pass to insmod 164 function load_mod() { 164 function load_mod() { 165 local mod="$1"; shift 165 local mod="$1"; shift 166 166 167 is_livepatch_mod "$mod" && 167 is_livepatch_mod "$mod" && 168 die "use load_lp() to load the 168 die "use load_lp() to load the livepatch module $mod" 169 169 170 __load_mod "$mod" "$@" 170 __load_mod "$mod" "$@" 171 } 171 } 172 172 173 # load_lp_nowait(modname, params) - load a ker 173 # load_lp_nowait(modname, params) - load a kernel module with a livepatch 174 # but do not wait on unt 174 # but do not wait on until the transition finishes 175 # modname - module name to load 175 # modname - module name to load 176 # params - module parameters to pass to 176 # params - module parameters to pass to insmod 177 function load_lp_nowait() { 177 function load_lp_nowait() { 178 local mod="$1"; shift 178 local mod="$1"; shift 179 179 180 is_livepatch_mod "$mod" || 180 is_livepatch_mod "$mod" || 181 die "module $mod is not a live 181 die "module $mod is not a livepatch" 182 182 183 __load_mod "$mod" "$@" 183 __load_mod "$mod" "$@" 184 184 185 # Wait for livepatch in sysfs ... 185 # Wait for livepatch in sysfs ... 186 loop_until '[[ -e "/sys/kernel/livepat 186 loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' || 187 die "failed to load module $mo 187 die "failed to load module $mod (sysfs)" 188 } 188 } 189 189 190 # load_lp(modname, params) - load a kernel mod 190 # load_lp(modname, params) - load a kernel module with a livepatch 191 # modname - module name to load 191 # modname - module name to load 192 # params - module parameters to pass to 192 # params - module parameters to pass to insmod 193 function load_lp() { 193 function load_lp() { 194 local mod="$1"; shift 194 local mod="$1"; shift 195 195 196 load_lp_nowait "$mod" "$@" 196 load_lp_nowait "$mod" "$@" 197 197 198 # Wait until the transition finishes . 198 # Wait until the transition finishes ... 199 loop_until 'grep -q '^0$' /sys/kernel/ 199 loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' || 200 die "failed to complete transi 200 die "failed to complete transition" 201 } 201 } 202 202 203 # load_failing_mod(modname, params) - load a k 203 # load_failing_mod(modname, params) - load a kernel module, expect to fail 204 # modname - module name to load 204 # modname - module name to load 205 # params - module parameters to pass to 205 # params - module parameters to pass to insmod 206 function load_failing_mod() { 206 function load_failing_mod() { 207 local mod="$1"; shift 207 local mod="$1"; shift 208 208 209 local msg="% insmod test_modules/$mod. 209 local msg="% insmod test_modules/$mod.ko $*" 210 log "${msg%% }" 210 log "${msg%% }" 211 ret=$(insmod "test_modules/$mod.ko" "$ 211 ret=$(insmod "test_modules/$mod.ko" "$@" 2>&1) 212 if [[ "$ret" == "" ]]; then 212 if [[ "$ret" == "" ]]; then 213 die "$mod unexpectedly loaded" 213 die "$mod unexpectedly loaded" 214 fi 214 fi 215 log "$ret" 215 log "$ret" 216 } 216 } 217 217 218 # unload_mod(modname) - unload a kernel module 218 # unload_mod(modname) - unload a kernel module 219 # modname - module name to unload 219 # modname - module name to unload 220 function unload_mod() { 220 function unload_mod() { 221 local mod="$1" 221 local mod="$1" 222 222 223 # Wait for module reference count to c 223 # Wait for module reference count to clear ... 224 loop_until '[[ $(cat "/sys/module/$mod 224 loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' || 225 die "failed to unload module $ 225 die "failed to unload module $mod (refcnt)" 226 226 227 log "% rmmod $mod" 227 log "% rmmod $mod" 228 ret=$(rmmod "$mod" 2>&1) 228 ret=$(rmmod "$mod" 2>&1) 229 if [[ "$ret" != "" ]]; then 229 if [[ "$ret" != "" ]]; then 230 die "$ret" 230 die "$ret" 231 fi 231 fi 232 232 233 # Wait for module in sysfs ... 233 # Wait for module in sysfs ... 234 loop_until '[[ ! -e "/sys/module/$mod" 234 loop_until '[[ ! -e "/sys/module/$mod" ]]' || 235 die "failed to unload module $ 235 die "failed to unload module $mod (/sys/module)" 236 } 236 } 237 237 238 # unload_lp(modname) - unload a kernel module 238 # unload_lp(modname) - unload a kernel module with a livepatch 239 # modname - module name to unload 239 # modname - module name to unload 240 function unload_lp() { 240 function unload_lp() { 241 unload_mod "$1" 241 unload_mod "$1" 242 } 242 } 243 243 244 # disable_lp(modname) - disable a livepatch 244 # disable_lp(modname) - disable a livepatch 245 # modname - module name to unload 245 # modname - module name to unload 246 function disable_lp() { 246 function disable_lp() { 247 local mod="$1" 247 local mod="$1" 248 248 249 log "% echo 0 > /sys/kernel/livepatch/ 249 log "% echo 0 > /sys/kernel/livepatch/$mod/enabled" 250 echo 0 > /sys/kernel/livepatch/"$mod"/ 250 echo 0 > /sys/kernel/livepatch/"$mod"/enabled 251 251 252 # Wait until the transition finishes a 252 # Wait until the transition finishes and the livepatch gets 253 # removed from sysfs... 253 # removed from sysfs... 254 loop_until '[[ ! -e "/sys/kernel/livep 254 loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' || 255 die "failed to disable livepat 255 die "failed to disable livepatch $mod" 256 } 256 } 257 257 258 # set_pre_patch_ret(modname, pre_patch_ret) 258 # set_pre_patch_ret(modname, pre_patch_ret) 259 # modname - module name to set 259 # modname - module name to set 260 # pre_patch_ret - new pre_patch_ret valu 260 # pre_patch_ret - new pre_patch_ret value 261 function set_pre_patch_ret { 261 function set_pre_patch_ret { 262 local mod="$1"; shift 262 local mod="$1"; shift 263 local ret="$1" 263 local ret="$1" 264 264 265 log "% echo $ret > /sys/module/$mod/pa 265 log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret" 266 echo "$ret" > /sys/module/"$mod"/param 266 echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret 267 267 268 # Wait for sysfs value to hold ... 268 # Wait for sysfs value to hold ... 269 loop_until '[[ $(cat "/sys/module/$mod 269 loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' || 270 die "failed to set pre_patch_r 270 die "failed to set pre_patch_ret parameter for $mod module" 271 } 271 } 272 272 273 function start_test { 273 function start_test { 274 local test="$1" 274 local test="$1" 275 275 276 # Dump something unique into the dmesg 276 # Dump something unique into the dmesg log, then stash the entry 277 # in LAST_DMESG. The check_result() f 277 # in LAST_DMESG. The check_result() function will use it to 278 # find new kernel messages since the t 278 # find new kernel messages since the test started. 279 local last_dmesg_msg="livepatch kselft 279 local last_dmesg_msg="livepatch kselftest timestamp: $(date --rfc-3339=ns)" 280 log "$last_dmesg_msg" 280 log "$last_dmesg_msg" 281 loop_until 'dmesg | grep -q "$last_dme 281 loop_until 'dmesg | grep -q "$last_dmesg_msg"' || 282 die "buffer busy? can't find c 282 die "buffer busy? can't find canary dmesg message: $last_dmesg_msg" 283 LAST_DMESG=$(dmesg | grep "$last_dmesg 283 LAST_DMESG=$(dmesg | grep "$last_dmesg_msg") 284 284 285 echo -n "TEST: $test ... " 285 echo -n "TEST: $test ... " 286 log "===== TEST: $test =====" 286 log "===== TEST: $test =====" 287 } 287 } 288 288 289 # check_result() - verify dmesg output 289 # check_result() - verify dmesg output 290 # TODO - better filter, out of order msg 290 # TODO - better filter, out of order msgs, etc? 291 function check_result { 291 function check_result { 292 local expect="$*" 292 local expect="$*" 293 local result 293 local result 294 294 295 # Test results include any new dmesg e 295 # Test results include any new dmesg entry since LAST_DMESG, then: 296 # - include lines matching keywords 296 # - include lines matching keywords 297 # - exclude lines matching keywords 297 # - exclude lines matching keywords 298 # - filter out dmesg timestamp prefixe 298 # - filter out dmesg timestamp prefixes 299 result=$(dmesg | awk -v last_dmesg="$L 299 result=$(dmesg | awk -v last_dmesg="$LAST_DMESG" 'p; $0 == last_dmesg { p=1 }' | \ 300 grep -e 'livepatch:' -e 'test 300 grep -e 'livepatch:' -e 'test_klp' | \ 301 grep -v '\(tainting\|taints\) 301 grep -v '\(tainting\|taints\) kernel' | \ 302 sed 's/^\[[ 0-9.]*\] //') 302 sed 's/^\[[ 0-9.]*\] //') 303 303 304 if [[ "$expect" == "$result" ]] ; then 304 if [[ "$expect" == "$result" ]] ; then 305 echo "ok" 305 echo "ok" 306 elif [[ "$result" == "" ]] ; then 306 elif [[ "$result" == "" ]] ; then 307 echo -e "not ok\n\nbuffer over 307 echo -e "not ok\n\nbuffer overrun? can't find canary dmesg entry: $LAST_DMESG\n" 308 die "livepatch kselftest(s) fa 308 die "livepatch kselftest(s) failed" 309 else 309 else 310 echo -e "not ok\n\n$(diff -upr 310 echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n" 311 die "livepatch kselftest(s) fa 311 die "livepatch kselftest(s) failed" 312 fi 312 fi 313 } 313 } 314 314 315 # check_sysfs_rights(modname, rel_path, expect 315 # check_sysfs_rights(modname, rel_path, expected_rights) - check sysfs 316 # path permissions 316 # path permissions 317 # modname - livepatch module creating th 317 # modname - livepatch module creating the sysfs interface 318 # rel_path - relative path of the sysfs 318 # rel_path - relative path of the sysfs interface 319 # expected_rights - expected access righ 319 # expected_rights - expected access rights 320 function check_sysfs_rights() { 320 function check_sysfs_rights() { 321 local mod="$1"; shift 321 local mod="$1"; shift 322 local rel_path="$1"; shift 322 local rel_path="$1"; shift 323 local expected_rights="$1"; shift 323 local expected_rights="$1"; shift 324 324 325 local path="$KLP_SYSFS_DIR/$mod/$rel_p 325 local path="$KLP_SYSFS_DIR/$mod/$rel_path" 326 local rights=$(/bin/stat --format '%A' 326 local rights=$(/bin/stat --format '%A' "$path") 327 if test "$rights" != "$expected_rights 327 if test "$rights" != "$expected_rights" ; then 328 die "Unexpected access rights 328 die "Unexpected access rights of $path: $expected_rights vs. $rights" 329 fi 329 fi 330 } 330 } 331 331 332 # check_sysfs_value(modname, rel_path, expecte 332 # check_sysfs_value(modname, rel_path, expected_value) - check sysfs value 333 # modname - livepatch module creating th 333 # modname - livepatch module creating the sysfs interface 334 # rel_path - relative path of the sysfs 334 # rel_path - relative path of the sysfs interface 335 # expected_value - expected value read f 335 # expected_value - expected value read from the file 336 function check_sysfs_value() { 336 function check_sysfs_value() { 337 local mod="$1"; shift 337 local mod="$1"; shift 338 local rel_path="$1"; shift 338 local rel_path="$1"; shift 339 local expected_value="$1"; shift 339 local expected_value="$1"; shift 340 340 341 local path="$KLP_SYSFS_DIR/$mod/$rel_p 341 local path="$KLP_SYSFS_DIR/$mod/$rel_path" 342 local value=`cat $path` 342 local value=`cat $path` 343 if test "$value" != "$expected_value" 343 if test "$value" != "$expected_value" ; then 344 die "Unexpected value in $path 344 die "Unexpected value in $path: $expected_value vs. $value" 345 fi 345 fi 346 } 346 }
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.