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

selftests/exec: add file type errno tests

Make sure execve() returns the expected errno values for non-regular
files.

Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Marc Zyngier <maz@kernel.org>
Link: http://lkml.kernel.org/r/20200813231723.2725102-3-keescook@chromium.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Kees Cook and committed by
Linus Torvalds
0f71241a fc4177be

+200 -2
+1
tools/testing/selftests/exec/.gitignore
··· 10 10 /recursion-depth 11 11 xxxxxxxx* 12 12 pipe 13 + S_I*.test
+3 -2
tools/testing/selftests/exec/Makefile
··· 3 3 CFLAGS += -Wno-nonnull 4 4 CFLAGS += -D_GNU_SOURCE 5 5 6 - TEST_PROGS := binfmt_script 6 + TEST_PROGS := binfmt_script non-regular 7 7 TEST_GEN_PROGS := execveat 8 8 TEST_GEN_FILES := execveat.symlink execveat.denatured script subdir pipe 9 9 # Makefile is a run-time dependency, since it's accessed by the execveat test ··· 11 11 12 12 TEST_GEN_PROGS += recursion-depth 13 13 14 - EXTRA_CLEAN := $(OUTPUT)/subdir.moved $(OUTPUT)/execveat.moved $(OUTPUT)/xxxxx* 14 + EXTRA_CLEAN := $(OUTPUT)/subdir.moved $(OUTPUT)/execveat.moved $(OUTPUT)/xxxxx* \ 15 + $(OUTPUT)/S_I*.test 15 16 16 17 include ../lib.mk 17 18
+196
tools/testing/selftests/exec/non-regular.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + #include <errno.h> 3 + #include <fcntl.h> 4 + #include <stdio.h> 5 + #include <string.h> 6 + #include <unistd.h> 7 + #include <sys/socket.h> 8 + #include <sys/stat.h> 9 + #include <sys/sysmacros.h> 10 + #include <sys/types.h> 11 + 12 + #include "../kselftest_harness.h" 13 + 14 + /* Remove a file, ignoring the result if it didn't exist. */ 15 + void rm(struct __test_metadata *_metadata, const char *pathname, 16 + int is_dir) 17 + { 18 + int rc; 19 + 20 + if (is_dir) 21 + rc = rmdir(pathname); 22 + else 23 + rc = unlink(pathname); 24 + 25 + if (rc < 0) { 26 + ASSERT_EQ(errno, ENOENT) { 27 + TH_LOG("Not ENOENT: %s", pathname); 28 + } 29 + } else { 30 + ASSERT_EQ(rc, 0) { 31 + TH_LOG("Failed to remove: %s", pathname); 32 + } 33 + } 34 + } 35 + 36 + FIXTURE(file) { 37 + char *pathname; 38 + int is_dir; 39 + }; 40 + 41 + FIXTURE_VARIANT(file) 42 + { 43 + const char *name; 44 + int expected; 45 + int is_dir; 46 + void (*setup)(struct __test_metadata *_metadata, 47 + FIXTURE_DATA(file) *self, 48 + const FIXTURE_VARIANT(file) *variant); 49 + int major, minor, mode; /* for mknod() */ 50 + }; 51 + 52 + void setup_link(struct __test_metadata *_metadata, 53 + FIXTURE_DATA(file) *self, 54 + const FIXTURE_VARIANT(file) *variant) 55 + { 56 + const char * const paths[] = { 57 + "/bin/true", 58 + "/usr/bin/true", 59 + }; 60 + int i; 61 + 62 + for (i = 0; i < ARRAY_SIZE(paths); i++) { 63 + if (access(paths[i], X_OK) == 0) { 64 + ASSERT_EQ(symlink(paths[i], self->pathname), 0); 65 + return; 66 + } 67 + } 68 + ASSERT_EQ(1, 0) { 69 + TH_LOG("Could not find viable 'true' binary"); 70 + } 71 + } 72 + 73 + FIXTURE_VARIANT_ADD(file, S_IFLNK) 74 + { 75 + .name = "S_IFLNK", 76 + .expected = ELOOP, 77 + .setup = setup_link, 78 + }; 79 + 80 + void setup_dir(struct __test_metadata *_metadata, 81 + FIXTURE_DATA(file) *self, 82 + const FIXTURE_VARIANT(file) *variant) 83 + { 84 + ASSERT_EQ(mkdir(self->pathname, 0755), 0); 85 + } 86 + 87 + FIXTURE_VARIANT_ADD(file, S_IFDIR) 88 + { 89 + .name = "S_IFDIR", 90 + .is_dir = 1, 91 + .expected = EACCES, 92 + .setup = setup_dir, 93 + }; 94 + 95 + void setup_node(struct __test_metadata *_metadata, 96 + FIXTURE_DATA(file) *self, 97 + const FIXTURE_VARIANT(file) *variant) 98 + { 99 + dev_t dev; 100 + int rc; 101 + 102 + dev = makedev(variant->major, variant->minor); 103 + rc = mknod(self->pathname, 0755 | variant->mode, dev); 104 + ASSERT_EQ(rc, 0) { 105 + if (errno == EPERM) 106 + SKIP(return, "Please run as root; cannot mknod(%s)", 107 + variant->name); 108 + } 109 + } 110 + 111 + FIXTURE_VARIANT_ADD(file, S_IFBLK) 112 + { 113 + .name = "S_IFBLK", 114 + .expected = EACCES, 115 + .setup = setup_node, 116 + /* /dev/loop0 */ 117 + .major = 7, 118 + .minor = 0, 119 + .mode = S_IFBLK, 120 + }; 121 + 122 + FIXTURE_VARIANT_ADD(file, S_IFCHR) 123 + { 124 + .name = "S_IFCHR", 125 + .expected = EACCES, 126 + .setup = setup_node, 127 + /* /dev/zero */ 128 + .major = 1, 129 + .minor = 5, 130 + .mode = S_IFCHR, 131 + }; 132 + 133 + void setup_fifo(struct __test_metadata *_metadata, 134 + FIXTURE_DATA(file) *self, 135 + const FIXTURE_VARIANT(file) *variant) 136 + { 137 + ASSERT_EQ(mkfifo(self->pathname, 0755), 0); 138 + } 139 + 140 + FIXTURE_VARIANT_ADD(file, S_IFIFO) 141 + { 142 + .name = "S_IFIFO", 143 + .expected = EACCES, 144 + .setup = setup_fifo, 145 + }; 146 + 147 + FIXTURE_SETUP(file) 148 + { 149 + ASSERT_GT(asprintf(&self->pathname, "%s.test", variant->name), 6); 150 + self->is_dir = variant->is_dir; 151 + 152 + rm(_metadata, self->pathname, variant->is_dir); 153 + variant->setup(_metadata, self, variant); 154 + } 155 + 156 + FIXTURE_TEARDOWN(file) 157 + { 158 + rm(_metadata, self->pathname, self->is_dir); 159 + } 160 + 161 + TEST_F(file, exec_errno) 162 + { 163 + char * const argv[2] = { (char * const)self->pathname, NULL }; 164 + 165 + EXPECT_LT(execv(argv[0], argv), 0); 166 + EXPECT_EQ(errno, variant->expected); 167 + } 168 + 169 + /* S_IFSOCK */ 170 + FIXTURE(sock) 171 + { 172 + int fd; 173 + }; 174 + 175 + FIXTURE_SETUP(sock) 176 + { 177 + self->fd = socket(AF_INET, SOCK_STREAM, 0); 178 + ASSERT_GE(self->fd, 0); 179 + } 180 + 181 + FIXTURE_TEARDOWN(sock) 182 + { 183 + if (self->fd >= 0) 184 + ASSERT_EQ(close(self->fd), 0); 185 + } 186 + 187 + TEST_F(sock, exec_errno) 188 + { 189 + char * const argv[2] = { " magic socket ", NULL }; 190 + char * const envp[1] = { NULL }; 191 + 192 + EXPECT_LT(fexecve(self->fd, argv, envp), 0); 193 + EXPECT_EQ(errno, EACCES); 194 + } 195 + 196 + TEST_HARNESS_MAIN