proc_int_k2u_conv_kop() returns the sign through *negp and the magnitude
through *u_ptr.  For a negative value it used to convert the
sign-extended int and negate afterwards, so the magnitude came back as a
wrapped 64-bit number.  Nothing noticed, because no test wrote a
negative value to a jiffies sysctl.

Write one to each of the three int converters and require the value to
read back unchanged.  Each value is chosen to convert exactly at every
CONFIG_HZ, so the test does not depend on it:

        converter               write   jiffies stored  reads back
        jiffies                 -1      -HZ             -1
        ms_jiffies              -1000   -HZ             -1000
        userhz_jiffies          -100    -HZ             -100

On a kernel without the fix, at CONFIG_HZ=250:

  # int_jiffies: got '-18372957097414713411', want '-1'
  # int_ms_jiffies: got '-18446744069414585320', want '-1000'
  # int_userhz_jiffies: got '-18446742229035144346', want '-100'
  not ok 1 test_int_jiffies_negative

(paths shortened; the runner prints them under /proc/sys/debug/test_sysctl)

Signed-off-by: Zhan Xusheng <[email protected]>
---
 tools/testing/selftests/sysctl/sysctl.sh | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/tools/testing/selftests/sysctl/sysctl.sh 
b/tools/testing/selftests/sysctl/sysctl.sh
index ceb5cfa0cfd4..ad1ee0fccd7c 100755
--- a/tools/testing/selftests/sysctl/sysctl.sh
+++ b/tools/testing/selftests/sysctl/sysctl.sh
@@ -328,6 +328,29 @@ test_int_plain()
        assert_int_range "$t" "$baseline" || RET=$KSFT_FAIL
 }
 
+# The jiffies converters report the sign separately from the magnitude, so a
+# negative value must survive the round trip through the converter.  Each
+# value below is chosen to convert exactly at every CONFIG_HZ.
+test_int_jiffies_negative()
+{
+       local t="${SYSCTL}/int_jiffies"
+       local m="${SYSCTL}/int_ms_jiffies"
+       local u="${SYSCTL}/int_userhz_jiffies"
+
+       [[ -f "$t" && -f "$m" && -f "$u" ]] || { RET=$KSFT_SKIP; return; }
+
+       printf '%s' "-1" > "$t"
+       assert_content "$t" "-1" || RET=$KSFT_FAIL
+       printf '%s' "-1000" > "$m"
+       assert_content "$m" "-1000" || RET=$KSFT_FAIL
+       printf '%s' "-100" > "$u"
+       assert_content "$u" "-100" || RET=$KSFT_FAIL
+
+       # The positive path was never broken; keep it covered.
+       printf '%s' "1" > "$t"
+       assert_content "$t" "1" || RET=$KSFT_FAIL
+}
+
 test_uint_plain()
 {
        local t="${SYSCTL}/uint_0001"
-- 
2.43.0


Reply via email to