diff options
Diffstat (limited to 'tools/testing/selftests/lkdtm')
-rw-r--r-- | tools/testing/selftests/lkdtm/.gitignore | 1 | ||||
-rw-r--r-- | tools/testing/selftests/lkdtm/Makefile | 1 | ||||
-rw-r--r-- | tools/testing/selftests/lkdtm/config | 13 | ||||
-rwxr-xr-x | tools/testing/selftests/lkdtm/run.sh | 20 | ||||
-rwxr-xr-x | tools/testing/selftests/lkdtm/stack-entropy.sh | 51 | ||||
-rw-r--r-- | tools/testing/selftests/lkdtm/tests.txt | 31 |
6 files changed, 105 insertions, 12 deletions
diff --git a/tools/testing/selftests/lkdtm/.gitignore b/tools/testing/selftests/lkdtm/.gitignore index f26212605b6b..d4b0be857deb 100644 --- a/tools/testing/selftests/lkdtm/.gitignore +++ b/tools/testing/selftests/lkdtm/.gitignore @@ -1,2 +1,3 @@ *.sh !run.sh +!stack-entropy.sh diff --git a/tools/testing/selftests/lkdtm/Makefile b/tools/testing/selftests/lkdtm/Makefile index 1bcc9ee990eb..c71109ceeb2d 100644 --- a/tools/testing/selftests/lkdtm/Makefile +++ b/tools/testing/selftests/lkdtm/Makefile @@ -5,6 +5,7 @@ include ../lib.mk # NOTE: $(OUTPUT) won't get default value if used before lib.mk TEST_FILES := tests.txt +TEST_PROGS := stack-entropy.sh TEST_GEN_PROGS = $(patsubst %,$(OUTPUT)/%.sh,$(shell awk '{print $$1}' tests.txt | sed -e 's/\#//')) all: $(TEST_GEN_PROGS) diff --git a/tools/testing/selftests/lkdtm/config b/tools/testing/selftests/lkdtm/config index d874990e442b..7afe05e8c4d7 100644 --- a/tools/testing/selftests/lkdtm/config +++ b/tools/testing/selftests/lkdtm/config @@ -1 +1,14 @@ CONFIG_LKDTM=y +CONFIG_DEBUG_LIST=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_FORTIFY_SOURCE=y +CONFIG_GCC_PLUGIN_STACKLEAK=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y +CONFIG_INIT_ON_FREE_DEFAULT_ON=y +CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y +CONFIG_UBSAN=y +CONFIG_UBSAN_BOUNDS=y +CONFIG_STACKPROTECTOR_STRONG=y +CONFIG_SLUB_DEBUG=y +CONFIG_SLUB_DEBUG_ON=y diff --git a/tools/testing/selftests/lkdtm/run.sh b/tools/testing/selftests/lkdtm/run.sh index bb7a1775307b..95e904959207 100755 --- a/tools/testing/selftests/lkdtm/run.sh +++ b/tools/testing/selftests/lkdtm/run.sh @@ -56,8 +56,14 @@ if echo "$test" | grep -q '^#' ; then fi # If no expected output given, assume an Oops with back trace is success. +repeat=1 if [ -z "$expect" ]; then expect="call trace:" +else + if echo "$expect" | grep -q '^repeat:' ; then + repeat=$(echo "$expect" | cut -d' ' -f1 | cut -d: -f2) + expect=$(echo "$expect" | cut -d' ' -f2-) + fi fi # Prepare log for report checking @@ -76,10 +82,16 @@ fi # Save existing dmesg so we can detect new content below dmesg > "$DMESG" -# Most shells yell about signals and we're expecting the "cat" process -# to usually be killed by the kernel. So we have to run it in a sub-shell -# and silence errors. -($SHELL -c 'cat <(echo '"$test"') >'"$TRIGGER" 2>/dev/null) || true +# 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. +for i in $(seq 1 $repeat); do + echo "$test" | cat >"$TRIGGER" || true +done # Record and dump the results dmesg | comm --nocheck-order -13 "$DMESG" - > "$LOG" || true diff --git a/tools/testing/selftests/lkdtm/stack-entropy.sh b/tools/testing/selftests/lkdtm/stack-entropy.sh new file mode 100755 index 000000000000..14fedeef762e --- /dev/null +++ b/tools/testing/selftests/lkdtm/stack-entropy.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Measure kernel stack entropy by sampling via LKDTM's REPORT_STACK test. +set -e +samples="${1:-1000}" +TRIGGER=/sys/kernel/debug/provoke-crash/DIRECT +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 + +# Capture dmesg continuously since it may fill up depending on sample size. +log=$(mktemp -t stack-entropy-XXXXXX) +dmesg --follow >"$log" & pid=$! +report=-1 +for i in $(seq 1 $samples); do + echo "REPORT_STACK" > $TRIGGER + if [ -t 1 ]; then + percent=$(( 100 * $i / $samples )) + if [ "$percent" -ne "$report" ]; then + /bin/echo -en "$percent%\r" + report="$percent" + fi + fi +done +kill "$pid" + +# Count unique offsets since last run. +seen=$(tac "$log" | grep -m1 -B"$samples"0 'Starting stack offset' | \ + grep 'Stack offset' | awk '{print $NF}' | sort | uniq -c | wc -l) +bits=$(echo "obase=2; $seen" | bc | wc -L) +echo "Bits of stack entropy: $bits" +rm -f "$log" + +# We would expect any functional stack randomization to be at least 5 bits. +if [ "$bits" -lt 5 ]; then + echo "Stack entropy is low! Booted without 'randomize_kstack_offset=y'?" + exit 1 +else + exit 0 +fi diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index 74a8d329a72c..cff124c1eddd 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -1,4 +1,5 @@ #PANIC +#PANIC_STOP_IRQOFF Crashes entire system BUG kernel BUG at WARNING WARNING: WARNING_MESSAGE message trigger @@ -7,24 +8,30 @@ EXCEPTION #EXHAUST_STACK Corrupts memory on failure #CORRUPT_STACK Crashes entire system on success #CORRUPT_STACK_STRONG Crashes entire system on success +ARRAY_BOUNDS call trace:|UBSAN: array-index-out-of-bounds CORRUPT_LIST_ADD list_add corruption CORRUPT_LIST_DEL list_del corruption STACK_GUARD_PAGE_LEADING STACK_GUARD_PAGE_TRAILING -UNSET_SMEP CR4 bits went missing +REPORT_STACK_CANARY repeat:2 ok: stack canaries differ +UNSET_SMEP pinned CR4 bits changed: DOUBLE_FAULT CORRUPT_PAC UNALIGNED_LOAD_STORE_WRITE -#OVERWRITE_ALLOCATION Corrupts memory on failure +SLAB_LINEAR_OVERFLOW +VMALLOC_LINEAR_OVERFLOW #WRITE_AFTER_FREE Corrupts memory on failure -READ_AFTER_FREE +READ_AFTER_FREE call trace:|Memory correctly poisoned #WRITE_BUDDY_AFTER_FREE Corrupts memory on failure -READ_BUDDY_AFTER_FREE +READ_BUDDY_AFTER_FREE call trace:|Memory correctly poisoned +SLAB_INIT_ON_ALLOC Memory appears initialized +BUDDY_INIT_ON_ALLOC Memory appears initialized SLAB_FREE_DOUBLE SLAB_FREE_CROSS SLAB_FREE_PAGE #SOFTLOCKUP Hangs the system #HARDLOCKUP Hangs the system +#SMP_CALL_LOCKUP Hangs the system #SPINLOCKUP Hangs the system #HUNG_TASK Hangs the system EXEC_DATA @@ -39,6 +46,7 @@ ACCESS_NULL WRITE_RO WRITE_RO_AFTER_INIT WRITE_KERN +WRITE_OPD REFCOUNT_INC_OVERFLOW REFCOUNT_ADD_OVERFLOW REFCOUNT_INC_NOT_ZERO_OVERFLOW @@ -58,13 +66,20 @@ 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_SLAB_SIZE_TO +USERCOPY_SLAB_SIZE_FROM +USERCOPY_SLAB_WHITELIST_TO +USERCOPY_SLAB_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 +CFI_BACKWARD call trace:|ok: control flow unchanged +FORTIFY_STRSCPY detected buffer overflow +FORTIFY_STR_OBJECT detected buffer overflow +FORTIFY_STR_MEMBER detected buffer overflow +FORTIFY_MEM_OBJECT detected buffer overflow +FORTIFY_MEM_MEMBER detected field-spanning write +PPC_SLB_MULTIHIT Recovered |