2 # SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1
3 # Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
5 # This performs a series tests against the proc sysctl interface.
7 # Kselftest framework requirement - SKIP code is 4.
11 TEST_DRIVER
="test_${TEST_NAME}"
12 TEST_DIR
=$
(dirname $0)
17 # TEST_ID:TEST_COUNT:ENABLED:TARGET:SKIP_NO_TARGET
19 # TEST_ID: is the test id number
20 # TEST_COUNT: number of times we should run the test
21 # ENABLED: 1 if enabled, 0 otherwise
22 # TARGET: test target file required on the test_sysctl module
23 # SKIP_NO_TARGET: 1 skip if TARGET not there
24 # 0 run eventhough TARGET not there
26 # Once these are enabled please leave them as-is. Write your own test,
27 # we have tons of space.
28 ALL_TESTS
="0001:1:1:int_0001:1"
29 ALL_TESTS
="$ALL_TESTS 0002:1:1:string_0001:1"
30 ALL_TESTS
="$ALL_TESTS 0003:1:1:int_0002:1"
31 ALL_TESTS
="$ALL_TESTS 0004:1:1:uint_0001:1"
32 ALL_TESTS
="$ALL_TESTS 0005:3:1:int_0003:1"
33 ALL_TESTS
="$ALL_TESTS 0006:50:1:bitmap_0001:1"
34 ALL_TESTS
="$ALL_TESTS 0007:1:1:boot_int:1"
35 ALL_TESTS
="$ALL_TESTS 0008:1:1:match_int:1"
36 ALL_TESTS
="$ALL_TESTS 0009:1:1:unregister_error:0"
37 ALL_TESTS
="$ALL_TESTS 0010:1:1:mnt/mnt_error:0"
38 ALL_TESTS
="$ALL_TESTS 0011:1:1:empty_add:0"
40 function allow_user_defaults
()
43 DIR
="/sys/module/test_sysctl/"
45 if [ -z $DEFAULT_NUM_TESTS ]; then
48 if [ -z $SYSCTL ]; then
49 SYSCTL
="/proc/sys/debug/test_sysctl"
51 if [ -z $PROD_SYSCTL ]; then
52 PROD_SYSCTL
="/proc/sys"
54 if [ -z $WRITES_STRICT ]; then
55 WRITES_STRICT
="${PROD_SYSCTL}/kernel/sysctl_writes_strict"
59 function check_production_sysctl_writes_strict
()
61 echo -n "Checking production write strict setting ... "
62 if [ ! -e ${WRITES_STRICT} ]; then
63 echo "FAIL, but skip in case of old kernel" >&2
65 old_strict
=$
(cat ${WRITES_STRICT})
66 if [ "$old_strict" = "1" ]; then
69 echo "FAIL, strict value is 0 but force to 1 to continue" >&2
70 echo "1" > ${WRITES_STRICT}
74 if [ -z $PAGE_SIZE ]; then
75 PAGE_SIZE
=$
(getconf PAGESIZE
)
77 if [ -z $MAX_DIGITS ]; then
78 MAX_DIGITS
=$
(($PAGE_SIZE/8))
80 if [ -z $INT_MAX ]; then
81 INT_MAX
=$
(getconf INT_MAX
)
83 if [ -z $UINT_MAX ]; then
84 UINT_MAX
=$
(getconf UINT_MAX
)
91 if [ $uid -ne 0 ]; then
92 echo $msg must be run as root
>&2
96 if ! which perl
2> /dev
/null
> /dev
/null
; then
97 echo "$0: You need perl installed"
100 if ! which getconf
2> /dev
/null
> /dev
/null
; then
101 echo "$0: You need getconf installed"
104 if ! which diff 2> /dev
/null
> /dev
/null
; then
105 echo "$0: You need diff installed"
110 function load_req_mod
()
112 if [ ! -d $SYSCTL ]; then
113 if ! modprobe
-q -n $TEST_DRIVER; then
114 echo "$0: module $TEST_DRIVER not found [SKIP]"
115 echo "You must set CONFIG_TEST_SYSCTL=m in your kernel" >&2
118 modprobe
$TEST_DRIVER
119 if [ $?
-ne 0 ]; then
120 echo "$0: modprobe $TEST_DRIVER failed."
129 TRIGGER
=$
(basename ${TARGET})
149 echo -n $VAL > $TARGET
154 if [ ! -z $TARGET ] && [ ! -z $ORIG ]; then
155 if [ -f ${TARGET} ]; then
156 echo "${ORIG}" > "${TARGET}"
163 echo "${TEST_STR}" > "${TARGET}"
170 if [ "${seen}" != "${TEST_STR}" ]; then
176 # proc files get read a page at a time, which can confuse diff,
177 # and get you incorrect results on proc files with long data. To use
178 # diff against them you must first extract the output to a file, and
179 # then compare against that file.
180 verify_diff_proc_file
()
182 TMP_DUMP_FILE
=$
(mktemp
)
183 cat $1 > $TMP_DUMP_FILE
185 if ! diff -w -q $TMP_DUMP_FILE $2; then
194 echo "$TEST_STR" |
diff -q -w -u - $1 > /dev
/null
200 if [[ $rc != 0 ]]; then
201 echo "Failed test, return value: $rc" >&2
211 if [ ! -z ${old_strict} ]; then
212 echo ${old_strict} > ${WRITES_STRICT}
219 echo "== Testing sysctl behavior against ${TARGET} =="
223 echo -n "Writing test file ... "
224 echo "${TEST_STR}" > "${TEST_FILE}"
225 if ! verify
"${TEST_FILE}"; then
232 echo -n "Checking sysctl is not set to test value ... "
233 if verify
"${TARGET}"; then
240 echo -n "Writing sysctl from shell ... "
242 if ! verify
"${TARGET}"; then
249 echo -n "Resetting sysctl to original value ... "
251 if verify
"${TARGET}"; then
258 # Now that we've validated the sanity of "set_test" and "set_orig",
259 # we can use those functions to set starting states before running
260 # specific behavioral tests.
262 echo -n "Writing entire sysctl in single write ... "
264 dd if="${TEST_FILE}" of
="${TARGET}" bs
=4096 2>/dev
/null
265 if ! verify
"${TARGET}"; then
272 echo -n "Writing middle of sysctl after synchronized seek ... "
274 dd if="${TEST_FILE}" of
="${TARGET}" bs
=1 seek
=1 skip
=1 2>/dev
/null
275 if ! verify
"${TARGET}"; then
282 echo -n "Writing beyond end of sysctl ... "
284 dd if="${TEST_FILE}" of
="${TARGET}" bs
=20 seek
=2 2>/dev
/null
285 if verify
"${TARGET}"; then
292 echo -n "Writing sysctl with multiple long writes ... "
294 (perl
-e 'print "A" x 50;'; echo "${TEST_STR}") | \
295 dd of
="${TARGET}" bs
=50 2>/dev
/null
296 if verify
"${TARGET}"; then
307 echo -n "Testing that $1 fails as expected ... "
310 orig
="$(cat $TARGET)"
311 echo -n "$TEST_STR" > $TARGET 2> /dev
/null
313 # write should fail and $TARGET should retain its original value
314 if [ $?
= 0 ] ||
[ "$(cat $TARGET)" != "$orig" ]; then
325 # sysctl conversion functions receive a boolean sign and ulong
326 # magnitude; here we list the magnitudes we want to test (each of
327 # which will be tested in both positive and negative forms). Since
328 # none of these values fit in 32 bits, writing them to an int- or
329 # uint-typed sysctl should fail.
331 # common boundary-condition values (zero, +1, -1, INT_MIN,
332 # and INT_MAX respectively) if truncated to lower 32 bits
333 # (potential for being falsely deemed in range)
340 # these look like negatives, but without a leading '-' are
341 # actually large positives (should be rejected as above
342 # despite being zero/+1/-1/INT_MIN/INT_MAX in the lower 32)
350 for sign
in '' '-'; do
351 for mag
in "${magnitudes[@]}"; do
352 check_failure
"${sign}${mag}"
357 # Your test must accept digits 3 and 4 to use this
360 echo -n "Checking ignoring spaces up to PAGE_SIZE works on write ... "
363 LIMIT
=$
((MAX_DIGITS
-1))
365 (perl
-e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
366 dd of
="${TARGET}" 2>/dev
/null
368 if ! verify
"${TARGET}"; then
376 echo -n "Checking passing PAGE_SIZE of spaces fails on write ... "
379 LIMIT
=$
((MAX_DIGITS
))
381 (perl
-e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
382 dd of
="${TARGET}" 2>/dev
/null
384 if verify
"${TARGET}"; then
393 # You are using an int
394 run_limit_digit_int
()
396 echo -n "Testing INT_MAX works ... "
399 echo -n $TEST_STR > $TARGET
401 if ! verify
"${TARGET}"; then
409 echo -n "Testing INT_MAX + 1 will fail as expected ... "
411 let TEST_STR
=$INT_MAX+1
412 echo -n $TEST_STR > $TARGET 2> /dev
/null
414 if verify
"${TARGET}"; then
422 echo -n "Testing negative values will work as expected ... "
425 echo -n $TEST_STR > $TARGET 2> /dev
/null
426 if ! verify
"${TARGET}"; then
435 # You used an int array
436 run_limit_digit_int_array
()
438 echo -n "Testing array works as expected ... "
440 echo -n $TEST_STR > $TARGET
442 if ! verify_diff_w
"${TARGET}"; then
450 echo -n "Testing skipping trailing array elements works ... "
451 # Do not reset_vals, carry on the values from the last test.
452 # If we only echo in two digits the last two are left intact
454 echo -n $TEST_STR > $TARGET
455 # After we echo in, to help diff we need to set on TEST_STR what
456 # we expect the result to be.
457 TEST_STR
="100 101 2 1"
459 if ! verify_diff_w
"${TARGET}"; then
467 echo -n "Testing PAGE_SIZE limit on array works ... "
468 # Do not reset_vals, carry on the values from the last test.
469 # Even if you use an int array, you are still restricted to
470 # MAX_DIGITS, this is a known limitation. Test limit works.
471 LIMIT
=$
((MAX_DIGITS
-1))
473 (perl
-e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
474 dd of
="${TARGET}" 2>/dev
/null
477 if ! verify_diff_w
"${TARGET}"; then
485 echo -n "Testing exceeding PAGE_SIZE limit fails as expected ... "
486 # Do not reset_vals, carry on the values from the last test.
488 LIMIT
=$
((MAX_DIGITS
))
490 (perl
-e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
491 dd of
="${TARGET}" 2>/dev
/null
494 if verify_diff_w
"${TARGET}"; then
503 # You are using an unsigned int
504 run_limit_digit_uint
()
506 echo -n "Testing UINT_MAX works ... "
509 echo -n $TEST_STR > $TARGET
511 if ! verify
"${TARGET}"; then
519 echo -n "Testing UINT_MAX + 1 will fail as expected ... "
521 TEST_STR
=$
(($UINT_MAX+1))
522 echo -n $TEST_STR > $TARGET 2> /dev
/null
524 if verify
"${TARGET}"; then
532 echo -n "Testing negative values will not work as expected ... "
535 echo -n $TEST_STR > $TARGET 2> /dev
/null
537 if verify
"${TARGET}"; then
548 echo -n "Writing entire sysctl in short writes ... "
550 dd if="${TEST_FILE}" of
="${TARGET}" bs
=1 2>/dev
/null
551 if ! verify
"${TARGET}"; then
558 echo -n "Writing middle of sysctl after unsynchronized seek ... "
560 dd if="${TEST_FILE}" of
="${TARGET}" bs
=1 seek
=1 2>/dev
/null
561 if verify
"${TARGET}"; then
568 echo -n "Checking sysctl maxlen is at least $MAXLEN ... "
570 perl
-e 'print "A" x ('"${MAXLEN}"'-2), "B";' | \
571 dd of
="${TARGET}" bs
="${MAXLEN}" 2>/dev
/null
572 if ! grep -q B
"${TARGET}"; then
579 echo -n "Checking sysctl keeps original string on overflow append ... "
581 perl
-e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
582 dd of
="${TARGET}" bs
=$
(( MAXLEN
- 1 )) 2>/dev
/null
583 if grep -q B
"${TARGET}"; then
590 echo -n "Checking sysctl stays NULL terminated on write ... "
592 perl
-e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
593 dd of
="${TARGET}" bs
="${MAXLEN}" 2>/dev
/null
594 if grep -q B
"${TARGET}"; then
601 echo -n "Checking sysctl stays NULL terminated on overwrite ... "
603 perl
-e 'print "A" x ('"${MAXLEN}"'-1), "BB";' | \
604 dd of
="${TARGET}" bs
=$
(( $MAXLEN + 1 )) 2>/dev
/null
605 if grep -q B
"${TARGET}"; then
617 TARGET
="${SYSCTL}/$1"
620 if [ ! -f ${TARGET} ] ; then
627 # Total length of bitmaps string to use, a bit under
628 # the maximum input size of the test node
629 LENGTH
=$
((RANDOM
% 65000))
632 BIT
=$
((RANDOM
% 1024))
634 # String containing our list of bits to set
637 # build up the string
638 while [ "${#TEST_STR}" -le "$LENGTH" ]; do
639 # Make sure next entry is discontiguous,
640 # skip ahead at least 2
641 BIT
=$
((BIT
+ $
((2 + RANDOM
% 10))))
643 # Add new bit to the list
644 TEST_STR
="${TEST_STR},${BIT}"
646 # Randomly make it a range
647 if [ "$((RANDOM % 2))" -eq "1" ]; then
648 RANGE_END
=$
((BIT
+ $
((1 + RANDOM
% 10))))
649 TEST_STR
="${TEST_STR}-${RANGE_END}"
654 echo -n "Checking bitmap handler ... "
656 echo -n "$TEST_STR" > $TEST_FILE
658 cat $TEST_FILE > $TARGET 2> /dev
/null
659 if [ $?
-ne 0 ]; then
665 if ! verify_diff_proc_file
"$TARGET" "$TEST_FILE"; then
677 TARGET
="${SYSCTL}/$(get_test_target 0001)"
679 ORIG
=$
(cat "${TARGET}")
680 TEST_STR
=$
(( $ORIG + 1 ))
689 TARGET
="${SYSCTL}/$(get_test_target 0002)"
691 ORIG
=$
(cat "${TARGET}")
692 TEST_STR
="Testing sysctl"
693 # Only string sysctls support seeking/appending.
702 TARGET
="${SYSCTL}/$(get_test_target 0003)"
704 ORIG
=$
(cat "${TARGET}")
705 TEST_STR
=$
(( $ORIG + 1 ))
715 TARGET
="${SYSCTL}/$(get_test_target 0004)"
717 ORIG
=$
(cat "${TARGET}")
718 TEST_STR
=$
(( $ORIG + 1 ))
728 TARGET
="${SYSCTL}/$(get_test_target 0005)"
730 ORIG
=$
(cat "${TARGET}")
732 run_limit_digit_int_array
737 TARGET
="${SYSCTL}/$(get_test_target 0006)"
745 TARGET
="${SYSCTL}/$(get_test_target 0007)"
746 echo -n "Testing if $TARGET is set to 1 ... "
748 if [ ! -f $TARGET ]; then
749 echo -e "SKIPPING\n$TARGET is not present"
754 echo -e "SKIPPING\nTest only possible if sysctl_test is built-in, not module:"
755 cat $TEST_DIR/config
>&2
759 ORIG
=$
(cat "${TARGET}")
761 if [ x
$ORIG = "x1" ]; then
766 if [ ! -f /proc
/cmdline
]; then
767 echo -e "SKIPPING\nThere is no /proc/cmdline to check for paramter"
771 FOUND
=$
(grep -c "sysctl[./]debug[./]test_sysctl[./]boot_int=1" /proc
/cmdline
)
772 if [ $FOUND = "1" ]; then
773 echo -e "FAIL\nKernel param found but $TARGET is not 1." >&2
778 echo -e "SKIPPING\nExpected kernel parameter missing."
779 echo "Kernel must be booted with parameter: sysctl.debug.test_sysctl.boot_int=1"
785 TARGET
="${SYSCTL}/$(get_test_target 0008)"
786 echo -n "Testing if $TARGET is matched in kernel ... "
788 if [ ! -f $TARGET ]; then
789 echo -e "SKIPPING\n$TARGET is not present"
793 ORIG_VALUE
=$
(cat "${TARGET}")
795 if [ $ORIG_VALUE -ne 1 ]; then
807 TARGET
="${SYSCTL}/$(get_test_target 0009)"
808 echo -n "Testing if $TARGET unregistered correctly ... "
809 if [ -d $TARGET ]; then
821 TARGET
="${SYSCTL}/$(get_test_target 0010)"
822 echo -n "Testing that $TARGET was not created ... "
823 if [ -d $TARGET ]; then
835 TARGET
="${SYSCTL}/$(get_test_target 0011)"
836 echo -n "Testing empty dir handling in ${TARGET} ... "
837 if [ ! -d ${TARGET} ]; then
838 echo -e "FAIL\nCould not create ${TARGET}" >&2
843 TARGET2
="${TARGET}/empty"
844 if [ ! -d ${TARGET2} ]; then
845 echo -e "FAIL\nCould not create ${TARGET2}" >&2
858 echo "TEST_ID x NUM_TEST"
859 echo "TEST_ID: Test ID"
860 echo "NUM_TESTS: Number of recommended times to run the test"
862 echo "0001 x $(get_test_count 0001) - tests proc_dointvec_minmax()"
863 echo "0002 x $(get_test_count 0002) - tests proc_dostring()"
864 echo "0003 x $(get_test_count 0003) - tests proc_dointvec()"
865 echo "0004 x $(get_test_count 0004) - tests proc_douintvec()"
866 echo "0005 x $(get_test_count 0005) - tests proc_douintvec() array"
867 echo "0006 x $(get_test_count 0006) - tests proc_do_large_bitmap()"
868 echo "0007 x $(get_test_count 0007) - tests setting sysctl from kernel boot param"
869 echo "0008 x $(get_test_count 0008) - tests sysctl macro values match"
870 echo "0009 x $(get_test_count 0009) - tests sysct unregister"
871 echo "0010 x $(get_test_count 0010) - tests sysct mount point"
872 echo "0011 x $(get_test_count 0011) - tests empty directories"
877 NUM_TESTS
=$
(grep -o ' ' <<<"$ALL_TESTS" |
grep -c .
)
878 let NUM_TESTS
=$NUM_TESTS+1
879 MAX_TEST
=$
(printf "%04d\n" $NUM_TESTS)
880 echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |"
881 echo " [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>"
882 echo " [ all ] [ -h | --help ] [ -l ]"
884 echo "Valid tests: 0001-$MAX_TEST"
886 echo " all Runs all tests (default)"
887 echo " -t Run test ID the number amount of times is recommended"
888 echo " -w Watch test ID run until it runs into an error"
889 echo " -c Run test ID once"
890 echo " -s Run test ID x test-count number of times"
891 echo " -l List all test ID list"
892 echo " -h|--help Help"
894 echo "If an error every occurs execution will immediately terminate."
895 echo "If you are adding a new test try using -w <test-ID> first to"
896 echo "make sure the test passes a series of tests."
900 echo "$TEST_NAME.sh -- executes all tests"
901 echo "$TEST_NAME.sh -t 0002 -- Executes test ID 0002 number of times is recomended"
902 echo "$TEST_NAME.sh -w 0002 -- Watch test ID 0002 run until an error occurs"
903 echo "$TEST_NAME.sh -s 0002 -- Run test ID 0002 once"
904 echo "$TEST_NAME.sh -c 0002 3 -- Run test ID 0002 three times"
913 if ! [[ $1 =~
$re ]]; then
917 function remove_leading_zeros
()
919 echo $1 |
sed 's/^0*//'
922 function get_test_count
()
925 awk_field
=$
(remove_leading_zeros
$1)
926 TEST_DATA
=$
(echo $ALL_TESTS |
awk '{print $'$awk_field'}')
927 echo ${TEST_DATA} |
awk -F":" '{print $2}'
930 function get_test_enabled
()
933 awk_field
=$
(remove_leading_zeros
$1)
934 TEST_DATA
=$
(echo $ALL_TESTS |
awk '{print $'$awk_field'}')
935 echo ${TEST_DATA} |
awk -F":" '{print $3}'
938 function get_test_target
()
941 awk_field
=$
(remove_leading_zeros
$1)
942 TEST_DATA
=$
(echo $ALL_TESTS |
awk '{print $'$awk_field'}')
943 echo ${TEST_DATA} |
awk -F":" '{print $4}'
946 function get_test_skip_no_target
()
949 awk_field
=$
(remove_leading_zeros
$1)
950 TEST_DATA
=$
(echo $ALL_TESTS |
awk '{print $'$awk_field'}')
951 echo ${TEST_DATA} |
awk -F":" '{print $5}'
958 if target_exists
$TEST_TARGET $TEST_ID; then
959 TEST_SKIP
=$
(get_test_skip_no_target
$TEST_ID)
960 if [[ $TEST_SKIP -eq "1" ]]; then
961 echo "Target $TEST_TARGET for test $TEST_ID does not exist ... SKIPPING"
968 function run_all_tests
()
970 for i
in $ALL_TESTS ; do
971 TEST_ID
=${i%:*:*:*:*}
972 ENABLED
=$
(get_test_enabled
$TEST_ID)
973 TEST_COUNT
=$
(get_test_count
$TEST_ID)
974 TEST_TARGET
=$
(get_test_target
$TEST_ID)
976 if [[ $ENABLED -eq "1" ]]; then
977 test_case
$TEST_ID $TEST_COUNT $TEST_TARGET
984 if [ $# -ne 3 ]; then
988 echo "Running test: $2 - run #$1"
991 function watch_case
()
996 if [ $# -eq 1 ]; then
998 watch_log
$i ${TEST_NAME}_test_
$1
1008 function test_case
()
1014 if skip_test
$TEST_ID $TARGET; then
1019 while [ $i -lt $NUM_TESTS ]; do
1021 watch_log
$i ${TEST_NAME}_test_
${TEST_ID} noclear
1022 RUN_TEST
=${TEST_NAME}_test_
${TEST_ID}
1028 function parse_args
()
1030 if [ $# -eq 0 ]; then
1033 if [[ "$1" = "all" ]]; then
1035 elif [[ "$1" = "-w" ]]; then
1038 elif [[ "$1" = "-t" ]]; then
1041 test_case
$1 $
(get_test_count
$1) $
(get_test_target
$1)
1042 elif [[ "$1" = "-c" ]]; then
1046 test_case
$1 $2 $
(get_test_target
$1)
1047 elif [[ "$1" = "-s" ]]; then
1049 test_case
$1 1 $
(get_test_target
$1)
1050 elif [[ "$1" = "-l" ]]; then
1052 elif [[ "$1" = "-h" ||
"$1" = "--help" ]]; then
1062 check_production_sysctl_writes_strict
1065 trap "test_finish" EXIT