forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
7ad8167
commit 4185b3b
Showing
8 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// SPDX-License-Identifier: GPL-2.0+ | ||
/* | ||
* Test cases for using floating point operations inside a kernel module. | ||
* | ||
* This tests kernel_fpu_begin() and kernel_fpu_end() functions, especially | ||
* when userland has modified the floating point control registers. The kernel | ||
* state might depend on the state set by the userland thread that was active | ||
* before a syscall. | ||
* | ||
* To facilitate the test, this module registers file | ||
* /sys/kernel/debug/selftest_helpers/test_fpu, which when read causes a | ||
* sequence of floating point operations. If the operations fail, either the | ||
* read returns error status or the kernel crashes. | ||
* If the operations succeed, the read returns "1\n". | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/kernel.h> | ||
#include <linux/debugfs.h> | ||
#include <asm/fpu/api.h> | ||
|
||
static int test_fpu(void) | ||
{ | ||
/* | ||
* This sequence of operations tests that rounding mode is | ||
* to nearest and that denormal numbers are supported. | ||
* Volatile variables are used to avoid compiler optimizing | ||
* the calculations away. | ||
*/ | ||
volatile double a, b, c, d, e, f, g; | ||
|
||
a = 4.0; | ||
b = 1e-15; | ||
c = 1e-310; | ||
|
||
/* Sets precision flag */ | ||
d = a + b; | ||
|
||
/* Result depends on rounding mode */ | ||
e = a + b / 2; | ||
|
||
/* Denormal and very large values */ | ||
f = b / c; | ||
|
||
/* Depends on denormal support */ | ||
g = a + c * f; | ||
|
||
if (d > a && e > a && g > a) | ||
return 0; | ||
else | ||
return -EINVAL; | ||
} | ||
|
||
static int test_fpu_get(void *data, u64 *val) | ||
{ | ||
int status = -EINVAL; | ||
|
||
kernel_fpu_begin(); | ||
status = test_fpu(); | ||
kernel_fpu_end(); | ||
|
||
*val = 1; | ||
return status; | ||
} | ||
|
||
DEFINE_SIMPLE_ATTRIBUTE(test_fpu_fops, test_fpu_get, NULL, "%lld\n"); | ||
static struct dentry *selftest_dir; | ||
|
||
static int __init test_fpu_init(void) | ||
{ | ||
selftest_dir = debugfs_create_dir("selftest_helpers", NULL); | ||
if (!selftest_dir) | ||
return -ENOMEM; | ||
|
||
debugfs_create_file("test_fpu", 0444, selftest_dir, NULL, | ||
&test_fpu_fops); | ||
|
||
return 0; | ||
} | ||
|
||
static void __exit test_fpu_exit(void) | ||
{ | ||
debugfs_remove(selftest_dir); | ||
} | ||
|
||
module_init(test_fpu_init); | ||
module_exit(test_fpu_exit); | ||
|
||
MODULE_LICENSE("GPL"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# SPDX-License-Identifier: GPL-2.0+ | ||
test_fpu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# SPDX-License-Identifier: GPL-2.0+ | ||
|
||
LDLIBS := -lm | ||
|
||
TEST_GEN_PROGS := test_fpu | ||
|
||
TEST_PROGS := run_test_fpu.sh | ||
|
||
include ../lib.mk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#!/bin/bash | ||
# SPDX-License-Identifier: GPL-2.0 | ||
# | ||
# Load kernel module for FPU tests | ||
|
||
uid=$(id -u) | ||
if [ $uid -ne 0 ]; then | ||
echo "$0: Must be run as root" | ||
exit 1 | ||
fi | ||
|
||
if ! which modprobe > /dev/null 2>&1; then | ||
echo "$0: You need modprobe installed" | ||
exit 4 | ||
fi | ||
|
||
if ! modinfo test_fpu > /dev/null 2>&1; then | ||
echo "$0: You must have the following enabled in your kernel:" | ||
echo "CONFIG_TEST_FPU=m" | ||
exit 4 | ||
fi | ||
|
||
NR_CPUS=$(getconf _NPROCESSORS_ONLN) | ||
if [ ! $NR_CPUS ]; then | ||
NR_CPUS=1 | ||
fi | ||
|
||
modprobe test_fpu | ||
|
||
if [ ! -e /sys/kernel/debug/selftest_helpers/test_fpu ]; then | ||
mount -t debugfs none /sys/kernel/debug | ||
|
||
if [ ! -e /sys/kernel/debug/selftest_helpers/test_fpu ]; then | ||
echo "$0: Error mounting debugfs" | ||
exit 4 | ||
fi | ||
fi | ||
|
||
echo "Running 1000 iterations on all CPUs... " | ||
for i in $(seq 1 1000); do | ||
for c in $(seq 1 $NR_CPUS); do | ||
./test_fpu & | ||
done | ||
done | ||
|
||
rmmod test_fpu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// SPDX-License-Identifier: GPL-2.0+ | ||
/* This testcase operates with the test_fpu kernel driver. | ||
* It modifies the FPU control register in user mode and calls the kernel | ||
* module to perform floating point operations in the kernel. The control | ||
* register value should be independent between kernel and user mode. | ||
*/ | ||
|
||
#define _GNU_SOURCE | ||
#include <stdio.h> | ||
#include <errno.h> | ||
#include <string.h> | ||
#include <fenv.h> | ||
#include <unistd.h> | ||
#include <fcntl.h> | ||
|
||
const char *test_fpu_path = "/sys/kernel/debug/selftest_helpers/test_fpu"; | ||
|
||
int main(void) | ||
{ | ||
char dummy[1]; | ||
int fd = open(test_fpu_path, O_RDONLY); | ||
|
||
if (fd < 0) { | ||
printf("[SKIP]\tcan't access %s: %s\n", | ||
test_fpu_path, strerror(errno)); | ||
return 0; | ||
} | ||
|
||
if (read(fd, dummy, 1) < 0) { | ||
printf("[FAIL]\taccess with default rounding mode failed\n"); | ||
return 1; | ||
} | ||
|
||
fesetround(FE_DOWNWARD); | ||
if (read(fd, dummy, 1) < 0) { | ||
printf("[FAIL]\taccess with downward rounding mode failed\n"); | ||
return 2; | ||
} | ||
if (fegetround() != FE_DOWNWARD) { | ||
printf("[FAIL]\tusermode rounding mode clobbered\n"); | ||
return 3; | ||
} | ||
|
||
/* Note: the tests up to this point are quite safe and will only return | ||
* an error. But the exception mask setting can cause misbehaving kernel | ||
* to crash. | ||
*/ | ||
feclearexcept(FE_ALL_EXCEPT); | ||
feenableexcept(FE_ALL_EXCEPT); | ||
if (read(fd, dummy, 1) < 0) { | ||
printf("[FAIL]\taccess with fpu exceptions unmasked failed\n"); | ||
return 4; | ||
} | ||
if (fegetexcept() != FE_ALL_EXCEPT) { | ||
printf("[FAIL]\tusermode fpu exception mask clobbered\n"); | ||
return 5; | ||
} | ||
|
||
printf("[OK]\ttest_fpu\n"); | ||
return 0; | ||
} |