Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Author: Aleksa Sarai <cyphar@cyphar.com>
0004  * Copyright (C) 2018-2019 SUSE LLC.
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  * O_LARGEFILE is set to 0 by glibc.
0022  * XXX: This is wrong on {mips, parisc, powerpc, sparc}.
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         /* Normal struct. */
0056         { .name = "normal struct",
0057           .arg.inner.flags = O_RDONLY,
0058           .size = sizeof(struct open_how) },
0059         /* Bigger struct, with zeroed out end. */
0060         { .name = "bigger struct (zeroed out)",
0061           .arg.inner.flags = O_RDONLY,
0062           .size = sizeof(struct open_how_ext) },
0063 
0064         /* TODO: Once expanded, check zero-padding. */
0065 
0066         /* Smaller than version-0 struct. */
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         /* Bigger struct, with non-zero trailing bytes. */
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                  * Explicitly misalign the structure copying it with the given
0109                  * (mis)alignment offset. The other data is set to be non-zero to
0110                  * make sure that non-zero bytes outside the struct aren't checked
0111                  *
0112                  * This is effectively to check that is_zeroed_user() works.
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         /* O_TMPFILE is incompatible with O_PATH and O_CREAT. */
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         /* O_PATH only permits certain other flags to be set ... */
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         /* ... and others are absolutely not permitted. */
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         /* ->mode must only be set with O_{CREAT,TMPFILE}. */
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         /* ->mode must only contain 0777 bits. */
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         /* ->resolve flags must not conflict. */
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         /* ->resolve must only contain RESOLVE_* flags. */
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         /* currently unknown upper 32 bit rejected. */
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              * Skip the testcase if it failed because not supported
0265              * by FS. (e.g. a valid O_TMPFILE combination on NFS)
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             /* O_CLOEXEC isn't shown in F_GETFL. */
0288             if (otherflags & FD_CLOEXEC)
0289                 fdflags |= O_CLOEXEC;
0290             /* O_CREAT is hidden from F_GETFL. */
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 }