1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
#include "../config-host.h"
/* SPDX-License-Identifier: MIT */
/*
* Description: test for a struct file reference leak in IORING_OP_NOP.
*
* When an SQE carries IOSQE_FIXED_FILE (which sets REQ_F_FIXED_FILE at
* prep time) together with IORING_NOP_FILE but *without*
* IORING_NOP_FIXED_FILE, io_nop() takes a normal file reference via
* io_file_get_normal(), but io_put_file() won't drop the ref as
* REQ_F_FIXED_FILE is set.
*
* Reported-by: [email protected]
* Fixes: a85f31052bce ("io_uring/nop: add support to fixed and normal files")
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include "liburing.h"
#include "helpers.h"
#ifndef IORING_NOP_FILE
#define IORING_NOP_FILE (1U << 1)
#endif
#ifndef IORING_NOP_FIXED_FILE
#define IORING_NOP_FIXED_FILE (1U << 2)
#endif
int main(int argc, char *argv[])
{
struct io_uring ring;
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int pipefd[2];
char c = 'x';
ssize_t n;
int ret;
if (argc > 1)
return T_EXIT_SKIP;
/* don't die on the EPIPE write in the non-buggy case */
signal(SIGPIPE, SIG_IGN);
ret = io_uring_queue_init(8, &ring, 0);
if (ret) {
fprintf(stderr, "queue_init: %d\n", ret);
return T_EXIT_FAIL;
}
if (pipe(pipefd) < 0) {
perror("pipe");
return T_EXIT_FAIL;
}
sqe = io_uring_get_sqe(&ring);
io_uring_prep_nop(sqe);
sqe->nop_flags = IORING_NOP_FILE;
sqe->fd = pipefd[0];
sqe->flags |= IOSQE_FIXED_FILE;
ret = io_uring_submit(&ring);
if (ret != 1) {
fprintf(stderr, "submit: %d\n", ret);
return T_EXIT_FAIL;
}
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret) {
fprintf(stderr, "wait_cqe: %d\n", ret);
return T_EXIT_FAIL;
}
io_uring_cqe_seen(&ring, cqe);
/*
* Drop our own reference on the read end. If the kernel leaked the
* NOP's reference, the read end stays alive and the write below
* succeeds. If correct, the read end is gone and we get -EPIPE.
*/
close(pipefd[0]);
n = write(pipefd[1], &c, 1);
if (n == 1) {
fprintf(stderr, "BUG: write to pipe with closed read end "
"succeeded -- NOP leaked a file reference\n");
ret = T_EXIT_FAIL;
} else if (n < 0 && errno == EPIPE) {
ret = T_EXIT_PASS;
} else {
fprintf(stderr, "unexpected write result: n=%zd errno=%d\n",
n, errno);
ret = T_EXIT_FAIL;
}
close(pipefd[1]);
io_uring_queue_exit(&ring);
return ret;
}
|