1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: check that racing wakeups don't re-issue a poll multishot,
4 * this can leak ring provided buffers. also test if ring
5 * provided buffers for regular receive can leak if we hit a
6 * poll race.
7 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <pthread.h>
12 #include <string.h>
13 #include <sys/socket.h>
14
15 #include "liburing.h"
16 #include "helpers.h"
17
18 #define NREQS 64
19 #define BUF_SIZE 64
20
21 static int no_buf_ring;
22
23 struct data {
24 pthread_barrier_t barrier;
25 int fd;
26 };
27
thread(void * data)28 static void *thread(void *data)
29 {
30 struct data *d = data;
31 char buf[BUF_SIZE];
32 int ret, i, fd;
33
34 memset(buf, 0x5a, BUF_SIZE);
35 pthread_barrier_wait(&d->barrier);
36 fd = d->fd;
37 for (i = 0; i < NREQS; i++) {
38 ret = write(fd, buf, sizeof(buf));
39 if (ret != BUF_SIZE) {
40 if (ret < 0) {
41 perror("write");
42 printf("bad fd %d\n", fd);
43 } else
44 fprintf(stderr, "wrote short %d\n", ret);
45 }
46 }
47 return NULL;
48 }
49
test(struct io_uring * ring,struct data * d)50 static int test(struct io_uring *ring, struct data *d)
51 {
52 struct io_uring_buf_ring *br;
53 struct io_uring_sqe *sqe;
54 struct io_uring_cqe *cqe;
55 int fd[2], ret, i;
56 pthread_t t;
57 void *buf, *ptr;
58 void *ret2;
59
60 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) < 0) {
61 perror("socketpair");
62 return T_EXIT_FAIL;
63 }
64
65 d->fd = fd[1];
66
67 if (posix_memalign((void **) &buf, 16384, BUF_SIZE * NREQS))
68 return T_EXIT_FAIL;
69
70 br = io_uring_setup_buf_ring(ring, NREQS, 1, 0, &ret);
71 if (!br) {
72 if (ret == -EINVAL) {
73 no_buf_ring = 1;
74 return T_EXIT_SKIP;
75 }
76 fprintf(stderr, "buf ring reg %d\n", ret);
77 return T_EXIT_FAIL;
78 }
79
80 ptr = buf;
81 for (i = 0; i < NREQS; i++) {
82 io_uring_buf_ring_add(br, ptr, BUF_SIZE, i + 1,
83 io_uring_buf_ring_mask(NREQS), i);
84 ptr += BUF_SIZE;
85 }
86 io_uring_buf_ring_advance(br, NREQS);
87
88 pthread_create(&t, NULL, thread, d);
89
90 for (i = 0; i < NREQS; i++) {
91 sqe = io_uring_get_sqe(ring);
92 io_uring_prep_recv(sqe, fd[0], NULL, 0, 0);
93 sqe->flags |= IOSQE_BUFFER_SELECT;
94 sqe->buf_group = 1;
95 }
96
97 pthread_barrier_wait(&d->barrier);
98
99 ret = io_uring_submit(ring);
100 if (ret != NREQS) {
101 fprintf(stderr, "submit %d\n", ret);
102 return T_EXIT_FAIL;
103 }
104
105 i = 0;
106 do {
107 ret = io_uring_wait_cqe(ring, &cqe);
108 if (ret) {
109 fprintf(stderr, "cqe wait %d\n", ret);
110 return T_EXIT_FAIL;
111 }
112 i++;
113 if (cqe->res != BUF_SIZE) {
114 fprintf(stderr, "Bad cqe res %d\n", cqe->res);
115 break;
116 }
117 if (cqe->flags & IORING_CQE_F_BUFFER) {
118 int bid = cqe->flags >> 16;
119
120 if (bid > NREQS) {
121 fprintf(stderr, "Bad BID %d\n", bid);
122 return T_EXIT_FAIL;
123 }
124 } else {
125 fprintf(stderr, "No BID set!\n");
126 printf("ret=%d\n", cqe->res);
127 return T_EXIT_FAIL;
128 }
129 io_uring_cqe_seen(ring, cqe);
130 if (i > NREQS) {
131 fprintf(stderr, "Got too many requests?\n");
132 return T_EXIT_FAIL;
133 }
134 } while (i < NREQS);
135
136 pthread_join(t, &ret2);
137 free(buf);
138 io_uring_free_buf_ring(ring, br, NREQS, 1);
139 close(fd[0]);
140 close(fd[1]);
141 return T_EXIT_PASS;
142 }
143
test_mshot(struct io_uring * ring,struct data * d)144 static int test_mshot(struct io_uring *ring, struct data *d)
145 {
146 struct io_uring_buf_ring *br;
147 struct io_uring_sqe *sqe;
148 struct io_uring_cqe *cqe;
149 int fd[2], ret, i;
150 pthread_t t;
151 void *buf, *ptr;
152 void *ret2;
153
154 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) < 0) {
155 perror("socketpair");
156 return T_EXIT_FAIL;
157 }
158
159 d->fd = fd[1];
160
161 if (posix_memalign((void *) &buf, 16384, BUF_SIZE * NREQS))
162 return T_EXIT_FAIL;
163
164 br = io_uring_setup_buf_ring(ring, NREQS, 1, 0, &ret);
165 if (!br) {
166 fprintf(stderr, "buf ring reg %d\n", ret);
167 return T_EXIT_FAIL;
168 }
169
170 ptr = buf;
171 for (i = 0; i < NREQS; i++) {
172 io_uring_buf_ring_add(br, ptr, BUF_SIZE, i + 1,
173 io_uring_buf_ring_mask(NREQS), i);
174 ptr += BUF_SIZE;
175 }
176 io_uring_buf_ring_advance(br, NREQS);
177
178 pthread_create(&t, NULL, thread, d);
179
180 sqe = io_uring_get_sqe(ring);
181 io_uring_prep_recv_multishot(sqe, fd[0], NULL, 0, 0);
182 sqe->flags |= IOSQE_BUFFER_SELECT;
183 sqe->buf_group = 1;
184
185 pthread_barrier_wait(&d->barrier);
186
187 ret = io_uring_submit(ring);
188 if (ret != 1) {
189 fprintf(stderr, "submit %d\n", ret);
190 return T_EXIT_FAIL;
191 }
192
193 i = 0;
194 do {
195 ret = io_uring_wait_cqe(ring, &cqe);
196 if (ret) {
197 fprintf(stderr, "cqe wait %d\n", ret);
198 return T_EXIT_FAIL;
199 }
200 i++;
201 if (!(cqe->flags & IORING_CQE_F_MORE))
202 break;
203 if (cqe->res != BUF_SIZE) {
204 fprintf(stderr, "Bad cqe res %d\n", cqe->res);
205 break;
206 }
207 if (cqe->flags & IORING_CQE_F_BUFFER) {
208 int bid = cqe->flags >> 16;
209
210 if (bid > NREQS) {
211 fprintf(stderr, "Bad BID %d\n", bid);
212 return T_EXIT_FAIL;
213 }
214 } else {
215 fprintf(stderr, "No BID set!\n");
216 printf("ret=%d\n", cqe->res);
217 return T_EXIT_FAIL;
218 }
219 io_uring_cqe_seen(ring, cqe);
220 if (i > NREQS) {
221 fprintf(stderr, "Got too many requests?\n");
222 return T_EXIT_FAIL;
223 }
224 } while (1);
225
226 if (i != NREQS + 1) {
227 fprintf(stderr, "Only got %d requests\n", i);
228 return T_EXIT_FAIL;
229 }
230
231 pthread_join(t, &ret2);
232 io_uring_free_buf_ring(ring, br, NREQS, 1);
233 free(buf);
234 close(fd[0]);
235 close(fd[1]);
236 return T_EXIT_PASS;
237 }
238
main(int argc,char * argv[])239 int main(int argc, char *argv[])
240 {
241 struct io_uring ring;
242 struct data d;
243 int i, ret;
244
245 if (argc > 1)
246 return T_EXIT_SKIP;
247
248 pthread_barrier_init(&d.barrier, NULL, 2);
249
250 for (i = 0; i < 1000; i++) {
251 io_uring_queue_init(NREQS, &ring, 0);
252 ret = test(&ring, &d);
253 if (ret != T_EXIT_PASS) {
254 if (no_buf_ring)
255 break;
256 fprintf(stderr, "Test failed loop %d\n", i);
257 return T_EXIT_FAIL;
258 }
259 io_uring_queue_exit(&ring);
260 }
261
262 if (no_buf_ring)
263 return T_EXIT_SKIP;
264
265 for (i = 0; i < 1000; i++) {
266 io_uring_queue_init(NREQS, &ring, 0);
267 ret = test_mshot(&ring, &d);
268 if (ret != T_EXIT_PASS) {
269 fprintf(stderr, "Test mshot failed loop %d\n", i);
270 return T_EXIT_FAIL;
271 }
272 io_uring_queue_exit(&ring);
273 }
274
275 return T_EXIT_PASS;
276 }
277