1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Author: Aleksa Sarai <cyphar@cyphar.com>
4 * Copyright (C) 2018-2019 SUSE LLC.
11 #include <sys/types.h>
12 #include <sys/mount.h>
17 #include "../kselftest.h"
21 * O_LARGEFILE is set to 0 by glibc.
22 * XXX: This is wrong on {mips, parisc, powerpc, sparc}.
25 #define O_LARGEFILE 0x8000
28 struct open_how inner
;
38 struct open_how_ext arg
;
43 #define NUM_OPENAT2_STRUCT_TESTS 7
44 #define NUM_OPENAT2_STRUCT_VARIATIONS 13
46 void test_openat2_struct(void)
48 int misalignments
[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 };
50 struct struct_test tests
[] = {
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
) },
60 /* TODO: Once expanded, check zero-padding. */
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
},
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
},
81 BUILD_BUG_ON(ARRAY_LEN(misalignments
) != NUM_OPENAT2_STRUCT_VARIATIONS
);
82 BUILD_BUG_ON(ARRAY_LEN(tests
) != NUM_OPENAT2_STRUCT_TESTS
);
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
;
88 for (int j
= 0; j
< ARRAY_LEN(misalignments
); j
++) {
89 int fd
, misalign
= misalignments
[j
];
92 void (*resultfn
)(const char *msg
, ...) = ksft_test_result_pass
;
94 void *copy
= NULL
, *how_copy
= &how_ext
;
96 if (!openat2_supported
) {
97 ksft_print_msg("openat2(2) unsupported\n");
98 resultfn
= ksft_test_result_skip
;
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
108 * This is effectively to check that is_zeroed_user() works.
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
));
116 fd
= raw_openat2(AT_FDCWD
, ".", how_copy
, test
->size
);
120 failed
= (fd
!= test
->err
);
122 fdpath
= fdreadlink(fd
);
127 resultfn
= ksft_test_result_fail
;
129 ksft_print_msg("openat2 unexpectedly returned ");
131 ksft_print_msg("%d['%s']\n", fd
, fdpath
);
133 ksft_print_msg("%d (%s)\n", fd
, strerror(-fd
));
138 resultfn("openat2 with %s argument [misalign=%d] succeeds\n",
139 test
->name
, misalign
);
141 resultfn("openat2 with %s argument [misalign=%d] fails with %d (%s)\n",
142 test
->name
, misalign
, test
->err
,
143 strerror(-test
->err
));
158 #define NUM_OPENAT2_FLAG_TESTS 23
160 void test_openat2_flags(void)
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
},
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
},
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
},
213 /* ->resolve must only contain RESOLVE_* flags. */
214 { .name
= "invalid how.resolve and O_RDONLY",
215 .how
.flags
= O_RDONLY
,
216 .how
.resolve
= 0x1337, .err
= -EINVAL
},
217 { .name
= "invalid how.resolve and O_CREAT",
218 .how
.flags
= O_CREAT
,
219 .how
.resolve
= 0x1337, .err
= -EINVAL
},
220 { .name
= "invalid how.resolve and O_TMPFILE",
221 .how
.flags
= O_TMPFILE
| O_RDWR
,
222 .how
.resolve
= 0x1337, .err
= -EINVAL
},
223 { .name
= "invalid how.resolve and O_PATH",
225 .how
.resolve
= 0x1337, .err
= -EINVAL
},
228 BUILD_BUG_ON(ARRAY_LEN(tests
) != NUM_OPENAT2_FLAG_TESTS
);
230 for (int i
= 0; i
< ARRAY_LEN(tests
); i
++) {
231 int fd
, fdflags
= -1;
232 char *path
, *fdpath
= NULL
;
234 struct flag_test
*test
= &tests
[i
];
235 void (*resultfn
)(const char *msg
, ...) = ksft_test_result_pass
;
237 if (!openat2_supported
) {
238 ksft_print_msg("openat2(2) unsupported\n");
239 resultfn
= ksft_test_result_skip
;
243 path
= (test
->how
.flags
& O_CREAT
) ? "/tmp/ksft.openat2_tmpfile" : ".";
246 fd
= sys_openat2(AT_FDCWD
, path
, &test
->how
);
250 failed
= (fd
!= test
->err
);
254 fdpath
= fdreadlink(fd
);
255 fdflags
= fcntl(fd
, F_GETFL
);
256 otherflags
= fcntl(fd
, F_GETFD
);
259 E_assert(fdflags
>= 0, "fcntl F_GETFL of new fd");
260 E_assert(otherflags
>= 0, "fcntl F_GETFD of new fd");
262 /* O_CLOEXEC isn't shown in F_GETFL. */
263 if (otherflags
& FD_CLOEXEC
)
264 fdflags
|= O_CLOEXEC
;
265 /* O_CREAT is hidden from F_GETFL. */
266 if (test
->how
.flags
& O_CREAT
)
268 if (!(test
->how
.flags
& O_LARGEFILE
))
269 fdflags
&= ~O_LARGEFILE
;
270 failed
|= (fdflags
!= test
->how
.flags
);
274 resultfn
= ksft_test_result_fail
;
276 ksft_print_msg("openat2 unexpectedly returned ");
278 ksft_print_msg("%d['%s'] with %X (!= %X)\n",
282 ksft_print_msg("%d (%s)\n", fd
, strerror(-fd
));
287 resultfn("openat2 with %s succeeds\n", test
->name
);
289 resultfn("openat2 with %s fails with %d (%s)\n",
290 test
->name
, test
->err
, strerror(-test
->err
));
297 #define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
298 NUM_OPENAT2_FLAG_TESTS)
300 int main(int argc
, char **argv
)
303 ksft_set_plan(NUM_TESTS
);
305 test_openat2_struct();
306 test_openat2_flags();
308 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)