1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: test restrictions
4 *
5 */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <poll.h>
13 #include <sys/eventfd.h>
14
15 #include "liburing.h"
16 #include "helpers.h"
17
test_restrictions_sqe_op(void)18 static int test_restrictions_sqe_op(void)
19 {
20 struct io_uring_restriction res[2];
21 struct io_uring_sqe *sqe;
22 struct io_uring_cqe *cqe;
23 struct io_uring ring;
24 int ret, pipe1[2];
25
26 uint64_t ptr;
27 struct iovec vec = {
28 .iov_base = &ptr,
29 .iov_len = sizeof(ptr)
30 };
31
32 if (pipe(pipe1) != 0) {
33 perror("pipe");
34 return T_EXIT_FAIL;
35 }
36
37 ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
38 if (ret) {
39 if (ret == -EINVAL)
40 return T_EXIT_SKIP;
41 fprintf(stderr, "ring setup failed: %d\n", ret);
42 return T_EXIT_FAIL;
43 }
44
45 res[0].opcode = IORING_RESTRICTION_SQE_OP;
46 res[0].sqe_op = IORING_OP_WRITEV;
47
48 res[1].opcode = IORING_RESTRICTION_SQE_OP;
49 res[1].sqe_op = IORING_OP_WRITE;
50
51 ret = io_uring_register_restrictions(&ring, res, 2);
52 if (ret) {
53 if (ret == -EINVAL)
54 return T_EXIT_SKIP;
55
56 fprintf(stderr, "failed to register restrictions: %d\n", ret);
57 return T_EXIT_FAIL;
58 }
59
60 ret = io_uring_enable_rings(&ring);
61 if (ret) {
62 fprintf(stderr, "ring enabling failed: %d\n", ret);
63 return T_EXIT_FAIL;
64 }
65
66 sqe = io_uring_get_sqe(&ring);
67 io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
68 sqe->user_data = 1;
69
70 sqe = io_uring_get_sqe(&ring);
71 io_uring_prep_readv(sqe, pipe1[0], &vec, 1, 0);
72 sqe->user_data = 2;
73
74 ret = io_uring_submit(&ring);
75 if (ret != 2) {
76 fprintf(stderr, "submit: %d\n", ret);
77 return T_EXIT_FAIL;
78 }
79
80 for (int i = 0; i < 2; i++) {
81 ret = io_uring_wait_cqe(&ring, &cqe);
82 if (ret) {
83 fprintf(stderr, "wait: %d\n", ret);
84 return T_EXIT_FAIL;
85 }
86
87 switch (cqe->user_data) {
88 case 1: /* writev */
89 if (cqe->res != sizeof(ptr)) {
90 fprintf(stderr, "write res: %d\n", cqe->res);
91 return T_EXIT_FAIL;
92 }
93
94 break;
95 case 2: /* readv should be denied */
96 if (cqe->res != -EACCES) {
97 fprintf(stderr, "read res: %d\n", cqe->res);
98 return T_EXIT_FAIL;
99 }
100 break;
101 }
102 io_uring_cqe_seen(&ring, cqe);
103 }
104
105 io_uring_queue_exit(&ring);
106 return T_EXIT_PASS;
107 }
108
test_restrictions_register_op(void)109 static int test_restrictions_register_op(void)
110 {
111 struct io_uring_restriction res[1];
112 struct io_uring ring;
113 int ret, pipe1[2];
114
115 uint64_t ptr;
116 struct iovec vec = {
117 .iov_base = &ptr,
118 .iov_len = sizeof(ptr)
119 };
120
121 if (pipe(pipe1) != 0) {
122 perror("pipe");
123 return T_EXIT_FAIL;
124 }
125
126 ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
127 if (ret) {
128 fprintf(stderr, "ring setup failed: %d\n", ret);
129 return T_EXIT_FAIL;
130 }
131
132 res[0].opcode = IORING_RESTRICTION_REGISTER_OP;
133 res[0].register_op = IORING_REGISTER_BUFFERS;
134
135 ret = io_uring_register_restrictions(&ring, res, 1);
136 if (ret) {
137 if (ret == -EINVAL)
138 return T_EXIT_SKIP;
139
140 fprintf(stderr, "failed to register restrictions: %d\n", ret);
141 return T_EXIT_FAIL;
142 }
143
144 ret = io_uring_enable_rings(&ring);
145 if (ret) {
146 fprintf(stderr, "ring enabling failed: %d\n", ret);
147 return T_EXIT_FAIL;
148 }
149
150 ret = io_uring_register_buffers(&ring, &vec, 1);
151 if (ret) {
152 fprintf(stderr, "io_uring_register_buffers failed: %d\n", ret);
153 return T_EXIT_FAIL;
154 }
155
156 ret = io_uring_register_files(&ring, pipe1, 2);
157 if (ret != -EACCES) {
158 fprintf(stderr, "io_uring_register_files ret: %d\n", ret);
159 return T_EXIT_FAIL;
160 }
161
162 io_uring_queue_exit(&ring);
163 return T_EXIT_PASS;
164 }
165
test_restrictions_fixed_file(void)166 static int test_restrictions_fixed_file(void)
167 {
168 struct io_uring_restriction res[4];
169 struct io_uring_sqe *sqe;
170 struct io_uring_cqe *cqe;
171 struct io_uring ring;
172 int ret, pipe1[2];
173
174 uint64_t ptr;
175 struct iovec vec = {
176 .iov_base = &ptr,
177 .iov_len = sizeof(ptr)
178 };
179
180 if (pipe(pipe1) != 0) {
181 perror("pipe");
182 return T_EXIT_FAIL;
183 }
184
185 ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
186 if (ret) {
187 fprintf(stderr, "ring setup failed: %d\n", ret);
188 return T_EXIT_FAIL;
189 }
190
191 res[0].opcode = IORING_RESTRICTION_SQE_OP;
192 res[0].sqe_op = IORING_OP_WRITEV;
193
194 res[1].opcode = IORING_RESTRICTION_SQE_OP;
195 res[1].sqe_op = IORING_OP_READV;
196
197 res[2].opcode = IORING_RESTRICTION_SQE_FLAGS_REQUIRED;
198 res[2].sqe_flags = IOSQE_FIXED_FILE;
199
200 res[3].opcode = IORING_RESTRICTION_REGISTER_OP;
201 res[3].register_op = IORING_REGISTER_FILES;
202
203 ret = io_uring_register_restrictions(&ring, res, 4);
204 if (ret) {
205 if (ret == -EINVAL)
206 return T_EXIT_SKIP;
207
208 fprintf(stderr, "failed to register restrictions: %d\n", ret);
209 return T_EXIT_FAIL;
210 }
211
212 ret = io_uring_enable_rings(&ring);
213 if (ret) {
214 fprintf(stderr, "ring enabling failed: %d\n", ret);
215 return T_EXIT_FAIL;
216 }
217
218 ret = io_uring_register_files(&ring, pipe1, 2);
219 if (ret) {
220 fprintf(stderr, "io_uring_register_files ret: %d\n", ret);
221 return T_EXIT_FAIL;
222 }
223
224 sqe = io_uring_get_sqe(&ring);
225 io_uring_prep_writev(sqe, 1, &vec, 1, 0);
226 io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
227 sqe->user_data = 1;
228
229 sqe = io_uring_get_sqe(&ring);
230 io_uring_prep_readv(sqe, 0, &vec, 1, 0);
231 io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
232 sqe->user_data = 2;
233
234 sqe = io_uring_get_sqe(&ring);
235 io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
236 sqe->user_data = 3;
237
238 ret = io_uring_submit(&ring);
239 if (ret != 3) {
240 fprintf(stderr, "submit: %d\n", ret);
241 return T_EXIT_FAIL;
242 }
243
244 for (int i = 0; i < 3; i++) {
245 ret = io_uring_wait_cqe(&ring, &cqe);
246 if (ret) {
247 fprintf(stderr, "wait: %d\n", ret);
248 return T_EXIT_FAIL;
249 }
250
251 switch (cqe->user_data) {
252 case 1: /* writev */
253 if (cqe->res != sizeof(ptr)) {
254 fprintf(stderr, "write res: %d\n", cqe->res);
255 return T_EXIT_FAIL;
256 }
257
258 break;
259 case 2: /* readv */
260 if (cqe->res != sizeof(ptr)) {
261 fprintf(stderr, "read res: %d\n", cqe->res);
262 return T_EXIT_FAIL;
263 }
264 break;
265 case 3: /* writev without fixed_file should be denied */
266 if (cqe->res != -EACCES) {
267 fprintf(stderr, "write res: %d\n", cqe->res);
268 return T_EXIT_FAIL;
269 }
270 break;
271 }
272 io_uring_cqe_seen(&ring, cqe);
273 }
274
275 io_uring_queue_exit(&ring);
276 return T_EXIT_PASS;
277 }
278
test_restrictions_flags(void)279 static int test_restrictions_flags(void)
280 {
281 struct io_uring_restriction res[3];
282 struct io_uring_sqe *sqe;
283 struct io_uring_cqe *cqe;
284 struct io_uring ring;
285 int ret, pipe1[2];
286
287 uint64_t ptr;
288 struct iovec vec = {
289 .iov_base = &ptr,
290 .iov_len = sizeof(ptr)
291 };
292
293 if (pipe(pipe1) != 0) {
294 perror("pipe");
295 return T_EXIT_FAIL;
296 }
297
298 ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
299 if (ret) {
300 fprintf(stderr, "ring setup failed: %d\n", ret);
301 return T_EXIT_FAIL;
302 }
303
304 res[0].opcode = IORING_RESTRICTION_SQE_OP;
305 res[0].sqe_op = IORING_OP_WRITEV;
306
307 res[1].opcode = IORING_RESTRICTION_SQE_FLAGS_ALLOWED;
308 res[1].sqe_flags = IOSQE_ASYNC | IOSQE_IO_LINK;
309
310 res[2].opcode = IORING_RESTRICTION_SQE_FLAGS_REQUIRED;
311 res[2].sqe_flags = IOSQE_FIXED_FILE;
312
313 ret = io_uring_register_restrictions(&ring, res, 3);
314 if (ret) {
315 if (ret == -EINVAL)
316 return T_EXIT_SKIP;
317
318 fprintf(stderr, "failed to register restrictions: %d\n", ret);
319 return T_EXIT_FAIL;
320 }
321
322 ret = io_uring_register_files(&ring, pipe1, 2);
323 if (ret) {
324 fprintf(stderr, "io_uring_register_files ret: %d\n", ret);
325 return T_EXIT_FAIL;
326 }
327
328 ret = io_uring_enable_rings(&ring);
329 if (ret) {
330 fprintf(stderr, "ring enabling failed: %d\n", ret);
331 return T_EXIT_FAIL;
332 }
333
334 sqe = io_uring_get_sqe(&ring);
335 io_uring_prep_writev(sqe, 1, &vec, 1, 0);
336 io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
337 sqe->user_data = 1;
338
339 sqe = io_uring_get_sqe(&ring);
340 io_uring_prep_writev(sqe, 1, &vec, 1, 0);
341 io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE | IOSQE_ASYNC);
342 sqe->user_data = 2;
343
344 sqe = io_uring_get_sqe(&ring);
345 io_uring_prep_writev(sqe, 1, &vec, 1, 0);
346 io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE | IOSQE_IO_LINK);
347 sqe->user_data = 3;
348
349 ret = io_uring_submit(&ring);
350 if (ret != 3) {
351 fprintf(stderr, "submit: %d\n", ret);
352 return T_EXIT_FAIL;
353 }
354
355 sqe = io_uring_get_sqe(&ring);
356 io_uring_prep_writev(sqe, 1, &vec, 1, 0);
357 io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE | IOSQE_IO_DRAIN);
358 sqe->user_data = 4;
359
360 ret = io_uring_submit(&ring);
361 if (ret != 1) {
362 fprintf(stderr, "submit: %d\n", ret);
363 return T_EXIT_FAIL;
364 }
365
366 sqe = io_uring_get_sqe(&ring);
367 io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
368 io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
369 sqe->user_data = 5;
370
371 ret = io_uring_submit(&ring);
372 if (ret != 1) {
373 fprintf(stderr, "submit: %d\n", ret);
374 return T_EXIT_FAIL;
375 }
376
377 sqe = io_uring_get_sqe(&ring);
378 io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
379 io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
380 sqe->user_data = 6;
381
382 ret = io_uring_submit(&ring);
383 if (ret != 1) {
384 fprintf(stderr, "submit: %d\n", ret);
385 return T_EXIT_FAIL;
386 }
387
388 sqe = io_uring_get_sqe(&ring);
389 io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
390 sqe->user_data = 7;
391
392 ret = io_uring_submit(&ring);
393 if (ret != 1) {
394 fprintf(stderr, "submit: %d\n", ret);
395 return T_EXIT_FAIL;
396 }
397
398 for (int i = 0; i < 7; i++) {
399 ret = io_uring_wait_cqe(&ring, &cqe);
400 if (ret) {
401 fprintf(stderr, "wait: %d\n", ret);
402 return T_EXIT_FAIL;
403 }
404
405 switch (cqe->user_data) {
406 case 1: /* writev - flags = IOSQE_FIXED_FILE */
407 case 2: /* writev - flags = IOSQE_FIXED_FILE | IOSQE_ASYNC */
408 case 3: /* writev - flags = IOSQE_FIXED_FILE | IOSQE_IO_LINK */
409 if (cqe->res != sizeof(ptr)) {
410 fprintf(stderr, "write res: %d user_data %" PRIu64 "\n",
411 cqe->res, (uint64_t) cqe->user_data);
412 return T_EXIT_FAIL;
413 }
414
415 break;
416 case 4: /* writev - flags = IOSQE_FIXED_FILE | IOSQE_IO_DRAIN */
417 case 5: /* writev - flags = IOSQE_IO_DRAIN */
418 case 6: /* writev - flags = IOSQE_ASYNC */
419 case 7: /* writev - flags = 0 */
420 if (cqe->res != -EACCES) {
421 fprintf(stderr, "write res: %d user_data %" PRIu64 "\n",
422 cqe->res, (uint64_t) cqe->user_data);
423 return T_EXIT_FAIL;
424 }
425 break;
426 }
427 io_uring_cqe_seen(&ring, cqe);
428 }
429
430 io_uring_queue_exit(&ring);
431 return T_EXIT_PASS;
432 }
433
test_restrictions_empty(void)434 static int test_restrictions_empty(void)
435 {
436 struct io_uring_restriction res[0];
437 struct io_uring_sqe *sqe;
438 struct io_uring_cqe *cqe;
439 struct io_uring ring;
440 int ret, pipe1[2];
441
442 uint64_t ptr;
443 struct iovec vec = {
444 .iov_base = &ptr,
445 .iov_len = sizeof(ptr)
446 };
447
448 if (pipe(pipe1) != 0) {
449 perror("pipe");
450 return T_EXIT_FAIL;
451 }
452
453 ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
454 if (ret) {
455 fprintf(stderr, "ring setup failed: %d\n", ret);
456 return T_EXIT_FAIL;
457 }
458
459 ret = io_uring_register_restrictions(&ring, res, 0);
460 if (ret) {
461 if (ret == -EINVAL)
462 return T_EXIT_SKIP;
463
464 fprintf(stderr, "failed to register restrictions: %d\n", ret);
465 return T_EXIT_FAIL;
466 }
467
468 ret = io_uring_enable_rings(&ring);
469 if (ret) {
470 fprintf(stderr, "ring enabling failed: %d\n", ret);
471 return T_EXIT_FAIL;
472 }
473
474 ret = io_uring_register_buffers(&ring, &vec, 1);
475 if (ret != -EACCES) {
476 fprintf(stderr, "io_uring_register_buffers ret: %d\n", ret);
477 return T_EXIT_FAIL;
478 }
479
480 ret = io_uring_register_files(&ring, pipe1, 2);
481 if (ret != -EACCES) {
482 fprintf(stderr, "io_uring_register_files ret: %d\n", ret);
483 return T_EXIT_FAIL;
484 }
485
486 sqe = io_uring_get_sqe(&ring);
487 io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
488
489 ret = io_uring_submit(&ring);
490 if (ret != 1) {
491 fprintf(stderr, "submit: %d\n", ret);
492 return T_EXIT_FAIL;
493 }
494
495 ret = io_uring_wait_cqe(&ring, &cqe);
496 if (ret) {
497 fprintf(stderr, "wait: %d\n", ret);
498 return T_EXIT_FAIL;
499 }
500
501 if (cqe->res != -EACCES) {
502 fprintf(stderr, "write res: %d\n", cqe->res);
503 return T_EXIT_FAIL;
504 }
505
506 io_uring_cqe_seen(&ring, cqe);
507
508 io_uring_queue_exit(&ring);
509 return T_EXIT_PASS;
510 }
511
test_restrictions_rings_not_disabled(void)512 static int test_restrictions_rings_not_disabled(void)
513 {
514 struct io_uring_restriction res[1];
515 struct io_uring ring;
516 int ret;
517
518 ret = io_uring_queue_init(8, &ring, 0);
519 if (ret) {
520 fprintf(stderr, "ring setup failed: %d\n", ret);
521 return T_EXIT_FAIL;
522 }
523
524 res[0].opcode = IORING_RESTRICTION_SQE_OP;
525 res[0].sqe_op = IORING_OP_WRITEV;
526
527 ret = io_uring_register_restrictions(&ring, res, 1);
528 if (ret != -EBADFD) {
529 fprintf(stderr, "io_uring_register_restrictions ret: %d\n",
530 ret);
531 return T_EXIT_FAIL;
532 }
533
534 io_uring_queue_exit(&ring);
535 return T_EXIT_PASS;
536 }
537
test_restrictions_rings_disabled(void)538 static int test_restrictions_rings_disabled(void)
539 {
540 struct io_uring_sqe *sqe;
541 struct io_uring ring;
542 int ret;
543
544 ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
545 if (ret) {
546 fprintf(stderr, "ring setup failed: %d\n", ret);
547 return T_EXIT_FAIL;
548 }
549
550 sqe = io_uring_get_sqe(&ring);
551 io_uring_prep_nop(sqe);
552
553 ret = io_uring_submit(&ring);
554 if (ret != -EBADFD) {
555 fprintf(stderr, "submit: %d\n", ret);
556 return T_EXIT_FAIL;
557 }
558
559 io_uring_queue_exit(&ring);
560 return T_EXIT_PASS;
561 }
562
main(int argc,char * argv[])563 int main(int argc, char *argv[])
564 {
565 int ret;
566
567 if (argc > 1)
568 return 0;
569
570 ret = test_restrictions_sqe_op();
571 if (ret == T_EXIT_SKIP) {
572 printf("test_restrictions_sqe_op: skipped\n");
573 return T_EXIT_SKIP;
574 } else if (ret == T_EXIT_FAIL) {
575 fprintf(stderr, "test_restrictions_sqe_op failed\n");
576 return ret;
577 }
578
579 ret = test_restrictions_register_op();
580 if (ret == T_EXIT_SKIP) {
581 printf("test_restrictions_register_op: skipped\n");
582 return T_EXIT_SKIP;
583 } else if (ret == T_EXIT_FAIL) {
584 fprintf(stderr, "test_restrictions_register_op failed\n");
585 return ret;
586 }
587
588 ret = test_restrictions_fixed_file();
589 if (ret == T_EXIT_SKIP) {
590 printf("test_restrictions_fixed_file: skipped\n");
591 return T_EXIT_SKIP;
592 } else if (ret == T_EXIT_FAIL) {
593 fprintf(stderr, "test_restrictions_fixed_file failed\n");
594 return ret;
595 }
596
597 ret = test_restrictions_flags();
598 if (ret == T_EXIT_SKIP) {
599 printf("test_restrictions_flags: skipped\n");
600 return T_EXIT_SKIP;
601 } else if (ret == T_EXIT_FAIL) {
602 fprintf(stderr, "test_restrictions_flags failed\n");
603 return ret;
604 }
605
606 ret = test_restrictions_empty();
607 if (ret == T_EXIT_SKIP) {
608 printf("test_restrictions_empty: skipped\n");
609 return T_EXIT_SKIP;
610 } else if (ret == T_EXIT_FAIL) {
611 fprintf(stderr, "test_restrictions_empty failed\n");
612 return ret;
613 }
614
615 ret = test_restrictions_rings_not_disabled();
616 if (ret == T_EXIT_SKIP) {
617 printf("test_restrictions_rings_not_disabled: skipped\n");
618 return T_EXIT_SKIP;
619 } else if (ret == T_EXIT_FAIL) {
620 fprintf(stderr, "test_restrictions_rings_not_disabled failed\n");
621 return ret;
622 }
623
624 ret = test_restrictions_rings_disabled();
625 if (ret == T_EXIT_SKIP) {
626 printf("test_restrictions_rings_disabled: skipped\n");
627 return T_EXIT_SKIP;
628 } else if (ret == T_EXIT_FAIL) {
629 fprintf(stderr, "test_restrictions_rings_disabled failed\n");
630 return ret;
631 }
632
633 return T_EXIT_PASS;
634 }
635