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