1
2
3
4
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
22
23
24#undef O_LARGEFILE
25#define O_LARGEFILE 0x8000
26
27struct 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
36struct 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
46void 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
52 { .name = "normal struct",
53 .arg.inner.flags = O_RDONLY,
54 .size = sizeof(struct open_how) },
55
56 { .name = "bigger struct (zeroed out)",
57 .arg.inner.flags = O_RDONLY,
58 .size = sizeof(struct open_how_ext) },
59
60
61
62
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
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
105
106
107
108
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
136skip:
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
152struct flag_test {
153 const char *name;
154 struct open_how how;
155 int err;
156};
157
158#define NUM_OPENAT2_FLAG_TESTS 25
159
160void test_openat2_flags(void)
161{
162 struct flag_test tests[] = {
163
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
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
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
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
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
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
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 { .name = "currently unknown bit (1 << 63)",
235 .how.flags = O_RDONLY | (1ULL << 63),
236 .how.resolve = 0, .err = -EINVAL },
237 };
238
239 BUILD_BUG_ON(ARRAY_LEN(tests) != NUM_OPENAT2_FLAG_TESTS);
240
241 for (int i = 0; i < ARRAY_LEN(tests); i++) {
242 int fd, fdflags = -1;
243 char *path, *fdpath = NULL;
244 bool failed = false;
245 struct flag_test *test = &tests[i];
246 void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
247
248 if (!openat2_supported) {
249 ksft_print_msg("openat2(2) unsupported\n");
250 resultfn = ksft_test_result_skip;
251 goto skip;
252 }
253
254 path = (test->how.flags & O_CREAT) ? "/tmp/ksft.openat2_tmpfile" : ".";
255 unlink(path);
256
257 fd = sys_openat2(AT_FDCWD, path, &test->how);
258 if (test->err >= 0)
259 failed = (fd < 0);
260 else
261 failed = (fd != test->err);
262 if (fd >= 0) {
263 int otherflags;
264
265 fdpath = fdreadlink(fd);
266 fdflags = fcntl(fd, F_GETFL);
267 otherflags = fcntl(fd, F_GETFD);
268 close(fd);
269
270 E_assert(fdflags >= 0, "fcntl F_GETFL of new fd");
271 E_assert(otherflags >= 0, "fcntl F_GETFD of new fd");
272
273
274 if (otherflags & FD_CLOEXEC)
275 fdflags |= O_CLOEXEC;
276
277 if (test->how.flags & O_CREAT)
278 fdflags |= O_CREAT;
279 if (!(test->how.flags & O_LARGEFILE))
280 fdflags &= ~O_LARGEFILE;
281 failed |= (fdflags != test->how.flags);
282 }
283
284 if (failed) {
285 resultfn = ksft_test_result_fail;
286
287 ksft_print_msg("openat2 unexpectedly returned ");
288 if (fdpath)
289 ksft_print_msg("%d['%s'] with %X (!= %X)\n",
290 fd, fdpath, fdflags,
291 test->how.flags);
292 else
293 ksft_print_msg("%d (%s)\n", fd, strerror(-fd));
294 }
295
296skip:
297 if (test->err >= 0)
298 resultfn("openat2 with %s succeeds\n", test->name);
299 else
300 resultfn("openat2 with %s fails with %d (%s)\n",
301 test->name, test->err, strerror(-test->err));
302
303 free(fdpath);
304 fflush(stdout);
305 }
306}
307
308#define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \
309 NUM_OPENAT2_FLAG_TESTS)
310
311int main(int argc, char **argv)
312{
313 ksft_print_header();
314 ksft_set_plan(NUM_TESTS);
315
316 test_openat2_struct();
317 test_openat2_flags();
318
319 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
320 ksft_exit_fail();
321 else
322 ksft_exit_pass();
323}
324