cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kmod.sh (15859B)


      1#!/bin/bash
      2#
      3# Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
      4#
      5# This program is free software; you can redistribute it and/or modify it
      6# under the terms of the GNU General Public License as published by the Free
      7# Software Foundation; either version 2 of the License, or at your option any
      8# later version; or, when distributed separately from the Linux kernel or
      9# when incorporated into other software packages, subject to the following
     10# license:
     11#
     12# This program is free software; you can redistribute it and/or modify it
     13# under the terms of copyleft-next (version 0.3.1 or later) as published
     14# at http://copyleft-next.org/.
     15
     16# This is a stress test script for kmod, the kernel module loader. It uses
     17# test_kmod which exposes a series of knobs for the API for us so we can
     18# tweak each test in userspace rather than in kernelspace.
     19#
     20# The way kmod works is it uses the kernel's usermode helper API to eventually
     21# call /sbin/modprobe. It has a limit of the number of concurrent calls
     22# possible. The kernel interface to load modules is request_module(), however
     23# mount uses get_fs_type(). Both behave slightly differently, but the
     24# differences are important enough to test each call separately. For this
     25# reason test_kmod starts by providing tests for both calls.
     26#
     27# The test driver test_kmod assumes a series of defaults which you can
     28# override by exporting to your environment prior running this script.
     29# For instance this script assumes you do not have xfs loaded upon boot.
     30# If this is false, export DEFAULT_KMOD_FS="ext4" prior to running this
     31# script if the filesystem module you don't have loaded upon bootup
     32# is ext4 instead. Refer to allow_user_defaults() for a list of user
     33# override variables possible.
     34#
     35# You'll want at least 4 GiB of RAM to expect to run these tests
     36# without running out of memory on them. For other requirements refer
     37# to test_reqs()
     38
     39set -e
     40
     41TEST_NAME="kmod"
     42TEST_DRIVER="test_${TEST_NAME}"
     43TEST_DIR=$(dirname $0)
     44
     45# This represents
     46#
     47# TEST_ID:TEST_COUNT:ENABLED
     48#
     49# TEST_ID: is the test id number
     50# TEST_COUNT: number of times we should run the test
     51# ENABLED: 1 if enabled, 0 otherwise
     52#
     53# Once these are enabled please leave them as-is. Write your own test,
     54# we have tons of space.
     55ALL_TESTS="0001:3:1"
     56ALL_TESTS="$ALL_TESTS 0002:3:1"
     57ALL_TESTS="$ALL_TESTS 0003:1:1"
     58ALL_TESTS="$ALL_TESTS 0004:1:1"
     59ALL_TESTS="$ALL_TESTS 0005:10:1"
     60ALL_TESTS="$ALL_TESTS 0006:10:1"
     61ALL_TESTS="$ALL_TESTS 0007:5:1"
     62ALL_TESTS="$ALL_TESTS 0008:150:1"
     63ALL_TESTS="$ALL_TESTS 0009:150:1"
     64ALL_TESTS="$ALL_TESTS 0010:1:1"
     65ALL_TESTS="$ALL_TESTS 0011:1:1"
     66ALL_TESTS="$ALL_TESTS 0012:1:1"
     67ALL_TESTS="$ALL_TESTS 0013:1:1"
     68
     69# Kselftest framework requirement - SKIP code is 4.
     70ksft_skip=4
     71
     72test_modprobe()
     73{
     74       if [ ! -d $DIR ]; then
     75               echo "$0: $DIR not present" >&2
     76               echo "You must have the following enabled in your kernel:" >&2
     77               cat $TEST_DIR/config >&2
     78               exit $ksft_skip
     79       fi
     80}
     81
     82function allow_user_defaults()
     83{
     84	if [ -z $DEFAULT_KMOD_DRIVER ]; then
     85		DEFAULT_KMOD_DRIVER="test_module"
     86	fi
     87
     88	if [ -z $DEFAULT_KMOD_FS ]; then
     89		DEFAULT_KMOD_FS="xfs"
     90	fi
     91
     92	if [ -z $PROC_DIR ]; then
     93		PROC_DIR="/proc/sys/kernel/"
     94	fi
     95
     96	if [ -z $MODPROBE_LIMIT ]; then
     97		MODPROBE_LIMIT=50
     98	fi
     99
    100	if [ -z $DIR ]; then
    101		DIR="/sys/devices/virtual/misc/${TEST_DRIVER}0/"
    102	fi
    103
    104	if [ -z $DEFAULT_NUM_TESTS ]; then
    105		DEFAULT_NUM_TESTS=150
    106	fi
    107
    108	MODPROBE_LIMIT_FILE="${PROC_DIR}/kmod-limit"
    109}
    110
    111test_reqs()
    112{
    113	if ! which modprobe 2> /dev/null > /dev/null; then
    114		echo "$0: You need modprobe installed" >&2
    115		exit $ksft_skip
    116	fi
    117
    118	if ! which kmod 2> /dev/null > /dev/null; then
    119		echo "$0: You need kmod installed" >&2
    120		exit $ksft_skip
    121	fi
    122
    123	# kmod 19 has a bad bug where it returns 0 when modprobe
    124	# gets called *even* if the module was not loaded due to
    125	# some bad heuristics. For details see:
    126	#
    127	# A work around is possible in-kernel but its rather
    128	# complex.
    129	KMOD_VERSION=$(kmod --version | awk '{print $3}')
    130	if [[ $KMOD_VERSION  -le 19 ]]; then
    131		echo "$0: You need at least kmod 20" >&2
    132		echo "kmod <= 19 is buggy, for details see:" >&2
    133		echo "https://git.kernel.org/cgit/utils/kernel/kmod/kmod.git/commit/libkmod/libkmod-module.c?id=fd44a98ae2eb5eb32161088954ab21e58e19dfc4" >&2
    134		exit $ksft_skip
    135	fi
    136
    137	uid=$(id -u)
    138	if [ $uid -ne 0 ]; then
    139		echo $msg must be run as root >&2
    140		exit $ksft_skip
    141	fi
    142}
    143
    144function load_req_mod()
    145{
    146	trap "test_modprobe" EXIT
    147
    148	if [ ! -d $DIR ]; then
    149		# Alanis: "Oh isn't it ironic?"
    150		modprobe $TEST_DRIVER
    151	fi
    152}
    153
    154test_finish()
    155{
    156	echo "$MODPROBE" > /proc/sys/kernel/modprobe
    157	echo "Test completed"
    158}
    159
    160errno_name_to_val()
    161{
    162	case "$1" in
    163	# kmod calls modprobe and upon of a module not found
    164	# modprobe returns just 1... However in the kernel we
    165	# *sometimes* see 256...
    166	MODULE_NOT_FOUND)
    167		echo 256;;
    168	SUCCESS)
    169		echo 0;;
    170	-EPERM)
    171		echo -1;;
    172	-ENOENT)
    173		echo -2;;
    174	-EINVAL)
    175		echo -22;;
    176	-ERR_ANY)
    177		echo -123456;;
    178	*)
    179		echo invalid;;
    180	esac
    181}
    182
    183errno_val_to_name()
    184	case "$1" in
    185	256)
    186		echo MODULE_NOT_FOUND;;
    187	0)
    188		echo SUCCESS;;
    189	-1)
    190		echo -EPERM;;
    191	-2)
    192		echo -ENOENT;;
    193	-22)
    194		echo -EINVAL;;
    195	-123456)
    196		echo -ERR_ANY;;
    197	*)
    198		echo invalid;;
    199	esac
    200
    201config_set_test_case_driver()
    202{
    203	if ! echo -n 1 >$DIR/config_test_case; then
    204		echo "$0: Unable to set to test case to driver" >&2
    205		exit 1
    206	fi
    207}
    208
    209config_set_test_case_fs()
    210{
    211	if ! echo -n 2 >$DIR/config_test_case; then
    212		echo "$0: Unable to set to test case to fs" >&2
    213		exit 1
    214	fi
    215}
    216
    217config_num_threads()
    218{
    219	if ! echo -n $1 >$DIR/config_num_threads; then
    220		echo "$0: Unable to set to number of threads" >&2
    221		exit 1
    222	fi
    223}
    224
    225config_get_modprobe_limit()
    226{
    227	if [[ -f ${MODPROBE_LIMIT_FILE} ]] ; then
    228		MODPROBE_LIMIT=$(cat $MODPROBE_LIMIT_FILE)
    229	fi
    230	echo $MODPROBE_LIMIT
    231}
    232
    233config_num_thread_limit_extra()
    234{
    235	MODPROBE_LIMIT=$(config_get_modprobe_limit)
    236	let EXTRA_LIMIT=$MODPROBE_LIMIT+$1
    237	config_num_threads $EXTRA_LIMIT
    238}
    239
    240# For special characters use printf directly,
    241# refer to kmod_test_0001
    242config_set_driver()
    243{
    244	if ! echo -n $1 >$DIR/config_test_driver; then
    245		echo "$0: Unable to set driver" >&2
    246		exit 1
    247	fi
    248}
    249
    250config_set_fs()
    251{
    252	if ! echo -n $1 >$DIR/config_test_fs; then
    253		echo "$0: Unable to set driver" >&2
    254		exit 1
    255	fi
    256}
    257
    258config_get_driver()
    259{
    260	cat $DIR/config_test_driver
    261}
    262
    263config_get_test_result()
    264{
    265	cat $DIR/test_result
    266}
    267
    268config_reset()
    269{
    270	if ! echo -n "1" >"$DIR"/reset; then
    271		echo "$0: reset should have worked" >&2
    272		exit 1
    273	fi
    274}
    275
    276config_show_config()
    277{
    278	echo "----------------------------------------------------"
    279	cat "$DIR"/config
    280	echo "----------------------------------------------------"
    281}
    282
    283config_trigger()
    284{
    285	if ! echo -n "1" >"$DIR"/trigger_config 2>/dev/null; then
    286		echo "$1: FAIL - loading should have worked"
    287		config_show_config
    288		exit 1
    289	fi
    290	echo "$1: OK! - loading kmod test"
    291}
    292
    293config_trigger_want_fail()
    294{
    295	if echo "1" > $DIR/trigger_config 2>/dev/null; then
    296		echo "$1: FAIL - test case was expected to fail"
    297		config_show_config
    298		exit 1
    299	fi
    300	echo "$1: OK! - kmod test case failed as expected"
    301}
    302
    303config_expect_result()
    304{
    305	RC=$(config_get_test_result)
    306	RC_NAME=$(errno_val_to_name $RC)
    307
    308	ERRNO_NAME=$2
    309	ERRNO=$(errno_name_to_val $ERRNO_NAME)
    310
    311	if [[ $ERRNO_NAME = "-ERR_ANY" ]]; then
    312		if [[ $RC -ge 0 ]]; then
    313			echo "$1: FAIL, test expects $ERRNO_NAME - got $RC_NAME ($RC)" >&2
    314			config_show_config
    315			exit 1
    316		fi
    317	elif [[ $RC != $ERRNO ]]; then
    318		echo "$1: FAIL, test expects $ERRNO_NAME ($ERRNO) - got $RC_NAME ($RC)" >&2
    319		config_show_config
    320		exit 1
    321	fi
    322	echo "$1: OK! - Return value: $RC ($RC_NAME), expected $ERRNO_NAME"
    323}
    324
    325kmod_defaults_driver()
    326{
    327	config_reset
    328	modprobe -r $DEFAULT_KMOD_DRIVER
    329	config_set_driver $DEFAULT_KMOD_DRIVER
    330}
    331
    332kmod_defaults_fs()
    333{
    334	config_reset
    335	modprobe -r $DEFAULT_KMOD_FS
    336	config_set_fs $DEFAULT_KMOD_FS
    337	config_set_test_case_fs
    338}
    339
    340kmod_test_0001_driver()
    341{
    342	NAME='\000'
    343
    344	kmod_defaults_driver
    345	config_num_threads 1
    346	printf $NAME >"$DIR"/config_test_driver
    347	config_trigger ${FUNCNAME[0]}
    348	config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND
    349}
    350
    351kmod_test_0001_fs()
    352{
    353	NAME='\000'
    354
    355	kmod_defaults_fs
    356	config_num_threads 1
    357	printf $NAME >"$DIR"/config_test_fs
    358	config_trigger ${FUNCNAME[0]}
    359	config_expect_result ${FUNCNAME[0]} -EINVAL
    360}
    361
    362kmod_test_0001()
    363{
    364	kmod_test_0001_driver
    365	kmod_test_0001_fs
    366}
    367
    368kmod_test_0002_driver()
    369{
    370	NAME="nope-$DEFAULT_KMOD_DRIVER"
    371
    372	kmod_defaults_driver
    373	config_set_driver $NAME
    374	config_num_threads 1
    375	config_trigger ${FUNCNAME[0]}
    376	config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND
    377}
    378
    379kmod_test_0002_fs()
    380{
    381	NAME="nope-$DEFAULT_KMOD_FS"
    382
    383	kmod_defaults_fs
    384	config_set_fs $NAME
    385	config_trigger ${FUNCNAME[0]}
    386	config_expect_result ${FUNCNAME[0]} -EINVAL
    387}
    388
    389kmod_test_0002()
    390{
    391	kmod_test_0002_driver
    392	kmod_test_0002_fs
    393}
    394
    395kmod_test_0003()
    396{
    397	kmod_defaults_fs
    398	config_num_threads 1
    399	config_trigger ${FUNCNAME[0]}
    400	config_expect_result ${FUNCNAME[0]} SUCCESS
    401}
    402
    403kmod_test_0004()
    404{
    405	kmod_defaults_fs
    406	config_num_threads 2
    407	config_trigger ${FUNCNAME[0]}
    408	config_expect_result ${FUNCNAME[0]} SUCCESS
    409}
    410
    411kmod_test_0005()
    412{
    413	kmod_defaults_driver
    414	config_trigger ${FUNCNAME[0]}
    415	config_expect_result ${FUNCNAME[0]} SUCCESS
    416}
    417
    418kmod_test_0006()
    419{
    420	kmod_defaults_fs
    421	config_trigger ${FUNCNAME[0]}
    422	config_expect_result ${FUNCNAME[0]} SUCCESS
    423}
    424
    425kmod_test_0007()
    426{
    427	kmod_test_0005
    428	kmod_test_0006
    429}
    430
    431kmod_test_0008()
    432{
    433	kmod_defaults_driver
    434	MODPROBE_LIMIT=$(config_get_modprobe_limit)
    435	let EXTRA=$MODPROBE_LIMIT/6
    436	config_num_thread_limit_extra $EXTRA
    437	config_trigger ${FUNCNAME[0]}
    438	config_expect_result ${FUNCNAME[0]} SUCCESS
    439}
    440
    441kmod_test_0009()
    442{
    443	kmod_defaults_fs
    444	MODPROBE_LIMIT=$(config_get_modprobe_limit)
    445	let EXTRA=$MODPROBE_LIMIT/4
    446	config_num_thread_limit_extra $EXTRA
    447	config_trigger ${FUNCNAME[0]}
    448	config_expect_result ${FUNCNAME[0]} SUCCESS
    449}
    450
    451kmod_test_0010()
    452{
    453	kmod_defaults_driver
    454	config_num_threads 1
    455	echo "/KMOD_TEST_NONEXISTENT" > /proc/sys/kernel/modprobe
    456	config_trigger ${FUNCNAME[0]}
    457	config_expect_result ${FUNCNAME[0]} -ENOENT
    458	echo "$MODPROBE" > /proc/sys/kernel/modprobe
    459}
    460
    461kmod_test_0011()
    462{
    463	kmod_defaults_driver
    464	config_num_threads 1
    465	# This causes the kernel to not even try executing modprobe.  The error
    466	# code is still -ENOENT like when modprobe doesn't exist, so we can't
    467	# easily test for the exact difference.  But this still is a useful test
    468	# since there was a bug where request_module() returned 0 in this case.
    469	echo > /proc/sys/kernel/modprobe
    470	config_trigger ${FUNCNAME[0]}
    471	config_expect_result ${FUNCNAME[0]} -ENOENT
    472	echo "$MODPROBE" > /proc/sys/kernel/modprobe
    473}
    474
    475kmod_check_visibility()
    476{
    477	local name="$1"
    478	local cmd="$2"
    479
    480	modprobe $DEFAULT_KMOD_DRIVER
    481
    482	local priv=$(eval $cmd)
    483	local unpriv=$(capsh --drop=CAP_SYSLOG -- -c "$cmd")
    484
    485	if [ "$priv" = "$unpriv" ] || \
    486	   [ "${priv:0:3}" = "0x0" ] || \
    487	   [ "${unpriv:0:3}" != "0x0" ] ; then
    488		echo "${FUNCNAME[0]}: FAIL, $name visible to unpriv: '$priv' vs '$unpriv'" >&2
    489		exit 1
    490	else
    491		echo "${FUNCNAME[0]}: OK!"
    492	fi
    493}
    494
    495kmod_test_0012()
    496{
    497	kmod_check_visibility /proc/modules \
    498		"grep '^${DEFAULT_KMOD_DRIVER}\b' /proc/modules | awk '{print \$NF}'"
    499}
    500
    501kmod_test_0013()
    502{
    503	kmod_check_visibility '/sys/module/*/sections/*' \
    504		"cat /sys/module/${DEFAULT_KMOD_DRIVER}/sections/.*text | head -n1"
    505}
    506
    507list_tests()
    508{
    509	echo "Test ID list:"
    510	echo
    511	echo "TEST_ID x NUM_TEST"
    512	echo "TEST_ID:   Test ID"
    513	echo "NUM_TESTS: Number of recommended times to run the test"
    514	echo
    515	echo "0001 x $(get_test_count 0001) - Simple test - 1 thread  for empty string"
    516	echo "0002 x $(get_test_count 0002) - Simple test - 1 thread  for modules/filesystems that do not exist"
    517	echo "0003 x $(get_test_count 0003) - Simple test - 1 thread  for get_fs_type() only"
    518	echo "0004 x $(get_test_count 0004) - Simple test - 2 threads for get_fs_type() only"
    519	echo "0005 x $(get_test_count 0005) - multithreaded tests with default setup - request_module() only"
    520	echo "0006 x $(get_test_count 0006) - multithreaded tests with default setup - get_fs_type() only"
    521	echo "0007 x $(get_test_count 0007) - multithreaded tests with default setup test request_module() and get_fs_type()"
    522	echo "0008 x $(get_test_count 0008) - multithreaded - push kmod_concurrent over max_modprobes for request_module()"
    523	echo "0009 x $(get_test_count 0009) - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()"
    524	echo "0010 x $(get_test_count 0010) - test nonexistent modprobe path"
    525	echo "0011 x $(get_test_count 0011) - test completely disabling module autoloading"
    526	echo "0012 x $(get_test_count 0012) - test /proc/modules address visibility under CAP_SYSLOG"
    527	echo "0013 x $(get_test_count 0013) - test /sys/module/*/sections/* visibility under CAP_SYSLOG"
    528}
    529
    530usage()
    531{
    532	NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .)
    533	let NUM_TESTS=$NUM_TESTS+1
    534	MAX_TEST=$(printf "%04d\n" $NUM_TESTS)
    535	echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |"
    536	echo "		 [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>"
    537	echo "           [ all ] [ -h | --help ] [ -l ]"
    538	echo ""
    539	echo "Valid tests: 0001-$MAX_TEST"
    540	echo ""
    541	echo "    all     Runs all tests (default)"
    542	echo "    -t      Run test ID the number amount of times is recommended"
    543	echo "    -w      Watch test ID run until it runs into an error"
    544	echo "    -s      Run test ID once"
    545	echo "    -c      Run test ID x test-count number of times"
    546	echo "    -l      List all test ID list"
    547	echo " -h|--help  Help"
    548	echo
    549	echo "If an error every occurs execution will immediately terminate."
    550	echo "If you are adding a new test try using -w <test-ID> first to"
    551	echo "make sure the test passes a series of tests."
    552	echo
    553	echo Example uses:
    554	echo
    555	echo "${TEST_NAME}.sh		-- executes all tests"
    556	echo "${TEST_NAME}.sh -t 0008	-- Executes test ID 0008 number of times is recommended"
    557	echo "${TEST_NAME}.sh -w 0008	-- Watch test ID 0008 run until an error occurs"
    558	echo "${TEST_NAME}.sh -s 0008	-- Run test ID 0008 once"
    559	echo "${TEST_NAME}.sh -c 0008 3	-- Run test ID 0008 three times"
    560	echo
    561	list_tests
    562	exit 1
    563}
    564
    565function test_num()
    566{
    567	re='^[0-9]+$'
    568	if ! [[ $1 =~ $re ]]; then
    569		usage
    570	fi
    571}
    572
    573function get_test_data()
    574{
    575	test_num $1
    576	local field_num=$(echo $1 | sed 's/^0*//')
    577	echo $ALL_TESTS | awk '{print $'$field_num'}'
    578}
    579
    580function get_test_count()
    581{
    582	TEST_DATA=$(get_test_data $1)
    583	LAST_TWO=${TEST_DATA#*:*}
    584	echo ${LAST_TWO%:*}
    585}
    586
    587function get_test_enabled()
    588{
    589	TEST_DATA=$(get_test_data $1)
    590	echo ${TEST_DATA#*:*:}
    591}
    592
    593function run_all_tests()
    594{
    595	for i in $ALL_TESTS ; do
    596		TEST_ID=${i%:*:*}
    597		ENABLED=$(get_test_enabled $TEST_ID)
    598		TEST_COUNT=$(get_test_count $TEST_ID)
    599		if [[ $ENABLED -eq "1" ]]; then
    600			test_case $TEST_ID $TEST_COUNT
    601		fi
    602	done
    603}
    604
    605function watch_log()
    606{
    607	if [ $# -ne 3 ]; then
    608		clear
    609	fi
    610	date
    611	echo "Running test: $2 - run #$1"
    612}
    613
    614function watch_case()
    615{
    616	i=0
    617	while [ 1 ]; do
    618
    619		if [ $# -eq 1 ]; then
    620			test_num $1
    621			watch_log $i ${TEST_NAME}_test_$1
    622			${TEST_NAME}_test_$1
    623		else
    624			watch_log $i all
    625			run_all_tests
    626		fi
    627		let i=$i+1
    628	done
    629}
    630
    631function test_case()
    632{
    633	NUM_TESTS=$DEFAULT_NUM_TESTS
    634	if [ $# -eq 2 ]; then
    635		NUM_TESTS=$2
    636	fi
    637
    638	i=0
    639	while [ $i -lt $NUM_TESTS ]; do
    640		test_num $1
    641		watch_log $i ${TEST_NAME}_test_$1 noclear
    642		RUN_TEST=${TEST_NAME}_test_$1
    643		$RUN_TEST
    644		let i=$i+1
    645	done
    646}
    647
    648function parse_args()
    649{
    650	if [ $# -eq 0 ]; then
    651		run_all_tests
    652	else
    653		if [[ "$1" = "all" ]]; then
    654			run_all_tests
    655		elif [[ "$1" = "-w" ]]; then
    656			shift
    657			watch_case $@
    658		elif [[ "$1" = "-t" ]]; then
    659			shift
    660			test_num $1
    661			test_case $1 $(get_test_count $1)
    662		elif [[ "$1" = "-c" ]]; then
    663			shift
    664			test_num $1
    665			test_num $2
    666			test_case $1 $2
    667		elif [[ "$1" = "-s" ]]; then
    668			shift
    669			test_case $1 1
    670		elif [[ "$1" = "-l" ]]; then
    671			list_tests
    672		elif [[ "$1" = "-h" || "$1" = "--help" ]]; then
    673			usage
    674		else
    675			usage
    676		fi
    677	fi
    678}
    679
    680test_reqs
    681allow_user_defaults
    682load_req_mod
    683
    684MODPROBE=$(</proc/sys/kernel/modprobe)
    685trap "test_finish" EXIT
    686
    687parse_args $@
    688
    689exit 0