~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/openat2/openat2_test.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Author: Aleksa Sarai <cyphar@cyphar.com>
  4  * Copyright (C) 2018-2019 SUSE LLC.
  5  */
  6 
  7 #define _GNU_SOURCE
  8 #define __SANE_USERSPACE_TYPES__ // Use ll64
  9 #include <fcntl.h>
 10 #include <sched.h>
 11 #include <sys/stat.h>
 12 #include <sys/types.h>
 13 #include <sys/mount.h>
 14 #include <stdlib.h>
 15 #include <stdbool.h>
 16 #include <string.h>
 17 
 18 #include "../kselftest.h"
 19 #include "helpers.h"
 20 
 21 /*
 22  * O_LARGEFILE is set to 0 by glibc.
 23  * XXX: This is wrong on {mips, parisc, powerpc, sparc}.
 24  */
 25 #undef  O_LARGEFILE
 26 #ifdef __aarch64__
 27 #define O_LARGEFILE 0x20000
 28 #else
 29 #define O_LARGEFILE 0x8000
 30 #endif
 31 
 32 struct open_how_ext {
 33         struct open_how inner;
 34         uint32_t extra1;
 35         char pad1[128];
 36         uint32_t extra2;
 37         char pad2[128];
 38         uint32_t extra3;
 39 };
 40 
 41 struct struct_test {
 42         const char *name;
 43         struct open_how_ext arg;
 44         size_t size;
 45         int err;
 46 };
 47 
 48 #define NUM_OPENAT2_STRUCT_TESTS 7
 49 #define NUM_OPENAT2_STRUCT_VARIATIONS 13
 50 
 51 void test_openat2_struct(void)
 52 {
 53         int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
 54 
 55         struct struct_test tests[] = {
 56                 /* Normal struct. */
 57                 { .name = "normal struct",
 58                   .arg.inner.flags = O_RDONLY,
 59                   .size = sizeof(struct open_how) },
 60                 /* Bigger struct, with zeroed out end. */
 61                 { .name = "bigger struct (zeroed out)",
 62                   .arg.inner.flags = O_RDONLY,
 63                   .size = sizeof(struct open_how_ext) },
 64 
 65                 /* TODO: Once expanded, check zero-padding. */
 66 
 67                 /* Smaller than version-0 struct. */
 68                 { .name = "zero-sized 'struct'",
 69                   .arg.inner.flags = O_RDONLY, .size = 0, .err = -EINVAL },
 70                 { .name = "smaller-than-v0 struct",
 71                   .arg.inner.flags = O_RDONLY,
 72                   .size = OPEN_HOW_SIZE_VER0 - 1, .err = -EINVAL },
 73 
 74                 /* Bigger struct, with non-zero trailing bytes. */
 75                 { .name = "bigger struct (non-zero data in first 'future field')",
 76                   .arg.inner.flags = O_RDONLY, .arg.extra1 = 0xdeadbeef,
 77                   .size = sizeof(struct open_how_ext), .err = -E2BIG },
 78                 { .name = "bigger struct (non-zero data in middle of 'future fields')",
 79                   .arg.inner.flags = O_RDONLY, .arg.extra2 = 0xfeedcafe,
 80                   .size = sizeof(struct open_how_ext), .err = -E2BIG },
 81                 { .name = "bigger struct (non-zero data at end of 'future fields')",
 82                   .arg.inner.flags = O_RDONLY, .arg.extra3 = 0xabad1dea,
 83                   .size = sizeof(struct open_how_ext), .err = -E2BIG },
 84         };
 85 
 86         BUILD_BUG_ON(ARRAY_LEN(misalignments) != NUM_OPENAT2_STRUCT_VARIATIONS);
 87         BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_STRUCT_TESTS);
 88 
 89         for (int i = 0; i < ARRAY_LEN(tests); i++) {
 90                 struct struct_test *test = &tests[i];
 91                 struct open_how_ext how_ext = test->arg;
 92 
 93                 for (int j = 0; j < ARRAY_LEN(misalignments); j++) {
 94                         int fd, misalign = misalignments[j];
 95                         char *fdpath = NULL;
 96                         bool failed;
 97                         void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
 98 
 99                         void *copy = NULL, *how_copy = &how_ext;
100 
101                         if (!openat2_supported) {
102                                 ksft_print_msg("openat2(2) unsupported\n");
103                                 resultfn = ksft_test_result_skip;
104                                 goto skip;
105                         }
106 
107                         if (misalign) {
108                                 /*
109                                  * Explicitly misalign the structure copying it with the given
110                                  * (mis)alignment offset. The other data is set to be non-zero to
111                                  * make sure that non-zero bytes outside the struct aren't checked
112                                  *
113                                  * This is effectively to check that is_zeroed_user() works.
114                                  */
115                                 copy = malloc(misalign + sizeof(how_ext));
116                                 how_copy = copy + misalign;
117                                 memset(copy, 0xff, misalign);
118                                 memcpy(how_copy, &how_ext, sizeof(how_ext));
119                         }
120 
121                         fd = raw_openat2(AT_FDCWD, ".", how_copy, test->size);
122                         if (test->err >= 0)
123                                 failed = (fd < 0);
124                         else
125                                 failed = (fd != test->err);
126                         if (fd >= 0) {
127                                 fdpath = fdreadlink(fd);
128                                 close(fd);
129                         }
130 
131                         if (failed) {
132                                 resultfn = ksft_test_result_fail;
133 
134                                 ksft_print_msg("openat2 unexpectedly returned ");
135                                 if (fdpath)
136                                         ksft_print_msg("%d['%s']\n", fd, fdpath);
137                                 else
138                                         ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
139                         }
140 
141 skip:
142                         if (test->err >= 0)
143                                 resultfn("openat2 with %s argument [misalign=%d] succeeds\n",
144                                          test->name, misalign);
145                         else
146                                 resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n",
147                                          test->name, misalign, test->err,
148                                          strerror(-test->err));
149 
150                         free(copy);
151                         free(fdpath);
152                         fflush(stdout);
153                 }
154         }
155 }
156 
157 struct flag_test {
158         const char *name;
159         struct open_how how;
160         int err;
161 };
162 
163 #define NUM_OPENAT2_FLAG_TESTS 25
164 
165 void test_openat2_flags(void)
166 {
167         struct flag_test tests[] = {
168                 /* O_TMPFILE is incompatible with O_PATH and O_CREAT. */
169                 { .name = "incompatible flags (O_TMPFILE | O_PATH)",
170                   .how.flags = O_TMPFILE | O_PATH | O_RDWR, .err = -EINVAL },
171                 { .name = "incompatible flags (O_TMPFILE | O_CREAT)",
172                   .how.flags = O_TMPFILE | O_CREAT | O_RDWR, .err = -EINVAL },
173 
174                 /* O_PATH only permits certain other flags to be set ... */
175                 { .name = "compatible flags (O_PATH | O_CLOEXEC)",
176                   .how.flags = O_PATH | O_CLOEXEC },
177                 { .name = "compatible flags (O_PATH | O_DIRECTORY)",
178                   .how.flags = O_PATH | O_DIRECTORY },
179                 { .name = "compatible flags (O_PATH | O_NOFOLLOW)",
180                   .how.flags = O_PATH | O_NOFOLLOW },
181                 /* ... and others are absolutely not permitted. */
182                 { .name = "incompatible flags (O_PATH | O_RDWR)",
183                   .how.flags = O_PATH | O_RDWR, .err = -EINVAL },
184                 { .name = "incompatible flags (O_PATH | O_CREAT)",
185                   .how.flags = O_PATH | O_CREAT, .err = -EINVAL },
186                 { .name = "incompatible flags (O_PATH | O_EXCL)",
187                   .how.flags = O_PATH | O_EXCL, .err = -EINVAL },
188                 { .name = "incompatible flags (O_PATH | O_NOCTTY)",
189                   .how.flags = O_PATH | O_NOCTTY, .err = -EINVAL },
190                 { .name = "incompatible flags (O_PATH | O_DIRECT)",
191                   .how.flags = O_PATH | O_DIRECT, .err = -EINVAL },
192                 { .name = "incompatible flags (O_PATH | O_LARGEFILE)",
193                   .how.flags = O_PATH | O_LARGEFILE, .err = -EINVAL },
194 
195                 /* ->mode must only be set with O_{CREAT,TMPFILE}. */
196                 { .name = "non-zero how.mode and O_RDONLY",
197                   .how.flags = O_RDONLY, .how.mode = 0600, .err = -EINVAL },
198                 { .name = "non-zero how.mode and O_PATH",
199                   .how.flags = O_PATH,   .how.mode = 0600, .err = -EINVAL },
200                 { .name = "valid how.mode and O_CREAT",
201                   .how.flags = O_CREAT,  .how.mode = 0600 },
202                 { .name = "valid how.mode and O_TMPFILE",
203                   .how.flags = O_TMPFILE | O_RDWR, .how.mode = 0600 },
204                 /* ->mode must only contain 0777 bits. */
205                 { .name = "invalid how.mode and O_CREAT",
206                   .how.flags = O_CREAT,
207                   .how.mode = 0xFFFF, .err = -EINVAL },
208                 { .name = "invalid (very large) how.mode and O_CREAT",
209                   .how.flags = O_CREAT,
210                   .how.mode = 0xC000000000000000ULL, .err = -EINVAL },
211                 { .name = "invalid how.mode and O_TMPFILE",
212                   .how.flags = O_TMPFILE | O_RDWR,
213                   .how.mode = 0x1337, .err = -EINVAL },
214                 { .name = "invalid (very large) how.mode and O_TMPFILE",
215                   .how.flags = O_TMPFILE | O_RDWR,
216                   .how.mode = 0x0000A00000000000ULL, .err = -EINVAL },
217 
218                 /* ->resolve flags must not conflict. */
219                 { .name = "incompatible resolve flags (BENEATH | IN_ROOT)",
220                   .how.flags = O_RDONLY,
221                   .how.resolve = RESOLVE_BENEATH | RESOLVE_IN_ROOT,
222                   .err = -EINVAL },
223 
224                 /* ->resolve must only contain RESOLVE_* flags. */
225                 { .name = "invalid how.resolve and O_RDONLY",
226                   .how.flags = O_RDONLY,
227                   .how.resolve = 0x1337, .err = -EINVAL },
228                 { .name = "invalid how.resolve and O_CREAT",
229                   .how.flags = O_CREAT,
230                   .how.resolve = 0x1337, .err = -EINVAL },
231                 { .name = "invalid how.resolve and O_TMPFILE",
232                   .how.flags = O_TMPFILE | O_RDWR,
233                   .how.resolve = 0x1337, .err = -EINVAL },
234                 { .name = "invalid how.resolve and O_PATH",
235                   .how.flags = O_PATH,
236                   .how.resolve = 0x1337, .err = -EINVAL },
237 
238                 /* currently unknown upper 32 bit rejected. */
239                 { .name = "currently unknown bit (1 << 63)",
240                   .how.flags = O_RDONLY | (1ULL << 63),
241                   .how.resolve = 0, .err = -EINVAL },
242         };
243 
244         BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);
245 
246         for (int i = 0; i < ARRAY_LEN(tests); i++) {
247                 int fd, fdflags = -1;
248                 char *path, *fdpath = NULL;
249                 bool failed = false;
250                 struct flag_test *test = &tests[i];
251                 void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
252 
253                 if (!openat2_supported) {
254                         ksft_print_msg("openat2(2) unsupported\n");
255                         resultfn = ksft_test_result_skip;
256                         goto skip;
257                 }
258 
259                 path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : ".";
260                 unlink(path);
261 
262                 fd = sys_openat2(AT_FDCWD, path, &test->how);
263                 if (fd < 0 && fd == -EOPNOTSUPP) {
264                         /*
265                          * Skip the testcase if it failed because not supported
266                          * by FS. (e.g. a valid O_TMPFILE combination on NFS)
267                          */
268                         ksft_test_result_skip("openat2 with %s fails with %d (%s)\n",
269                                               test->name, fd, strerror(-fd));
270                         goto next;
271                 }
272 
273                 if (test->err >= 0)
274                         failed = (fd < 0);
275                 else
276                         failed = (fd != test->err);
277                 if (fd >= 0) {
278                         int otherflags;
279 
280                         fdpath = fdreadlink(fd);
281                         fdflags = fcntl(fd, F_GETFL);
282                         otherflags = fcntl(fd, F_GETFD);
283                         close(fd);
284 
285                         E_assert(fdflags >= 0, "fcntl F_GETFL of new fd");
286                         E_assert(otherflags >= 0, "fcntl F_GETFD of new fd");
287 
288                         /* O_CLOEXEC isn't shown in F_GETFL. */
289                         if (otherflags & FD_CLOEXEC)
290                                 fdflags |= O_CLOEXEC;
291                         /* O_CREAT is hidden from F_GETFL. */
292                         if (test->how.flags & O_CREAT)
293                                 fdflags |= O_CREAT;
294                         if (!(test->how.flags & O_LARGEFILE))
295                                 fdflags &= ~O_LARGEFILE;
296                         failed |= (fdflags != test->how.flags);
297                 }
298 
299                 if (failed) {
300                         resultfn = ksft_test_result_fail;
301 
302                         ksft_print_msg("openat2 unexpectedly returned ");
303                         if (fdpath)
304                                 ksft_print_msg("%d['%s'] with %X (!= %llX)\n",
305                                                fd, fdpath, fdflags,
306                                                test->how.flags);
307                         else
308                                 ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
309                 }
310 
311 skip:
312                 if (test->err >= 0)
313                         resultfn("openat2 with %s succeeds\n", test->name);
314                 else
315                         resultfn("openat2 with %s fails with %d (%s)\n",
316                                  test->name, test->err, strerror(-test->err));
317 next:
318                 free(fdpath);
319                 fflush(stdout);
320         }
321 }
322 
323 #define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
324                    NUM_OPENAT2_FLAG_TESTS)
325 
326 int main(int argc, char **argv)
327 {
328         ksft_print_header();
329         ksft_set_plan(NUM_TESTS);
330 
331         test_openat2_struct();
332         test_openat2_flags();
333 
334         if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
335                 ksft_exit_fail();
336         else
337                 ksft_exit_pass();
338 }
339 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php