diff options
Diffstat (limited to 'tools/testing/selftests/lkdtm')
-rw-r--r-- | tools/testing/selftests/lkdtm/.gitignore | 2 | ||||
-rw-r--r-- | tools/testing/selftests/lkdtm/Makefile | 12 | ||||
-rw-r--r-- | tools/testing/selftests/lkdtm/config | 1 | ||||
-rwxr-xr-x | tools/testing/selftests/lkdtm/run.sh | 104 | ||||
-rw-r--r-- | tools/testing/selftests/lkdtm/tests.txt | 70 |
5 files changed, 189 insertions, 0 deletions
diff --git a/tools/testing/selftests/lkdtm/.gitignore b/tools/testing/selftests/lkdtm/.gitignore new file mode 100644 index 000000000..f26212605 --- /dev/null +++ b/tools/testing/selftests/lkdtm/.gitignore @@ -0,0 +1,2 @@ +*.sh +!run.sh diff --git a/tools/testing/selftests/lkdtm/Makefile b/tools/testing/selftests/lkdtm/Makefile new file mode 100644 index 000000000..1bcc9ee99 --- /dev/null +++ b/tools/testing/selftests/lkdtm/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for LKDTM regression tests + +include ../lib.mk + +# NOTE: $(OUTPUT) won't get default value if used before lib.mk +TEST_FILES := tests.txt +TEST_GEN_PROGS = $(patsubst %,$(OUTPUT)/%.sh,$(shell awk '{print $$1}' tests.txt | sed -e 's/\#//')) +all: $(TEST_GEN_PROGS) + +$(OUTPUT)/%: run.sh tests.txt + install -m 0744 run.sh $@ diff --git a/tools/testing/selftests/lkdtm/config b/tools/testing/selftests/lkdtm/config new file mode 100644 index 000000000..d874990e4 --- /dev/null +++ b/tools/testing/selftests/lkdtm/config @@ -0,0 +1 @@ +CONFIG_LKDTM=y diff --git a/tools/testing/selftests/lkdtm/run.sh b/tools/testing/selftests/lkdtm/run.sh new file mode 100755 index 000000000..e95e79bd3 --- /dev/null +++ b/tools/testing/selftests/lkdtm/run.sh @@ -0,0 +1,104 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# This reads tests.txt for the list of LKDTM tests to invoke. Any marked +# with a leading "#" are skipped. The rest of the line after the +# test name is either the text to look for in dmesg for a "success", +# or the rationale for why a test is marked to be skipped. +# +set -e +TRIGGER=/sys/kernel/debug/provoke-crash/DIRECT +CLEAR_ONCE=/sys/kernel/debug/clear_warn_once +KSELFTEST_SKIP_TEST=4 + +# Verify we have LKDTM available in the kernel. +if [ ! -r $TRIGGER ] ; then + /sbin/modprobe -q lkdtm || true + if [ ! -r $TRIGGER ] ; then + echo "Cannot find $TRIGGER (missing CONFIG_LKDTM?)" + else + echo "Cannot write $TRIGGER (need to run as root?)" + fi + # Skip this test + exit $KSELFTEST_SKIP_TEST +fi + +# Figure out which test to run from our script name. +test=$(basename $0 .sh) +# Look up details about the test from master list of LKDTM tests. +line=$(grep -E '^#?'"$test"'\b' tests.txt) +if [ -z "$line" ]; then + echo "Skipped: missing test '$test' in tests.txt" + exit $KSELFTEST_SKIP_TEST +fi +# Check that the test is known to LKDTM. +if ! grep -E -q '^'"$test"'$' "$TRIGGER" ; then + echo "Skipped: test '$test' missing in $TRIGGER!" + exit $KSELFTEST_SKIP_TEST +fi + +# Extract notes/expected output from test list. +test=$(echo "$line" | cut -d" " -f1) +if echo "$line" | grep -q ' ' ; then + expect=$(echo "$line" | cut -d" " -f2-) +else + expect="" +fi + +# If the test is commented out, report a skip +if echo "$test" | grep -q '^#' ; then + test=$(echo "$test" | cut -c2-) + if [ -z "$expect" ]; then + expect="crashes entire system" + fi + echo "Skipping $test: $expect" + exit $KSELFTEST_SKIP_TEST +fi + +# If no expected output given, assume an Oops with back trace is success. +if [ -z "$expect" ]; then + expect="call trace:" +fi + +# Prepare log for report checking +LOG=$(mktemp --tmpdir -t lkdtm-log-XXXXXX) +DMESG=$(mktemp --tmpdir -t lkdtm-dmesg-XXXXXX) +cleanup() { + rm -f "$LOG" "$DMESG" +} +trap cleanup EXIT + +# Reset WARN_ONCE counters so we trip it each time this runs. +if [ -w $CLEAR_ONCE ] ; then + echo 1 > $CLEAR_ONCE +fi + +# Save existing dmesg so we can detect new content below +dmesg > "$DMESG" + +# Since the kernel is likely killing the process writing to the trigger +# file, it must not be the script's shell itself. i.e. we cannot do: +# echo "$test" >"$TRIGGER" +# Instead, use "cat" to take the signal. Since the shell will yell about +# the signal that killed the subprocess, we must ignore the failure and +# continue. However we don't silence stderr since there might be other +# useful details reported there in the case of other unexpected conditions. +echo "$test" | cat >"$TRIGGER" || true + +# Record and dump the results +dmesg | comm --nocheck-order -13 "$DMESG" - > "$LOG" || true + +cat "$LOG" +# Check for expected output +if grep -E -qi "$expect" "$LOG" ; then + echo "$test: saw '$expect': ok" + exit 0 +else + if grep -E -qi XFAIL: "$LOG" ; then + echo "$test: saw 'XFAIL': [SKIP]" + exit $KSELFTEST_SKIP_TEST + else + echo "$test: missing '$expect': [FAIL]" + exit 1 + fi +fi diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt new file mode 100644 index 000000000..9b84cba9e --- /dev/null +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -0,0 +1,70 @@ +#PANIC +BUG kernel BUG at +WARNING WARNING: +WARNING_MESSAGE message trigger +EXCEPTION +#LOOP Hangs the system +#EXHAUST_STACK Corrupts memory on failure +#CORRUPT_STACK Crashes entire system on success +#CORRUPT_STACK_STRONG Crashes entire system on success +CORRUPT_LIST_ADD list_add corruption +CORRUPT_LIST_DEL list_del corruption +STACK_GUARD_PAGE_LEADING +STACK_GUARD_PAGE_TRAILING +UNSET_SMEP pinned CR4 bits changed: +DOUBLE_FAULT +CORRUPT_PAC +UNALIGNED_LOAD_STORE_WRITE +#OVERWRITE_ALLOCATION Corrupts memory on failure +#WRITE_AFTER_FREE Corrupts memory on failure +READ_AFTER_FREE +#WRITE_BUDDY_AFTER_FREE Corrupts memory on failure +READ_BUDDY_AFTER_FREE +SLAB_FREE_DOUBLE +SLAB_FREE_CROSS +SLAB_FREE_PAGE +#SOFTLOCKUP Hangs the system +#HARDLOCKUP Hangs the system +#SPINLOCKUP Hangs the system +#HUNG_TASK Hangs the system +EXEC_DATA +EXEC_STACK +EXEC_KMALLOC +EXEC_VMALLOC +EXEC_RODATA +EXEC_USERSPACE +EXEC_NULL +ACCESS_USERSPACE +ACCESS_NULL +WRITE_RO +WRITE_RO_AFTER_INIT +WRITE_KERN +REFCOUNT_INC_OVERFLOW +REFCOUNT_ADD_OVERFLOW +REFCOUNT_INC_NOT_ZERO_OVERFLOW +REFCOUNT_ADD_NOT_ZERO_OVERFLOW +REFCOUNT_DEC_ZERO +REFCOUNT_DEC_NEGATIVE Negative detected: saturated +REFCOUNT_DEC_AND_TEST_NEGATIVE Negative detected: saturated +REFCOUNT_SUB_AND_TEST_NEGATIVE Negative detected: saturated +REFCOUNT_INC_ZERO +REFCOUNT_ADD_ZERO +REFCOUNT_INC_SATURATED Saturation detected: still saturated +REFCOUNT_DEC_SATURATED Saturation detected: still saturated +REFCOUNT_ADD_SATURATED Saturation detected: still saturated +REFCOUNT_INC_NOT_ZERO_SATURATED +REFCOUNT_ADD_NOT_ZERO_SATURATED +REFCOUNT_DEC_AND_TEST_SATURATED Saturation detected: still saturated +REFCOUNT_SUB_AND_TEST_SATURATED Saturation detected: still saturated +#REFCOUNT_TIMING timing only +#ATOMIC_TIMING timing only +USERCOPY_HEAP_SIZE_TO +USERCOPY_HEAP_SIZE_FROM +USERCOPY_HEAP_WHITELIST_TO +USERCOPY_HEAP_WHITELIST_FROM +USERCOPY_STACK_FRAME_TO +USERCOPY_STACK_FRAME_FROM +USERCOPY_STACK_BEYOND +USERCOPY_KERNEL +STACKLEAK_ERASING OK: the rest of the thread stack is properly erased +CFI_FORWARD_PROTO |