--- v7
+++ v6
@@ -9,11 +9,6 @@
Cc: Kees Cook <keescook@chromium.org>
Cc: Shuah Khan <shuah@kernel.org>
---
-
-Changes since v6:
-* Add full combination tests for all file types, including block
- devices, character devices, fifos, sockets and symlinks.
-* Properly save and restore initial sysctl value for all tests.
Changes since v5:
* Refactor with FIXTURE_VARIANT, which make the tests much more easy to
@@ -51,8 +46,8 @@
tools/testing/selftests/openat2/Makefile | 3 +-
tools/testing/selftests/openat2/config | 1 +
tools/testing/selftests/openat2/helpers.h | 1 +
- .../testing/selftests/openat2/omayexec_test.c | 325 ++++++++++++++++++
- 5 files changed, 332 insertions(+), 1 deletion(-)
+ .../testing/selftests/openat2/omayexec_test.c | 262 ++++++++++++++++++
+ 5 files changed, 269 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/openat2/config
create mode 100644 tools/testing/selftests/openat2/omayexec_test.c
@@ -106,10 +101,10 @@
#include "../kselftest.h"
diff --git a/tools/testing/selftests/openat2/omayexec_test.c b/tools/testing/selftests/openat2/omayexec_test.c
new file mode 100644
-index 000000000000..34b91f9d78d0
+index 000000000000..a33f31e59045
--- /dev/null
+++ b/tools/testing/selftests/openat2/omayexec_test.c
-@@ -0,0 +1,325 @@
+@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test O_MAYEXEC
@@ -126,7 +121,6 @@
+#include <sys/capability.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
-+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include "helpers.h"
@@ -139,13 +133,8 @@
+static const char sysctl_path[] = "/proc/sys/fs/open_mayexec_enforce";
+
+static const char workdir_path[] = "./test-mount";
-+static const char reg_file_path[] = "./test-mount/regular_file";
++static const char file_path[] = "./test-mount/file";
+static const char dir_path[] = "./test-mount/directory";
-+static const char symlink_path[] = "./test-mount/symlink";
-+static const char block_dev_path[] = "./test-mount/block_device";
-+static const char char_dev_path[] = "./test-mount/character_device";
-+static const char fifo_path[] = "./test-mount/fifo";
-+static const char sock_path[] = "./test-mount/socket";
+
+static void ignore_dac(struct __test_metadata *_metadata, int override)
+{
@@ -179,91 +168,55 @@
+}
+
+static void test_omx(struct __test_metadata *_metadata,
-+ const char *const path, const int no_mayexec_err_code,
-+ const int mayexec_err_code)
++ const char *const path, const int err_code)
+{
+ struct open_how how = {
-+ .flags = O_RDONLY | O_NOFOLLOW | O_CLOEXEC,
++ .flags = O_RDONLY | O_CLOEXEC,
+ };
+ int fd;
-+
-+ /* Do not block on pipes. */
-+ if (path == fifo_path)
-+ how.flags |= O_NONBLOCK;
+
+ /* Opens without O_MAYEXEC. */
+ fd = sys_openat2(AT_FDCWD, path, &how);
-+ if (!no_mayexec_err_code) {
-+ ASSERT_LE(0, fd) {
-+ TH_LOG("Failed to openat2 %s: %d", path, -fd);
-+ }
++ ASSERT_LE(0, fd);
++ EXPECT_EQ(0, close(fd));
++
++ how.flags |= O_MAYEXEC;
++
++ /* Checks that O_MAYEXEC is ignored with open(2). */
++ fd = open(path, how.flags);
++ ASSERT_LE(0, fd);
++ EXPECT_EQ(0, close(fd));
++
++ /* Checks that O_MAYEXEC is ignored with openat(2). */
++ fd = openat(AT_FDCWD, path, how.flags);
++ ASSERT_LE(0, fd);
++ EXPECT_EQ(0, close(fd));
++
++ /* Opens with O_MAYEXEC. */
++ fd = sys_openat2(AT_FDCWD, path, &how);
++ if (!err_code) {
++ ASSERT_LE(0, fd);
+ EXPECT_EQ(0, close(fd));
+ } else {
-+ ASSERT_EQ(no_mayexec_err_code, fd) {
-+ TH_LOG("Wrong error for openat2 %s: %d", path, -fd);
-+ }
++ ASSERT_EQ(err_code, fd);
+ }
-+
-+ how.flags |= O_MAYEXEC;
-+
-+ /* Checks that O_MAYEXEC is ignored with open(2). */
-+ fd = open(path, how.flags);
-+ if (!no_mayexec_err_code) {
-+ ASSERT_LE(0, fd) {
-+ TH_LOG("Failed to open %s: %d", path, errno);
-+ }
-+ EXPECT_EQ(0, close(fd));
-+ } else {
-+ ASSERT_EQ(no_mayexec_err_code, -errno);
-+ }
-+
-+ /* Checks that O_MAYEXEC is ignored with openat(2). */
-+ fd = openat(AT_FDCWD, path, how.flags);
-+ if (!no_mayexec_err_code) {
-+ ASSERT_LE(0, fd) {
-+ TH_LOG("Failed to openat %s: %d", path, errno);
-+ }
-+ EXPECT_EQ(0, close(fd));
-+ } else {
-+ ASSERT_EQ(no_mayexec_err_code, -errno);
-+ }
-+
-+ /* Opens with O_MAYEXEC. */
-+ fd = sys_openat2(AT_FDCWD, path, &how);
-+ if (!mayexec_err_code) {
-+ ASSERT_LE(0, fd) {
-+ TH_LOG("Failed to openat2 %s: %d", path, -fd);
-+ }
-+ EXPECT_EQ(0, close(fd));
-+ } else {
-+ ASSERT_EQ(mayexec_err_code, fd) {
-+ TH_LOG("Wrong error for openat2 %s: %d", path, -fd);
-+ }
-+ }
-+}
-+
-+static void test_file_types(struct __test_metadata *_metadata, const int err_code,
-+ const bool has_policy)
-+{
-+ test_omx(_metadata, reg_file_path, 0, err_code);
-+ test_omx(_metadata, dir_path, 0, -EISDIR);
-+ test_omx(_metadata, symlink_path, -ELOOP, -ELOOP);
-+ test_omx(_metadata, block_dev_path, 0, has_policy ? -EACCES : 0);
-+ test_omx(_metadata, char_dev_path, 0, has_policy ? -EACCES : 0);
-+ test_omx(_metadata, fifo_path, 0, has_policy ? -EACCES : 0);
-+ test_omx(_metadata, sock_path, -ENXIO, has_policy ? -EACCES : -ENXIO);
-+}
-+
-+static void test_files(struct __test_metadata *_metadata, const int err_code,
-+ const bool has_policy)
++}
++
++static void test_omx_dir_file(struct __test_metadata *_metadata, const int err_code)
++{
++ test_omx(_metadata, dir_path, -EISDIR);
++ test_omx(_metadata, file_path, err_code);
++}
++
++static void test_dir_file(struct __test_metadata *_metadata, const int err_code)
+{
+ /* Tests as root. */
+ ignore_dac(_metadata, 1);
-+ test_file_types(_metadata, err_code, has_policy);
++ test_omx_dir_file(_metadata, err_code);
+
+ /* Tests without bypass. */
+ ignore_dac(_metadata, 0);
-+ test_file_types(_metadata, err_code, has_policy);
++ test_omx_dir_file(_metadata, err_code);
+}
+
+static void sysctl_write_char(struct __test_metadata *_metadata, const char value)
@@ -327,6 +280,8 @@
+
+FIXTURE_SETUP(omayexec)
+{
++ int fd;
++
+ /*
+ * Cleans previous workspace if any error previously happened (don't
+ * check errors).
@@ -340,20 +295,14 @@
+ (variant->mount_exec ? 0 : MS_NOEXEC),
+ "mode=0700,size=4k"));
+
-+ /* Creates a regular file. */
-+ ASSERT_EQ(0, mknod(reg_file_path, S_IFREG | (variant->file_exec ? 0500 : 0400), 0));
-+ /* Creates a directory. */
-+ ASSERT_EQ(0, mkdir(dir_path, variant->file_exec ? 0500 : 0400));
-+ /* Creates a symlink pointing to the regular file. */
-+ ASSERT_EQ(0, symlink("regular_file", symlink_path));
-+ /* Creates a character device: /dev/null. */
-+ ASSERT_EQ(0, mknod(char_dev_path, S_IFCHR | 0400, makedev(1, 3)));
-+ /* Creates a block device: /dev/loop0 */
-+ ASSERT_EQ(0, mknod(block_dev_path, S_IFBLK | 0400, makedev(7, 0)));
-+ /* Creates a fifo. */
-+ ASSERT_EQ(0, mknod(fifo_path, S_IFIFO | 0400, 0));
-+ /* Creates a socket. */
-+ ASSERT_EQ(0, mknod(sock_path, S_IFSOCK | 0400, 0));
++ /* Creates a test file. */
++ fd = open(file_path, O_CREAT | O_RDONLY | O_CLOEXEC,
++ variant->file_exec ? 00500 : 00400);
++ ASSERT_LE(0, fd);
++ EXPECT_EQ(0, close(fd));
++
++ /* Creates a test directory. */
++ ASSERT_EQ(0, mkdir(dir_path, variant->file_exec ? 00500 : 00400));
+
+ /* Saves initial sysctl value. */
+ self->initial_sysctl_value = sysctl_read_char(_metadata);
@@ -367,7 +316,7 @@
+ /* Restores initial sysctl value. */
+ sysctl_write_char(_metadata, self->initial_sysctl_value);
+
-+ /* There is no need to unlink the test files. */
++ /* There is no need to unlink file_path nor dir_path. */
+ ASSERT_EQ(0, umount(workdir_path));
+ ASSERT_EQ(0, rmdir(workdir_path));
+}
@@ -376,48 +325,31 @@
+{
+ /* Do not enforce anything. */
+ sysctl_write_char(_metadata, '0');
-+ test_files(_metadata, 0, false);
++ test_dir_file(_metadata, 0);
+}
+
+TEST_F(omayexec, sysctl_1)
+{
+ /* Enforces mount exec check. */
+ sysctl_write_char(_metadata, '1');
-+ test_files(_metadata, variant->sysctl_err_code[0], true);
++ test_dir_file(_metadata, variant->sysctl_err_code[0]);
+}
+
+TEST_F(omayexec, sysctl_2)
+{
+ /* Enforces file exec check. */
+ sysctl_write_char(_metadata, '2');
-+ test_files(_metadata, variant->sysctl_err_code[1], true);
++ test_dir_file(_metadata, variant->sysctl_err_code[1]);
+}
+
+TEST_F(omayexec, sysctl_3)
+{
+ /* Enforces mount and file exec check. */
+ sysctl_write_char(_metadata, '3');
-+ test_files(_metadata, variant->sysctl_err_code[2], true);
-+}
-+
-+FIXTURE(cleanup) {
-+ char initial_sysctl_value;
-+};
-+
-+FIXTURE_SETUP(cleanup)
-+{
-+ /* Saves initial sysctl value. */
-+ self->initial_sysctl_value = sysctl_read_char(_metadata);
-+}
-+
-+FIXTURE_TEARDOWN(cleanup)
-+{
-+ /* Restores initial sysctl value. */
-+ ignore_sys_admin(_metadata, 1);
-+ sysctl_write_char(_metadata, self->initial_sysctl_value);
-+}
-+
-+TEST_F(cleanup, sysctl_access_write)
++ test_dir_file(_metadata, variant->sysctl_err_code[2]);
++}
++
++TEST(sysctl_access_write)
+{
+ int fd;
+ ssize_t ret;