diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-06-10 10:01:56 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-06-10 10:01:56 +0200 |
commit | 6c6a0147ce8ee79ce8176b076f7e6eb512989982 (patch) | |
tree | 4125c4d173d982441b1f878f314848e02da18b4b /0003-selftests-add-readfile-2-selftests.patch | |
parent | 0ce7a6b8527fbbc84add286a0efc73803a77bcab (diff) | |
download | patches-6c6a0147ce8ee79ce8176b076f7e6eb512989982.tar.gz |
add readfile patches to keep "safe"
Diffstat (limited to '0003-selftests-add-readfile-2-selftests.patch')
-rw-r--r-- | 0003-selftests-add-readfile-2-selftests.patch | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/0003-selftests-add-readfile-2-selftests.patch b/0003-selftests-add-readfile-2-selftests.patch new file mode 100644 index 00000000000000..c3a405403a0e70 --- /dev/null +++ b/0003-selftests-add-readfile-2-selftests.patch @@ -0,0 +1,578 @@ +From 2a133d8e0cb79bc7566c39aae1191a7118a1202e Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Date: Sun, 8 Mar 2020 09:54:45 +0100 +Subject: [PATCH 3/3] selftests: add readfile(2) selftests + +Test the functionality of readfile(2) in various ways. + +Also provide a simple speed test program to benchmark using readfile() +instead of using open()/read()/close(). + +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + tools/testing/selftests/Makefile | 1 + + tools/testing/selftests/readfile/.gitignore | 3 + + tools/testing/selftests/readfile/Makefile | 7 + + tools/testing/selftests/readfile/readfile.c | 285 ++++++++++++++++++ + .../selftests/readfile/readfile_speed.c | 221 ++++++++++++++ + 5 files changed, 517 insertions(+) + create mode 100644 tools/testing/selftests/readfile/.gitignore + create mode 100644 tools/testing/selftests/readfile/Makefile + create mode 100644 tools/testing/selftests/readfile/readfile.c + create mode 100644 tools/testing/selftests/readfile/readfile_speed.c + +diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile +index 1195bd85af38..82359233b945 100644 +--- a/tools/testing/selftests/Makefile ++++ b/tools/testing/selftests/Makefile +@@ -46,6 +46,7 @@ TARGETS += ptrace + TARGETS += openat2 + TARGETS += rseq + TARGETS += rtc ++TARGETS += readfile + TARGETS += seccomp + TARGETS += sigaltstack + TARGETS += size +diff --git a/tools/testing/selftests/readfile/.gitignore b/tools/testing/selftests/readfile/.gitignore +new file mode 100644 +index 000000000000..f0e758d437e4 +--- /dev/null ++++ b/tools/testing/selftests/readfile/.gitignore +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0 ++readfile ++readfile_speed +diff --git a/tools/testing/selftests/readfile/Makefile b/tools/testing/selftests/readfile/Makefile +new file mode 100644 +index 000000000000..1bf1bdec40f8 +--- /dev/null ++++ b/tools/testing/selftests/readfile/Makefile +@@ -0,0 +1,7 @@ ++# SPDX-License-Identifier: GPL-2.0 ++CFLAGS += -g -I../../../../usr/include/ ++CFLAGS += -O2 -Wl,-no-as-needed -Wall ++ ++TEST_GEN_PROGS := readfile readfile_speed ++ ++include ../lib.mk +diff --git a/tools/testing/selftests/readfile/readfile.c b/tools/testing/selftests/readfile/readfile.c +new file mode 100644 +index 000000000000..f0736c6dfa69 +--- /dev/null ++++ b/tools/testing/selftests/readfile/readfile.c +@@ -0,0 +1,285 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2020 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (c) 2020 The Linux Foundation ++ * ++ * Test the readfile() syscall in various ways. ++ */ ++#define _GNU_SOURCE ++#include <stdio.h> ++#include <stdlib.h> ++#include <sys/syscall.h> ++#include <sys/types.h> ++#include <dirent.h> ++#include <fcntl.h> ++#include <limits.h> ++#include <string.h> ++#include <syscall.h> ++ ++#include "../kselftest.h" ++ ++//#ifndef __NR_readfile ++//#define __NR_readfile -1 ++//#endif ++ ++#define __NR_readfile 440 ++ ++#define TEST_FILE1 "/sys/devices/system/cpu/vulnerabilities/meltdown" ++#define TEST_FILE2 "/sys/devices/system/cpu/vulnerabilities/spectre_v1" ++#define TEST_FILE4 "/sys/kernel/debug/usb/devices" ++ ++static int sys_readfile(int fd, const char *filename, unsigned char *buffer, ++ size_t bufsize, int flags) ++{ ++ return syscall(__NR_readfile, fd, filename, buffer, bufsize, flags); ++} ++ ++/* ++ * Test that readfile() is even in the running kernel or not. ++ */ ++static void test_readfile_supported(void) ++{ ++ const char *proc_map = "/proc/self/maps"; ++ unsigned char buffer[10]; ++ int retval; ++ ++ if (__NR_readfile < 0) ++ ksft_exit_skip("readfile() syscall is not defined for the kernel this test was built against\n"); ++ ++ /* ++ * Do a simple test to see if the syscall really is present in the ++ * running kernel ++ */ ++ retval = sys_readfile(0, proc_map, &buffer[0], sizeof(buffer), 0); ++ if (retval == -1) ++ ksft_exit_skip("readfile() syscall not present on running kernel\n"); ++ ++ ksft_test_result_pass("readfile() syscall present\n"); ++} ++ ++/* ++ * Open all files in a specific sysfs directory and read from them ++ * ++ * This tests the "openat" type functionality of opening all files relative to a ++ * directory. We don't care at the moment about the contents. ++ */ ++static void test_sysfs_files(void) ++{ ++ static unsigned char buffer[8000]; ++ const char *sysfs_dir = "/sys/devices/system/cpu/vulnerabilities/"; ++ struct dirent *dirent; ++ DIR *vuln_sysfs_dir; ++ int sysfs_fd; ++ int retval; ++ ++ sysfs_fd = open(sysfs_dir, O_PATH | O_DIRECTORY); ++ if (sysfs_fd == -1) { ++ ksft_test_result_skip("unable to open %s directory\n", ++ sysfs_dir); ++ return; ++ } ++ ++ vuln_sysfs_dir = opendir(sysfs_dir); ++ if (!vuln_sysfs_dir) { ++ ksft_test_result_skip("%s unable to be opened, skipping test\n"); ++ return; ++ } ++ ++ ksft_print_msg("readfile: testing relative path functionality by reading files in %s\n", ++ sysfs_dir); ++ /* open all sysfs file in this directory and read the whole thing */ ++ while ((dirent = readdir(vuln_sysfs_dir))) { ++ /* ignore . and .. */ ++ if (strcmp(dirent->d_name, ".") == 0 || ++ strcmp(dirent->d_name, "..") == 0) ++ continue; ++ ++ retval = sys_readfile(sysfs_fd, dirent->d_name, &buffer[0], ++ sizeof(buffer), 0); ++ ++ if (retval <= 0) { ++ ksft_test_result_fail("readfile(%s) failed with %d\n", ++ dirent->d_name, retval); ++ goto exit; ++ } ++ ++ /* cut off trailing \n character */ ++ buffer[retval - 1] = 0x00; ++ ksft_print_msg(" '%s' contains \"%s\"\n", dirent->d_name, ++ buffer); ++ } ++ ++ ksft_test_result_pass("readfile() relative path functionality passed\n"); ++ ++exit: ++ closedir(vuln_sysfs_dir); ++ close(sysfs_fd); ++} ++ ++/* Temporary directory variables */ ++static int root_fd; /* test root directory file handle */ ++static char tmpdir[PATH_MAX]; ++ ++static void setup_tmpdir(void) ++{ ++ char *tmpdir_root; ++ ++ tmpdir_root = getenv("TMPDIR"); ++ if (!tmpdir_root) ++ tmpdir_root = "/tmp"; ++ ++ snprintf(tmpdir, PATH_MAX, "%s/readfile.XXXXXX", tmpdir_root); ++ if (!mkdtemp(tmpdir)) { ++ ksft_test_result_fail("mkdtemp(%s) failed\n", tmpdir); ++ ksft_exit_fail(); ++ } ++ ++ root_fd = open(tmpdir, O_PATH | O_DIRECTORY); ++ if (root_fd == -1) { ++ ksft_exit_fail_msg("%s unable to be opened, error = %d\n", ++ tmpdir, root_fd); ++ ksft_exit_fail(); ++ } ++ ++ ksft_print_msg("%s created to use for testing\n", tmpdir); ++} ++ ++static void teardown_tmpdir(void) ++{ ++ int retval; ++ ++ close(root_fd); ++ ++ retval = rmdir(tmpdir); ++ if (retval) { ++ ksft_exit_fail_msg("%s removed with return value %d\n", ++ tmpdir, retval); ++ ksft_exit_fail(); ++ } ++ ksft_print_msg("%s cleaned up and removed\n", tmpdir); ++ ++} ++ ++static void test_filesize(size_t size) ++{ ++ char filename[PATH_MAX]; ++ unsigned char *write_data; ++ unsigned char *read_data; ++ int fd; ++ int retval; ++ size_t i; ++ ++ snprintf(filename, PATH_MAX, "size-%ld", size); ++ ++ read_data = malloc(size); ++ write_data = malloc(size); ++ if (!read_data || !write_data) ++ ksft_exit_fail_msg("Unable to allocate %ld bytes\n", size); ++ ++ fd = openat(root_fd, filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); ++ if (fd < 0) ++ ksft_exit_fail_msg("Unable to create file %s\n", filename); ++ ++ ksft_print_msg("%s created\n", filename); ++ ++ for (i = 0; i < size; ++i) ++ write_data[i] = (unsigned char)(0xff & i); ++ ++ write(fd, write_data, size); ++ close(fd); ++ ++ retval = sys_readfile(root_fd, filename, read_data, size, 0); ++ ++ if (retval != size) { ++ ksft_test_result_fail("Read %d bytes but wanted to read %ld bytes.\n", ++ retval, size); ++ goto exit; ++ } ++ ++ if (memcmp(read_data, write_data, size) != 0) { ++ ksft_test_result_fail("Read data of buffer size %d did not match written data\n", ++ size); ++ goto exit; ++ } ++ ++ ksft_test_result_pass("readfile() of size %ld succeeded.\n", size); ++ ++exit: ++ unlinkat(root_fd, filename, 0); ++ free(write_data); ++ free(read_data); ++} ++ ++ ++/* ++ * Create a bunch of differently sized files, and verify we read the correct ++ * amount of data from them. ++ */ ++static void test_filesizes(void) ++{ ++ setup_tmpdir(); ++ ++ test_filesize(0x10); ++ test_filesize(0x100); ++ test_filesize(0x1000); ++ test_filesize(0x10000); ++ test_filesize(0x100000); ++ test_filesize(0x1000000); ++ ++ teardown_tmpdir(); ++ ++} ++ ++static void readfile(const char *filename) ++{ ++// int root_fd; ++ unsigned char buffer[16000]; ++ int retval; ++ ++ memset(buffer, 0x00, sizeof(buffer)); ++ ++// root_fd = open("/", O_DIRECTORY); ++// if (root_fd == -1) ++// ksft_exit_fail_msg("error with root_fd\n"); ++ ++ retval = sys_readfile(root_fd, filename, &buffer[0], sizeof(buffer), 0); ++ ++// close(root_fd); ++ ++ if (retval <= 0) ++ ksft_test_result_fail("readfile() test of filename=%s failed with retval %d\n", ++ filename, retval); ++ else ++ ksft_test_result_pass("readfile() test of filename=%s succeeded with retval=%d\n", ++ filename, retval); ++// buffer='%s'\n", ++// filename, retval, &buffer[0]); ++ ++} ++ ++ ++int main(int argc, char *argv[]) ++{ ++ ksft_print_header(); ++ ksft_set_plan(10); ++ ++ test_readfile_supported(); // 1 test ++ ++ test_sysfs_files(); // 1 test ++ ++ test_filesizes(); // 6 tests ++ ++ setup_tmpdir(); ++ ++ readfile(TEST_FILE1); ++ readfile(TEST_FILE2); ++// readfile(TEST_FILE4); ++ ++ teardown_tmpdir(); ++ ++ if (ksft_get_fail_cnt()) ++ return ksft_exit_fail(); ++ ++ return ksft_exit_pass(); ++} ++ +diff --git a/tools/testing/selftests/readfile/readfile_speed.c b/tools/testing/selftests/readfile/readfile_speed.c +new file mode 100644 +index 000000000000..bbf985b30d1b +--- /dev/null ++++ b/tools/testing/selftests/readfile/readfile_speed.c +@@ -0,0 +1,221 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2020 Greg Kroah-Hartman <gregkh@linuxfoundation.org> ++ * Copyright (c) 2020 The Linux Foundation ++ * ++ * Tiny test program to try to benchmark the speed of the readfile syscall vs. ++ * the open/read/close sequence it replaces. ++ */ ++#define _GNU_SOURCE ++#include <stdio.h> ++#include <stdlib.h> ++#include <sys/syscall.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <dirent.h> ++#include <fcntl.h> ++#include <limits.h> ++#include <string.h> ++#include <syscall.h> ++#include <errno.h> ++#include <unistd.h> ++#include <stdarg.h> ++ ++//#ifndef __NR_readfile ++//#define __NR_readfile -1 ++//#endif ++ ++#define __NR_readfile 440 ++ ++#define TEST_FILE "/sys/devices/system/cpu/vulnerabilities/meltdown" ++ ++static int sys_readfile(int fd, const char *filename, unsigned char *buffer, ++ size_t bufsize, int flags) ++{ ++ return syscall(__NR_readfile, fd, filename, buffer, bufsize, flags); ++} ++ ++/* taken from all-io.h from util-linux repo */ ++static inline ssize_t read_all(int fd, unsigned char *buf, size_t count) ++{ ++ ssize_t ret; ++ ssize_t c = 0; ++ int tries = 0; ++ ++ //memset(buf, 0, count); ++ while (count > 0) { ++ ret = read(fd, buf, count); ++ if (ret <= 0) { ++ if (ret < 0 && (errno == EAGAIN || errno == EINTR) && ++ (tries++ < 5)) { ++ usleep(250000); ++ continue; ++ } ++ return c ? c : -1; ++ } ++ tries = 0; ++ count -= ret; ++ buf += ret; ++ c += ret; ++ } ++ return c; ++} ++ ++static int openreadclose(const char *filename, unsigned char *buffer, ++ size_t bufsize) ++{ ++ size_t count; ++ int fd; ++ ++ fd = openat(0, filename, O_RDONLY); ++ if (fd < 0) { ++ printf("error opening %s\n", filename); ++ return fd; ++ } ++ ++ count = read_all(fd, buffer, bufsize); ++ if (count < 0) { ++ printf("Error %ld reading from %s\n", count, filename); ++ } ++ ++ close(fd); ++ return count; ++} ++ ++enum test_type { ++ READFILE, ++ OPENREADCLOSE, ++}; ++ ++static int do_read_file_test(int loops, enum test_type test_type, ++ const char *filename, ++ unsigned char *buffer, size_t bufsize) ++{ ++ char *test; ++ int retval; ++ int i; ++ ++ switch (test_type) { ++ case READFILE: ++ test = "readfile"; ++ break; ++ ++ case OPENREADCLOSE: ++ test = "open/read/close"; ++ break; ++ default: ++ fprintf(stderr, "wrong test type"); ++ return -1; ++ } ++ ++ fprintf(stdout, ++ "Running %s test on file %s for %d loops...\n", ++ test, filename, loops); ++ ++ for (i = 0; i < loops; ++i) { ++ switch (test_type) { ++ case READFILE: ++ retval = sys_readfile(0, filename, buffer, bufsize, O_RDONLY); ++ break; ++ ++ case OPENREADCLOSE: ++ retval = openreadclose(filename, buffer, bufsize); ++ break; ++ } ++ if (retval < 0) { ++ fprintf(stderr, ++ "test failed on loop %d with error %d\n", ++ i, retval); ++ return retval; ++ } ++ } ++ return 0; ++} ++ ++static int check_file_present(const char *filename) ++{ ++ struct stat sb; ++ int retval; ++ ++ retval = stat(filename, &sb); ++ if (retval == -1) { ++ fprintf(stderr, ++ "filename %s is not present\n", filename); ++ return retval; ++ } ++ ++ if ((sb.st_mode & S_IFMT) != S_IFREG) { ++ fprintf(stderr, ++ "filename %s must be a real file, not anything else.\n", ++ filename); ++ return -1; ++ } ++ return 0; ++} ++ ++static void usage(char *progname) ++{ ++ fprintf(stderr, ++ "usage: %s [options]\n" ++ " -l loops Number of loops to run the test for.\n" ++ " default is 10'000\n" ++ " -t testtype Test type to run\n" ++ " types are: readfile, openreadclose\n" ++ " default is readfile\n", ++ progname); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int loops = 10000; ++ unsigned char buffer[10000]; ++ char c; ++ char *testtype = "readfile"; ++ char *progname; ++ char *filename; ++ enum test_type test_type; ++ int retval; ++ ++ progname = strrchr(argv[0], '/'); ++ progname = progname ? 1+progname : argv[0]; ++ ++ while (EOF != (c = getopt(argc, argv, "t:hl:"))) { ++ switch (c) { ++ case 'l': ++ loops = atoi(optarg); ++ break; ++ ++ case 't': ++ testtype = optarg; ++ break; ++ ++ case 'h': ++ usage(progname); ++ return 0; ++ ++ default: ++ usage(progname); ++ return -1; ++ } ++ } ++ ++ if (strcmp(testtype, "readfile") == 0) ++ test_type = READFILE; ++ else if (strcmp(testtype, "openreadclose") == 0) ++ test_type = OPENREADCLOSE; ++ else { ++ usage(progname); ++ return -1; ++ } ++ ++ filename = TEST_FILE; ++ ++ retval = check_file_present(filename); ++ if (retval) ++ return retval; ++ ++ retval = do_read_file_test(loops, test_type, TEST_FILE, ++ &buffer[0], sizeof(buffer)); ++ ++ return retval; ++} +-- +2.27.0 + |