Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests/fpu: Add an FPU selftest

Add a selftest for the usage of FPU code in kernel mode.

Currently only implemented for x86. In the future, kernel FPU testing
could be unified between the different architectures supporting it.

[ bp:

- Split out from a conglomerate patch, put comments over statements.
- run the test only on debugfs write.
- Add bare-minimum run_test_fpu.sh, run 1000 iterations on all CPUs
by default.
- Add conditionally -msse2 so that clang doesn't generate library
calls.
- Use cc-option to detect gcc 7.1 not supporting -mpreferred-stack-boundary=3 (amluto).
- Document stuff so that we don't forget.
- Fix:
ld: lib/test_fpu.o: in function `test_fpu_get':
>> test_fpu.c:(.text+0x16e): undefined reference to `__sanitizer_cov_trace_cmpd'
>> ld: test_fpu.c:(.text+0x1a7): undefined reference to `__sanitizer_cov_trace_cmpd'
ld: test_fpu.c:(.text+0x1e0): undefined reference to `__sanitizer_cov_trace_cmpd'
]

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Petteri Aimonen <jpa@git.mail.kapsi.fi>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
Link: https://lkml.kernel.org/r/20200624114646.28953-3-bp@alien8.de

authored by

Petteri Aimonen and committed by
Borislav Petkov
4185b3b9 7ad81676

