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