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