1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: test installing a direct descriptor into the regular
4 * file table
5 *
6 */
7 #include <errno.h>
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <fcntl.h>
13
14 #include "liburing.h"
15 #include "helpers.h"
16
17 static int no_fd_install;
18
19 /* test that O_CLOEXEC is accepted, and others are not */
test_flags(struct io_uring * ring,int async)20 static int test_flags(struct io_uring *ring, int async)
21 {
22 struct io_uring_sqe *sqe;
23 struct io_uring_cqe *cqe;
24 int ret, fds[2], fd;
25
26 if (pipe(fds) < 0) {
27 perror("pipe");
28 return T_EXIT_FAIL;
29 }
30
31 ret = io_uring_register_files(ring, &fds[0], 1);
32 if (ret) {
33 fprintf(stderr, "failed register files %d\n", ret);
34 return T_EXIT_FAIL;
35 }
36
37 /* check that setting an invalid flag fails */
38 sqe = io_uring_get_sqe(ring);
39 io_uring_prep_fixed_fd_install(sqe, 0, 1U << 17);
40 io_uring_submit(ring);
41
42 ret = io_uring_wait_cqe(ring, &cqe);
43 if (ret) {
44 fprintf(stderr, "wait cqe %d\n", ret);
45 return T_EXIT_FAIL;
46 }
47 if (cqe->res != -EINVAL) {
48 fprintf(stderr, "unexpected cqe res %d\n", cqe->res);
49 return T_EXIT_FAIL;
50 }
51 io_uring_cqe_seen(ring, cqe);
52
53 /* check that IORING_FIXED_FD_NO_CLOEXEC is accepted */
54 sqe = io_uring_get_sqe(ring);
55 io_uring_prep_fixed_fd_install(sqe, 0, IORING_FIXED_FD_NO_CLOEXEC);
56 if (async)
57 sqe->flags |= IOSQE_ASYNC;
58 io_uring_submit(ring);
59
60 ret = io_uring_wait_cqe(ring, &cqe);
61 if (ret) {
62 fprintf(stderr, "wait cqe %d\n", ret);
63 return T_EXIT_FAIL;
64 }
65 if (cqe->res < 0) {
66 fprintf(stderr, "unexpected cqe res %d\n", cqe->res);
67 return T_EXIT_FAIL;
68 }
69 fd = cqe->res;
70 io_uring_cqe_seen(ring, cqe);
71
72 close(fds[0]);
73 close(fds[1]);
74 close(fd);
75 io_uring_unregister_files(ring);
76
77 return T_EXIT_PASS;
78 }
79
test_linked(struct io_uring * ring)80 static int test_linked(struct io_uring *ring)
81 {
82 struct io_uring_sqe *sqe;
83 struct io_uring_cqe *cqe;
84 int ret, fds[2], fd, i;
85
86 if (pipe(fds) < 0) {
87 perror("pipe");
88 return T_EXIT_FAIL;
89 }
90
91 ret = io_uring_register_files(ring, &fds[0], 1);
92 if (ret) {
93 fprintf(stderr, "failed register files %d\n", ret);
94 return T_EXIT_FAIL;
95 }
96
97 sqe = io_uring_get_sqe(ring);
98 io_uring_prep_nop(sqe);
99 sqe->flags |= IOSQE_IO_LINK;
100 sqe->user_data = 1;
101
102 sqe = io_uring_get_sqe(ring);
103 io_uring_prep_fixed_fd_install(sqe, 0, 0);
104 sqe->user_data = 2;
105
106 ret = io_uring_submit(ring);
107 if (ret != 2) {
108 fprintf(stderr, "submit: %d\n", ret);
109 return T_EXIT_FAIL;
110 }
111
112 fd = -1;
113 for (i = 0; i < 2; i++) {
114 ret = io_uring_wait_cqe(ring, &cqe);
115 if (ret) {
116 fprintf(stderr, "wait cqe %d\n", ret);
117 return T_EXIT_FAIL;
118 }
119 if (cqe->res < 0) {
120 fprintf(stderr, "unexpected cqe res %d\n", cqe->res);
121 return T_EXIT_FAIL;
122 }
123 if (cqe->user_data == 2)
124 fd = cqe->res;
125 io_uring_cqe_seen(ring, cqe);
126 }
127
128 close(fds[0]);
129 close(fds[1]);
130 if (fd != -1)
131 close(fd);
132 io_uring_unregister_files(ring);
133 return T_EXIT_PASS;
134 }
135
136 /* test not setting IOSQE_FIXED_FILE */
test_not_fixed(struct io_uring * ring)137 static int test_not_fixed(struct io_uring *ring)
138 {
139 struct io_uring_sqe *sqe;
140 struct io_uring_cqe *cqe;
141 int ret, fds[2];
142
143 if (pipe(fds) < 0) {
144 perror("pipe");
145 return T_EXIT_FAIL;
146 }
147
148 ret = io_uring_register_files(ring, &fds[0], 1);
149 if (ret) {
150 fprintf(stderr, "failed register files %d\n", ret);
151 return T_EXIT_FAIL;
152 }
153
154 sqe = io_uring_get_sqe(ring);
155 io_uring_prep_fixed_fd_install(sqe, 0, 0);
156 sqe->flags &= ~IOSQE_FIXED_FILE;
157 io_uring_submit(ring);
158
159 ret = io_uring_wait_cqe(ring, &cqe);
160 if (ret) {
161 fprintf(stderr, "wait cqe %d\n", ret);
162 return T_EXIT_FAIL;
163 }
164 if (cqe->res != -EBADF) {
165 fprintf(stderr, "unexpected cqe res %d\n", cqe->res);
166 return T_EXIT_FAIL;
167 }
168
169 io_uring_cqe_seen(ring, cqe);
170
171 close(fds[0]);
172 close(fds[1]);
173 io_uring_unregister_files(ring);
174
175 return T_EXIT_PASS;
176 }
177
178 /* test invalid direct descriptor indexes */
test_bad_fd(struct io_uring * ring,int some_fd)179 static int test_bad_fd(struct io_uring *ring, int some_fd)
180 {
181 struct io_uring_sqe *sqe;
182 struct io_uring_cqe *cqe;
183 int ret;
184
185 sqe = io_uring_get_sqe(ring);
186 io_uring_prep_fixed_fd_install(sqe, some_fd, 0);
187 io_uring_submit(ring);
188
189 ret = io_uring_wait_cqe(ring, &cqe);
190 if (ret) {
191 fprintf(stderr, "wait cqe %d\n", ret);
192 return T_EXIT_FAIL;
193 }
194 if (cqe->res != -EBADF) {
195 fprintf(stderr, "unexpected cqe res %d\n", cqe->res);
196 return T_EXIT_FAIL;
197 }
198
199 io_uring_cqe_seen(ring, cqe);
200 return T_EXIT_PASS;
201 }
202
203 /* test basic functionality of shifting a direct descriptor to a normal file */
test_working(struct io_uring * ring)204 static int test_working(struct io_uring *ring)
205 {
206 struct io_uring_sqe *sqe;
207 struct io_uring_cqe *cqe;
208 int ret, fds[2];
209 char buf[32];
210
211 if (pipe(fds) < 0) {
212 perror("pipe");
213 return T_EXIT_FAIL;
214 }
215
216 /* register read side */
217 ret = io_uring_register_files(ring, &fds[0], 1);
218 if (ret) {
219 fprintf(stderr, "failed register files %d\n", ret);
220 return T_EXIT_FAIL;
221 }
222
223 /* close normal descriptor */
224 close(fds[0]);
225
226 /* normal read should fail */
227 ret = read(fds[0], buf, 1);
228 if (ret != -1) {
229 fprintf(stderr, "unexpected read ret %d\n", ret);
230 return T_EXIT_FAIL;
231 }
232 if (errno != EBADF) {
233 fprintf(stderr, "unexpected read failure %d\n", errno);
234 return T_EXIT_FAIL;
235 }
236
237 /* verify we can read the data */
238 sqe = io_uring_get_sqe(ring);
239 io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0);
240 sqe->flags |= IOSQE_FIXED_FILE;
241 io_uring_submit(ring);
242
243 /* put some data in the pipe */
244 ret = write(fds[1], "Hello", 5);
245 if (ret < 0) {
246 perror("write");
247 return T_EXIT_FAIL;
248 } else if (ret != 5) {
249 fprintf(stderr, "short write %d\n", ret);
250 return T_EXIT_FAIL;
251 }
252
253 ret = io_uring_wait_cqe(ring, &cqe);
254 if (ret) {
255 fprintf(stderr, "wait cqe %d\n", ret);
256 return T_EXIT_FAIL;
257 }
258 if (cqe->res != 5) {
259 fprintf(stderr, "weird pipe read ret %d\n", cqe->res);
260 return T_EXIT_FAIL;
261 }
262 io_uring_cqe_seen(ring, cqe);
263
264 /* fixed pipe read worked, now re-install as a regular fd */
265 sqe = io_uring_get_sqe(ring);
266 io_uring_prep_fixed_fd_install(sqe, 0, 0);
267 io_uring_submit(ring);
268
269 ret = io_uring_wait_cqe(ring, &cqe);
270 if (ret) {
271 fprintf(stderr, "wait cqe %d\n", ret);
272 return T_EXIT_FAIL;
273 }
274 if (cqe->res == -EINVAL) {
275 no_fd_install = 1;
276 return T_EXIT_SKIP;
277 }
278 if (cqe->res < 0) {
279 fprintf(stderr, "failed install fd: %d\n", cqe->res);
280 return T_EXIT_FAIL;
281 }
282 /* stash new pipe read side fd in old spot */
283 fds[0] = cqe->res;
284 io_uring_cqe_seen(ring, cqe);
285
286 ret = write(fds[1], "Hello", 5);
287 if (ret < 0) {
288 perror("write");
289 return T_EXIT_FAIL;
290 } else if (ret != 5) {
291 fprintf(stderr, "short write %d\n", ret);
292 return T_EXIT_FAIL;
293 }
294
295 /* normal pipe read should now work with new fd */
296 ret = read(fds[0], buf, sizeof(buf));
297 if (ret != 5) {
298 fprintf(stderr, "unexpected read ret %d\n", ret);
299 return T_EXIT_FAIL;
300 }
301
302 /* close fixed file */
303 sqe = io_uring_get_sqe(ring);
304 io_uring_prep_close_direct(sqe, 0);
305 io_uring_submit(ring);
306
307 ret = io_uring_wait_cqe(ring, &cqe);
308 if (ret) {
309 fprintf(stderr, "wait cqe %d\n", ret);
310 return T_EXIT_FAIL;
311 }
312 if (cqe->res) {
313 fprintf(stderr, "close fixed fd %d\n", cqe->res);
314 return T_EXIT_FAIL;
315 }
316 io_uring_cqe_seen(ring, cqe);
317
318 ret = write(fds[1], "Hello", 5);
319 if (ret < 0) {
320 perror("write");
321 return T_EXIT_FAIL;
322 } else if (ret != 5) {
323 fprintf(stderr, "short write %d\n", ret);
324 return T_EXIT_FAIL;
325 }
326
327 /* normal pipe read should still work with new fd */
328 ret = read(fds[0], buf, sizeof(buf));
329 if (ret != 5) {
330 fprintf(stderr, "unexpected read ret %d\n", ret);
331 return T_EXIT_FAIL;
332 }
333
334 /* fixed fd pipe read should now fail */
335 sqe = io_uring_get_sqe(ring);
336 io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0);
337 sqe->flags = IOSQE_FIXED_FILE;
338 io_uring_submit(ring);
339
340 /* put some data in the pipe */
341 ret = write(fds[1], "Hello", 5);
342 if (ret < 0) {
343 perror("write");
344 return T_EXIT_FAIL;
345 } else if (ret != 5) {
346 fprintf(stderr, "short write %d\n", ret);
347 return T_EXIT_FAIL;
348 }
349
350 ret = io_uring_wait_cqe(ring, &cqe);
351 if (ret) {
352 fprintf(stderr, "wait cqe %d\n", ret);
353 return T_EXIT_FAIL;
354 }
355 if (cqe->res != -EBADF) {
356 fprintf(stderr, "weird pipe read ret %d\n", cqe->res);
357 return T_EXIT_FAIL;
358 }
359 io_uring_cqe_seen(ring, cqe);
360
361 close(fds[0]);
362 close(fds[1]);
363 io_uring_unregister_files(ring);
364 return T_EXIT_PASS;
365 }
366
test_creds(struct io_uring * ring,int async)367 static int test_creds(struct io_uring *ring, int async)
368 {
369 struct io_uring_sqe *sqe;
370 struct io_uring_cqe *cqe;
371 int cred_id, ret, fds[2];
372
373 if (pipe(fds) < 0) {
374 perror("pipe");
375 return T_EXIT_FAIL;
376 }
377
378 ret = io_uring_register_files(ring, &fds[0], 1);
379 if (ret) {
380 fprintf(stderr, "failed register files %d\n", ret);
381 return T_EXIT_FAIL;
382 }
383
384 cred_id = io_uring_register_personality(ring);
385 if (cred_id < 0) {
386 fprintf(stderr, "Failed registering creds: %d\n", cred_id);
387 return T_EXIT_FAIL;
388 }
389
390 /* check that asking for creds fails */
391 sqe = io_uring_get_sqe(ring);
392 io_uring_prep_fixed_fd_install(sqe, 0, 0);
393 if (async)
394 sqe->flags |= IOSQE_ASYNC;
395 sqe->personality = cred_id;
396 io_uring_submit(ring);
397
398 ret = io_uring_wait_cqe(ring, &cqe);
399 if (ret) {
400 fprintf(stderr, "wait cqe %d\n", ret);
401 return T_EXIT_FAIL;
402 }
403 if (cqe->res > 0) {
404 fprintf(stderr, "install succeeded with creds\n");
405 return T_EXIT_FAIL;
406 }
407 if (cqe->res != -EPERM) {
408 fprintf(stderr, "unexpected cqe res %d\n", cqe->res);
409 return T_EXIT_FAIL;
410 }
411 io_uring_cqe_seen(ring, cqe);
412
413 close(fds[0]);
414 close(fds[1]);
415 io_uring_unregister_files(ring);
416 io_uring_unregister_personality(ring, cred_id);
417 return T_EXIT_PASS;
418 }
419
main(int argc,char * argv[])420 int main(int argc, char *argv[])
421 {
422 struct io_uring ring;
423 int ret;
424
425 if (argc > 1)
426 return T_EXIT_SKIP;
427
428 ret = io_uring_queue_init(4, &ring, 0);
429 if (ret) {
430 fprintf(stderr, "ring setup failed: %d\n", ret);
431 return T_EXIT_FAIL;
432 }
433
434 ret = test_working(&ring);
435 if (ret != T_EXIT_PASS) {
436 if (ret == T_EXIT_FAIL)
437 fprintf(stderr, "test_working failed\n");
438 return ret;
439 }
440 if (no_fd_install)
441 return T_EXIT_SKIP;
442
443 ret = test_bad_fd(&ring, 0);
444 if (ret != T_EXIT_PASS) {
445 if (ret == T_EXIT_FAIL)
446 fprintf(stderr, "test_bad_fd 0 failed\n");
447 return ret;
448 }
449
450 ret = test_bad_fd(&ring, 500);
451 if (ret != T_EXIT_PASS) {
452 if (ret == T_EXIT_FAIL)
453 fprintf(stderr, "test_bad_fd 500 failed\n");
454 return ret;
455 }
456
457 ret = test_not_fixed(&ring);
458 if (ret != T_EXIT_PASS) {
459 if (ret == T_EXIT_FAIL)
460 fprintf(stderr, "test_not_fixed failed\n");
461 return ret;
462 }
463
464 ret = test_flags(&ring, 0);
465 if (ret != T_EXIT_PASS) {
466 if (ret == T_EXIT_FAIL)
467 fprintf(stderr, "test_flags 0 failed\n");
468 return ret;
469 }
470
471 ret = test_flags(&ring, 1);
472 if (ret != T_EXIT_PASS) {
473 if (ret == T_EXIT_FAIL)
474 fprintf(stderr, "test_flags 1 failed\n");
475 return ret;
476 }
477
478 ret = test_creds(&ring, 0);
479 if (ret != T_EXIT_PASS) {
480 if (ret == T_EXIT_FAIL)
481 fprintf(stderr, "test_creds 0 failed\n");
482 return ret;
483 }
484
485 ret = test_creds(&ring, 1);
486 if (ret != T_EXIT_PASS) {
487 if (ret == T_EXIT_FAIL)
488 fprintf(stderr, "test_creds 1 failed\n");
489 return ret;
490 }
491
492 ret = test_linked(&ring);
493 if (ret != T_EXIT_PASS) {
494 if (ret == T_EXIT_FAIL)
495 fprintf(stderr, "test_linked failed\n");
496 return ret;
497 }
498
499 return T_EXIT_PASS;
500 }
501