0001
0002
0003
0004
0005
0006
0007 #define _GNU_SOURCE
0008 #include <fcntl.h>
0009 #include <sched.h>
0010 #include <sys/stat.h>
0011 #include <sys/types.h>
0012 #include <sys/mount.h>
0013 #include <stdlib.h>
0014 #include <stdbool.h>
0015 #include <string.h>
0016
0017 #include "../kselftest.h"
0018 #include "helpers.h"
0019
0020
0021
0022
0023
0024 #undef O_LARGEFILE
0025 #ifdef __aarch64__
0026 #define O_LARGEFILE 0x20000
0027 #else
0028 #define O_LARGEFILE 0x8000
0029 #endif
0030
0031 struct open_how_ext {
0032 struct open_how inner;
0033 uint32_t extra1;
0034 char pad1[128];
0035 uint32_t extra2;
0036 char pad2[128];
0037 uint32_t extra3;
0038 };
0039
0040 struct struct_test {
0041 const char *name;
0042 struct open_how_ext arg;
0043 size_t size;
0044 int err;
0045 };
0046
0047 #define NUM_OPENAT2_STRUCT_TESTS 7
0048 #define NUM_OPENAT2_STRUCT_VARIATIONS 13
0049
0050 void test_openat2_struct(void)
0051 {
0052 int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
0053
0054 struct struct_test tests[] = {
0055
0056 { .name = "normal struct",
0057 .arg.inner.flags = O_RDONLY,
0058 .size = sizeof(struct open_how) },
0059
0060 { .name = "bigger struct (zeroed out)",
0061 .arg.inner.flags = O_RDONLY,
0062 .size = sizeof(struct open_how_ext) },
0063
0064
0065
0066
0067 { .name = "zero-sized 'struct'",
0068 .arg.inner.flags = O_RDONLY, .size = 0, .err = -EINVAL },
0069 { .name = "smaller-than-v0 struct",
0070 .arg.inner.flags = O_RDONLY,
0071 .size = OPEN_HOW_SIZE_VER0 - 1, .err = -EINVAL },
0072
0073
0074 { .name = "bigger struct (non-zero data in first 'future field')",
0075 .arg.inner.flags = O_RDONLY, .arg.extra1 = 0xdeadbeef,
0076 .size = sizeof(struct open_how_ext), .err = -E2BIG },
0077 { .name = "bigger struct (non-zero data in middle of 'future fields')",
0078 .arg.inner.flags = O_RDONLY, .arg.extra2 = 0xfeedcafe,
0079 .size = sizeof(struct open_how_ext), .err = -E2BIG },
0080 { .name = "bigger struct (non-zero data at end of 'future fields')",
0081 .arg.inner.flags = O_RDONLY, .arg.extra3 = 0xabad1dea,
0082 .size = sizeof(struct open_how_ext), .err = -E2BIG },
0083 };
0084
0085 BUILD_BUG_ON(ARRAY_LEN(misalignments) != NUM_OPENAT2_STRUCT_VARIATIONS);
0086 BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_STRUCT_TESTS);
0087
0088 for (int i = 0; i < ARRAY_LEN(tests); i++) {
0089 struct struct_test *test = &tests[i];
0090 struct open_how_ext how_ext = test->arg;
0091
0092 for (int j = 0; j < ARRAY_LEN(misalignments); j++) {
0093 int fd, misalign = misalignments[j];
0094 char *fdpath = NULL;
0095 bool failed;
0096 void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
0097
0098 void *copy = NULL, *how_copy = &how_ext;
0099
0100 if (!openat2_supported) {
0101 ksft_print_msg("openat2(2) unsupported\n");
0102 resultfn = ksft_test_result_skip;
0103 goto skip;
0104 }
0105
0106 if (misalign) {
0107
0108
0109
0110
0111
0112
0113
0114 copy = malloc(misalign + sizeof(how_ext));
0115 how_copy = copy + misalign;
0116 memset(copy, 0xff, misalign);
0117 memcpy(how_copy, &how_ext, sizeof(how_ext));
0118 }
0119
0120 fd = raw_openat2(AT_FDCWD, ".", how_copy, test->size);
0121 if (test->err >= 0)
0122 failed = (fd < 0);
0123 else
0124 failed = (fd != test->err);
0125 if (fd >= 0) {
0126 fdpath = fdreadlink(fd);
0127 close(fd);
0128 }
0129
0130 if (failed) {
0131 resultfn = ksft_test_result_fail;
0132
0133 ksft_print_msg("openat2 unexpectedly returned ");
0134 if (fdpath)
0135 ksft_print_msg("%d['%s']\n", fd, fdpath);
0136 else
0137 ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
0138 }
0139
0140 skip:
0141 if (test->err >= 0)
0142 resultfn("openat2 with %s argument [misalign=%d] succeeds\n",
0143 test->name, misalign);
0144 else
0145 resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n",
0146 test->name, misalign, test->err,
0147 strerror(-test->err));
0148
0149 free(copy);
0150 free(fdpath);
0151 fflush(stdout);
0152 }
0153 }
0154 }
0155
0156 struct flag_test {
0157 const char *name;
0158 struct open_how how;
0159 int err;
0160 };
0161
0162 #define NUM_OPENAT2_FLAG_TESTS 25
0163
0164 void test_openat2_flags(void)
0165 {
0166 struct flag_test tests[] = {
0167
0168 { .name = "incompatible flags (O_TMPFILE | O_PATH)",
0169 .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = -EINVAL },
0170 { .name = "incompatible flags (O_TMPFILE | O_CREAT)",
0171 .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = -EINVAL },
0172
0173
0174 { .name = "compatible flags (O_PATH | O_CLOEXEC)",
0175 .how.flags = O_PATH | O_CLOEXEC },
0176 { .name = "compatible flags (O_PATH | O_DIRECTORY)",
0177 .how.flags = O_PATH | O_DIRECTORY },
0178 { .name = "compatible flags (O_PATH | O_NOFOLLOW)",
0179 .how.flags = O_PATH | O_NOFOLLOW },
0180
0181 { .name = "incompatible flags (O_PATH | O_RDWR)",
0182 .how.flags = O_PATH | O_RDWR, .err = -EINVAL },
0183 { .name = "incompatible flags (O_PATH | O_CREAT)",
0184 .how.flags = O_PATH | O_CREAT, .err = -EINVAL },
0185 { .name = "incompatible flags (O_PATH | O_EXCL)",
0186 .how.flags = O_PATH | O_EXCL, .err = -EINVAL },
0187 { .name = "incompatible flags (O_PATH | O_NOCTTY)",
0188 .how.flags = O_PATH | O_NOCTTY, .err = -EINVAL },
0189 { .name = "incompatible flags (O_PATH | O_DIRECT)",
0190 .how.flags = O_PATH | O_DIRECT, .err = -EINVAL },
0191 { .name = "incompatible flags (O_PATH | O_LARGEFILE)",
0192 .how.flags = O_PATH | O_LARGEFILE, .err = -EINVAL },
0193
0194
0195 { .name = "non-zero how.mode and O_RDONLY",
0196 .how.flags = O_RDONLY, .how.mode = 0600, .err = -EINVAL },
0197 { .name = "non-zero how.mode and O_PATH",
0198 .how.flags = O_PATH, .how.mode = 0600, .err = -EINVAL },
0199 { .name = "valid how.mode and O_CREAT",
0200 .how.flags = O_CREAT, .how.mode = 0600 },
0201 { .name = "valid how.mode and O_TMPFILE",
0202 .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 },
0203
0204 { .name = "invalid how.mode and O_CREAT",
0205 .how.flags = O_CREAT,
0206 .how.mode = 0xFFFF, .err = -EINVAL },
0207 { .name = "invalid (very large) how.mode and O_CREAT",
0208 .how.flags = O_CREAT,
0209 .how.mode = 0xC000000000000000ULL, .err = -EINVAL },
0210 { .name = "invalid how.mode and O_TMPFILE",
0211 .how.flags = O_TMPFILE | O_RDWR,
0212 .how.mode = 0x1337, .err = -EINVAL },
0213 { .name = "invalid (very large) how.mode and O_TMPFILE",
0214 .how.flags = O_TMPFILE | O_RDWR,
0215 .how.mode = 0x0000A00000000000ULL, .err = -EINVAL },
0216
0217
0218 { .name = "incompatible resolve flags (BENEATH | IN_ROOT)",
0219 .how.flags = O_RDONLY,
0220 .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT,
0221 .err = -EINVAL },
0222
0223
0224 { .name = "invalid how.resolve and O_RDONLY",
0225 .how.flags = O_RDONLY,
0226 .how.resolve = 0x1337, .err = -EINVAL },
0227 { .name = "invalid how.resolve and O_CREAT",
0228 .how.flags = O_CREAT,
0229 .how.resolve = 0x1337, .err = -EINVAL },
0230 { .name = "invalid how.resolve and O_TMPFILE",
0231 .how.flags = O_TMPFILE | O_RDWR,
0232 .how.resolve = 0x1337, .err = -EINVAL },
0233 { .name = "invalid how.resolve and O_PATH",
0234 .how.flags = O_PATH,
0235 .how.resolve = 0x1337, .err = -EINVAL },
0236
0237
0238 { .name = "currently unknown bit (1 << 63)",
0239 .how.flags = O_RDONLY | (1ULL << 63),
0240 .how.resolve = 0, .err = -EINVAL },
0241 };
0242
0243 BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);
0244
0245 for (int i = 0; i < ARRAY_LEN(tests); i++) {
0246 int fd, fdflags = -1;
0247 char *path, *fdpath = NULL;
0248 bool failed = false;
0249 struct flag_test *test = &tests[i];
0250 void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
0251
0252 if (!openat2_supported) {
0253 ksft_print_msg("openat2(2) unsupported\n");
0254 resultfn = ksft_test_result_skip;
0255 goto skip;
0256 }
0257
0258 path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : ".";
0259 unlink(path);
0260
0261 fd = sys_openat2(AT_FDCWD, path, &test->how);
0262 if (fd < 0 && fd == -EOPNOTSUPP) {
0263
0264
0265
0266
0267 ksft_test_result_skip("openat2 with %s fails with %d (%s)\n",
0268 test->name, fd, strerror(-fd));
0269 goto next;
0270 }
0271
0272 if (test->err >= 0)
0273 failed = (fd < 0);
0274 else
0275 failed = (fd != test->err);
0276 if (fd >= 0) {
0277 int otherflags;
0278
0279 fdpath = fdreadlink(fd);
0280 fdflags = fcntl(fd, F_GETFL);
0281 otherflags = fcntl(fd, F_GETFD);
0282 close(fd);
0283
0284 E_assert(fdflags >= 0, "fcntl F_GETFL of new fd");
0285 E_assert(otherflags >= 0, "fcntl F_GETFD of new fd");
0286
0287
0288 if (otherflags & FD_CLOEXEC)
0289 fdflags |= O_CLOEXEC;
0290
0291 if (test->how.flags & O_CREAT)
0292 fdflags |= O_CREAT;
0293 if (!(test->how.flags & O_LARGEFILE))
0294 fdflags &= ~O_LARGEFILE;
0295 failed |= (fdflags != test->how.flags);
0296 }
0297
0298 if (failed) {
0299 resultfn = ksft_test_result_fail;
0300
0301 ksft_print_msg("openat2 unexpectedly returned ");
0302 if (fdpath)
0303 ksft_print_msg("%d['%s'] with %X (!= %X)\n",
0304 fd, fdpath, fdflags,
0305 test->how.flags);
0306 else
0307 ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
0308 }
0309
0310 skip:
0311 if (test->err >= 0)
0312 resultfn("openat2 with %s succeeds\n", test->name);
0313 else
0314 resultfn("openat2 with %s fails with %d (%s)\n",
0315 test->name, test->err, strerror(-test->err));
0316 next:
0317 free(fdpath);
0318 fflush(stdout);
0319 }
0320 }
0321
0322 #define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
0323 NUM_OPENAT2_FLAG_TESTS)
0324
0325 int main(int argc, char **argv)
0326 {
0327 ksft_print_header();
0328 ksft_set_plan(NUM_TESTS);
0329
0330 test_openat2_struct();
0331 test_openat2_flags();
0332
0333 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
0334 ksft_exit_fail();
0335 else
0336 ksft_exit_pass();
0337 }