selftests: splice: Check behavior of full and short splices
authorKees Cook <keescook@chromium.org>
Fri, 7 Aug 2020 06:01:09 +0000 (23:01 -0700)
committerKees Cook <keescook@chromium.org>
Fri, 7 Aug 2020 17:50:11 +0000 (10:50 -0700)
In order to help catch regressions in splice vs read behavior in certain
special files, test a few with various different kinds of internal
kernel helpers.

Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
tools/testing/selftests/splice/.gitignore
tools/testing/selftests/splice/Makefile
tools/testing/selftests/splice/config [new file with mode: 0644]
tools/testing/selftests/splice/settings [new file with mode: 0644]
tools/testing/selftests/splice/short_splice_read.sh [new file with mode: 0755]
tools/testing/selftests/splice/splice_read.c [new file with mode: 0644]

index d5a2da428752a4b4c81e4b6354efc842b8e2899a..be8266f5d04c03f1a2d9c04d1f35338cd3c38ee7 100644 (file)
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 default_file_splice_read
+splice_read
index e519b159b60d83fdc14d8ab263387906d6cf1228..541cd826d5a55eee5642c1940b6bf1d1e0a938e9 100644 (file)
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-TEST_PROGS := default_file_splice_read.sh
-TEST_GEN_PROGS_EXTENDED := default_file_splice_read
+TEST_PROGS := default_file_splice_read.sh short_splice_read.sh
+TEST_GEN_PROGS_EXTENDED := default_file_splice_read splice_read
 
 include ../lib.mk
diff --git a/tools/testing/selftests/splice/config b/tools/testing/selftests/splice/config
new file mode 100644 (file)
index 0000000..058c928
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_TEST_LKM=m
diff --git a/tools/testing/selftests/splice/settings b/tools/testing/selftests/splice/settings
new file mode 100644 (file)
index 0000000..89cedfc
--- /dev/null
@@ -0,0 +1 @@
+timeout=5
diff --git a/tools/testing/selftests/splice/short_splice_read.sh b/tools/testing/selftests/splice/short_splice_read.sh
new file mode 100755 (executable)
index 0000000..7810d35
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+set -e
+
+ret=0
+
+do_splice()
+{
+       filename="$1"
+       bytes="$2"
+       expected="$3"
+
+       out=$(./splice_read "$filename" "$bytes" | cat)
+       if [ "$out" = "$expected" ] ; then
+               echo "ok: $filename $bytes"
+       else
+               echo "FAIL: $filename $bytes"
+               ret=1
+       fi
+}
+
+test_splice()
+{
+       filename="$1"
+
+       full=$(cat "$filename")
+       two=$(echo "$full" | grep -m1 . | cut -c-2)
+
+       # Make sure full splice has the same contents as a standard read.
+       do_splice "$filename" 4096 "$full"
+
+       # Make sure a partial splice see the first two characters.
+       do_splice "$filename" 2 "$two"
+}
+
+# proc_single_open(), seq_read()
+test_splice /proc/$$/limits
+# special open, seq_read()
+test_splice /proc/$$/comm
+
+# proc_handler, proc_dointvec_minmax
+test_splice /proc/sys/fs/nr_open
+# proc_handler, proc_dostring
+test_splice /proc/sys/kernel/modprobe
+# proc_handler, special read
+test_splice /proc/sys/kernel/version
+
+if ! [ -d /sys/module/test_module/sections ] ; then
+       modprobe test_module
+fi
+# kernfs, attr
+test_splice /sys/module/test_module/coresize
+# kernfs, binattr
+test_splice /sys/module/test_module/sections/.init.text
+
+exit $ret
diff --git a/tools/testing/selftests/splice/splice_read.c b/tools/testing/selftests/splice/splice_read.c
new file mode 100644 (file)
index 0000000..46dae6a
--- /dev/null
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+int main(int argc, char *argv[])
+{
+       int fd;
+       size_t size;
+       ssize_t spliced;
+
+       if (argc < 2) {
+               fprintf(stderr, "Usage: %s INPUT [BYTES]\n", argv[0]);
+               return EXIT_FAILURE;
+       }
+
+       fd = open(argv[1], O_RDONLY);
+       if (fd < 0) {
+               perror(argv[1]);
+               return EXIT_FAILURE;
+       }
+
+       if (argc == 3)
+               size = atol(argv[2]);
+       else {
+               struct stat statbuf;
+
+               if (fstat(fd, &statbuf) < 0) {
+                       perror(argv[1]);
+                       return EXIT_FAILURE;
+               }
+
+               if (statbuf.st_size > INT_MAX) {
+                       fprintf(stderr, "%s: Too big\n", argv[1]);
+                       return EXIT_FAILURE;
+               }
+
+               size = statbuf.st_size;
+       }
+
+       /* splice(2) file to stdout. */
+       spliced = splice(fd, NULL, STDOUT_FILENO, NULL,
+                     size, SPLICE_F_MOVE);
+       if (spliced < 0) {
+               perror("splice");
+               return EXIT_FAILURE;
+       }
+
+       close(fd);
+       return EXIT_SUCCESS;
+}