+243
+11
lib/Kconfig.debug
··· 2307 2307 2308 2308 If unsure, say N. 2309 2309 2310 + config TEST_FPU 2311 + tristate "Test floating point operations in kernel space" 2312 + depends on X86 && !KCOV_INSTRUMENT_ALL 2313 + help 2314 + Enable this option to add /sys/kernel/debug/selftest_helpers/test_fpu 2315 + which will trigger a sequence of floating point operations. This is used 2316 + for self-testing floating point control register setting in 2317 + kernel_fpu_begin(). 2318 + 2319 + If unsure, say N. 2320 + 2310 2321 endif # RUNTIME_TESTING_MENU 2311 2322 2312 2323 config MEMTEST
+24
lib/Makefile
··· 99 99 obj-$(CONFIG_TEST_LOCKUP) += test_lockup.o 100 100 obj-$(CONFIG_TEST_HMM) += test_hmm.o 101 101 102 + # 103 + # CFLAGS for compiling floating point code inside the kernel. x86/Makefile turns 104 + # off the generation of FPU/SSE* instructions for kernel proper but FPU_FLAGS 105 + # get appended last to CFLAGS and thus override those previous compiler options. 106 + # 107 + FPU_CFLAGS := -mhard-float -msse -msse2 108 + ifdef CONFIG_CC_IS_GCC 109 + # Stack alignment mismatch, proceed with caution. 110 + # GCC < 7.1 cannot compile code using `double` and -mpreferred-stack-boundary=3 111 + # (8B stack alignment). 112 + # See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53383 113 + # 114 + # The "-msse" in the first argument is there so that the 115 + # -mpreferred-stack-boundary=3 build error: 116 + # 117 + # -mpreferred-stack-boundary=3 is not between 4 and 12 118 + # 119 + # can be triggered. Otherwise gcc doesn't complain. 120 + FPU_CFLAGS += $(call cc-option,-msse -mpreferred-stack-boundary=3,-mpreferred-stack-boundary=4) 121 + endif 122 + 123 + obj-$(CONFIG_TEST_FPU) += test_fpu.o 124 + CFLAGS_test_fpu.o += $(FPU_CFLAGS) 125 + 102 126 obj-$(CONFIG_TEST_LIVEPATCH) += livepatch/ 103 127 104 128 obj-$(CONFIG_KUNIT) += kunit/
+89
lib/test_fpu.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Test cases for using floating point operations inside a kernel module. 4 + * 5 + * This tests kernel_fpu_begin() and kernel_fpu_end() functions, especially 6 + * when userland has modified the floating point control registers. The kernel 7 + * state might depend on the state set by the userland thread that was active 8 + * before a syscall. 9 + * 10 + * To facilitate the test, this module registers file 11 + * /sys/kernel/debug/selftest_helpers/test_fpu, which when read causes a 12 + * sequence of floating point operations. If the operations fail, either the 13 + * read returns error status or the kernel crashes. 14 + * If the operations succeed, the read returns "1\n". 15 + */ 16 + 17 + #include <linux/module.h> 18 + #include <linux/kernel.h> 19 + #include <linux/debugfs.h> 20 + #include <asm/fpu/api.h> 21 + 22 + static int test_fpu(void) 23 + { 24 + /* 25 + * This sequence of operations tests that rounding mode is 26 + * to nearest and that denormal numbers are supported. 27 + * Volatile variables are used to avoid compiler optimizing 28 + * the calculations away. 29 + */ 30 + volatile double a, b, c, d, e, f, g; 31 + 32 + a = 4.0; 33 + b = 1e-15; 34 + c = 1e-310; 35 + 36 + /* Sets precision flag */ 37 + d = a + b; 38 + 39 + /* Result depends on rounding mode */ 40 + e = a + b / 2; 41 + 42 + /* Denormal and very large values */ 43 + f = b / c; 44 + 45 + /* Depends on denormal support */ 46 + g = a + c * f; 47 + 48 + if (d > a && e > a && g > a) 49 + return 0; 50 + else 51 + return -EINVAL; 52 + } 53 + 54 + static int test_fpu_get(void *data, u64 *val) 55 + { 56 + int status = -EINVAL; 57 + 58 + kernel_fpu_begin(); 59 + status = test_fpu(); 60 + kernel_fpu_end(); 61 + 62 + *val = 1; 63 + return status; 64 + } 65 + 66 + DEFINE_SIMPLE_ATTRIBUTE(test_fpu_fops, test_fpu_get, NULL, "%lld\n"); 67 + static struct dentry *selftest_dir; 68 + 69 + static int __init test_fpu_init(void) 70 + { 71 + selftest_dir = debugfs_create_dir("selftest_helpers", NULL); 72 + if (!selftest_dir) 73 + return -ENOMEM; 74 + 75 + debugfs_create_file("test_fpu", 0444, selftest_dir, NULL, 76 + &test_fpu_fops); 77 + 78 + return 0; 79 + } 80 + 81 + static void __exit test_fpu_exit(void) 82 + { 83 + debugfs_remove(selftest_dir); 84 + } 85 + 86 + module_init(test_fpu_init); 87 + module_exit(test_fpu_exit); 88 + 89 + MODULE_LICENSE("GPL");
+1
tools/testing/selftests/Makefile
··· 15 15 TARGETS += filesystems/binderfs 16 16 TARGETS += filesystems/epoll 17 17 TARGETS += firmware 18 + TARGETS += fpu 18 19 TARGETS += ftrace 19 20 TARGETS += futex 20 21 TARGETS += gpio
+2
tools/testing/selftests/fpu/.gitignore
··· 1 + # SPDX-License-Identifier: GPL-2.0+ 2 + test_fpu
+9
tools/testing/selftests/fpu/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0+ 2 + 3 + LDLIBS := -lm 4 + 5 + TEST_GEN_PROGS := test_fpu 6 + 7 + TEST_PROGS := run_test_fpu.sh 8 + 9 + include ../lib.mk
+46
tools/testing/selftests/fpu/run_test_fpu.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Load kernel module for FPU tests 5 + 6 + uid=$(id -u) 7 + if [ $uid -ne 0 ]; then 8 + echo "$0: Must be run as root" 9 + exit 1 10 + fi 11 + 12 + if ! which modprobe > /dev/null 2>&1; then 13 + echo "$0: You need modprobe installed" 14 + exit 4 15 + fi 16 + 17 + if ! modinfo test_fpu > /dev/null 2>&1; then 18 + echo "$0: You must have the following enabled in your kernel:" 19 + echo "CONFIG_TEST_FPU=m" 20 + exit 4 21 + fi 22 + 23 + NR_CPUS=$(getconf _NPROCESSORS_ONLN) 24 + if [ ! $NR_CPUS ]; then 25 + NR_CPUS=1 26 + fi 27 + 28 + modprobe test_fpu 29 + 30 + if [ ! -e /sys/kernel/debug/selftest_helpers/test_fpu ]; then 31 + mount -t debugfs none /sys/kernel/debug 32 + 33 + if [ ! -e /sys/kernel/debug/selftest_helpers/test_fpu ]; then 34 + echo "$0: Error mounting debugfs" 35 + exit 4 36 + fi 37 + fi 38 + 39 + echo "Running 1000 iterations on all CPUs... " 40 + for i in $(seq 1 1000); do 41 + for c in $(seq 1 $NR_CPUS); do 42 + ./test_fpu & 43 + done 44 + done 45 + 46 + rmmod test_fpu
+61
tools/testing/selftests/fpu/test_fpu.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* This testcase operates with the test_fpu kernel driver. 3 + * It modifies the FPU control register in user mode and calls the kernel 4 + * module to perform floating point operations in the kernel. The control 5 + * register value should be independent between kernel and user mode. 6 + */ 7 + 8 + #define _GNU_SOURCE 9 + #include <stdio.h> 10 + #include <errno.h> 11 + #include <string.h> 12 + #include <fenv.h> 13 + #include <unistd.h> 14 + #include <fcntl.h> 15 + 16 + const char *test_fpu_path = "/sys/kernel/debug/selftest_helpers/test_fpu"; 17 + 18 + int main(void) 19 + { 20 + char dummy[1]; 21 + int fd = open(test_fpu_path, O_RDONLY); 22 + 23 + if (fd < 0) { 24 + printf("[SKIP]\tcan't access %s: %s\n", 25 + test_fpu_path, strerror(errno)); 26 + return 0; 27 + } 28 + 29 + if (read(fd, dummy, 1) < 0) { 30 + printf("[FAIL]\taccess with default rounding mode failed\n"); 31 + return 1; 32 + } 33 + 34 + fesetround(FE_DOWNWARD); 35 + if (read(fd, dummy, 1) < 0) { 36 + printf("[FAIL]\taccess with downward rounding mode failed\n"); 37 + return 2; 38 + } 39 + if (fegetround() != FE_DOWNWARD) { 40 + printf("[FAIL]\tusermode rounding mode clobbered\n"); 41 + return 3; 42 + } 43 + 44 + /* Note: the tests up to this point are quite safe and will only return 45 + * an error. But the exception mask setting can cause misbehaving kernel 46 + * to crash. 47 + */ 48 + feclearexcept(FE_ALL_EXCEPT); 49 + feenableexcept(FE_ALL_EXCEPT); 50 + if (read(fd, dummy, 1) < 0) { 51 + printf("[FAIL]\taccess with fpu exceptions unmasked failed\n"); 52 + return 4; 53 + } 54 + if (fegetexcept() != FE_ALL_EXCEPT) { 55 + printf("[FAIL]\tusermode fpu exception mask clobbered\n"); 56 + return 5; 57 + } 58 + 59 + printf("[OK]\ttest_fpu\n"); 60 + return 0; 61 + }