• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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