1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Test reads that will punt to blocking context, with immediate overwrite
4 * of iovec->iov_base to NULL. If the kernel doesn't properly handle
5 * reuse of the iovec, we should get -EFAULT.
6 */
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <pthread.h>
13 #include <sys/time.h>
14
15 #include "helpers.h"
16 #include "liburing.h"
17
18 #define STR_SIZE 32768
19 #define FILE_SIZE 65536
20
21 struct thread_data {
22 int fd1, fd2;
23 volatile int do_exit;
24 };
25
flusher(void * __data)26 static void *flusher(void *__data)
27 {
28 struct thread_data *data = __data;
29
30 while (!data->do_exit) {
31 posix_fadvise(data->fd1, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
32 posix_fadvise(data->fd2, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
33 usleep(10);
34 }
35
36 return NULL;
37 }
38
39 static char str1[STR_SIZE];
40 static char str2[STR_SIZE];
41
42 static struct io_uring ring;
43
44 static int no_stable;
45
prep(int fd,char * str,int split,int async)46 static int prep(int fd, char *str, int split, int async)
47 {
48 struct io_uring_sqe *sqe;
49 struct iovec iovs[16];
50 int ret, i;
51
52 if (split) {
53 int vsize = STR_SIZE / 16;
54 void *ptr = str;
55
56 for (i = 0; i < 16; i++) {
57 iovs[i].iov_base = ptr;
58 iovs[i].iov_len = vsize;
59 ptr += vsize;
60 }
61 } else {
62 iovs[0].iov_base = str;
63 iovs[0].iov_len = STR_SIZE;
64 }
65
66 sqe = io_uring_get_sqe(&ring);
67 io_uring_prep_readv(sqe, fd, iovs, split ? 16 : 1, 0);
68 sqe->user_data = fd;
69 if (async)
70 sqe->flags = IOSQE_ASYNC;
71 ret = io_uring_submit(&ring);
72 if (ret != 1) {
73 fprintf(stderr, "submit got %d\n", ret);
74 return 1;
75 }
76 if (split) {
77 for (i = 0; i < 16; i++)
78 iovs[i].iov_base = NULL;
79 } else {
80 iovs[0].iov_base = NULL;
81 }
82 return 0;
83 }
84
wait_nr(int nr)85 static int wait_nr(int nr)
86 {
87 int i, ret;
88
89 for (i = 0; i < nr; i++) {
90 struct io_uring_cqe *cqe;
91
92 ret = io_uring_wait_cqe(&ring, &cqe);
93 if (ret)
94 return ret;
95 if (cqe->res < 0) {
96 fprintf(stderr, "cqe->res=%d\n", cqe->res);
97 return 1;
98 }
99 io_uring_cqe_seen(&ring, cqe);
100 }
101
102 return 0;
103 }
104
test_reuse(int argc,char * argv[],int split,int async)105 static int test_reuse(int argc, char *argv[], int split, int async)
106 {
107 struct thread_data data;
108 struct io_uring_params p = { };
109 int fd1, fd2, ret, i;
110 struct timeval tv;
111 pthread_t thread;
112 char *fname1 = ".reuse.1";
113 int do_unlink = 1;
114 void *tret;
115
116 ret = io_uring_queue_init_params(32, &ring, &p);
117 if (ret) {
118 fprintf(stderr, "io_uring_queue_init: %d\n", ret);
119 return 1;
120 }
121
122 if (!(p.features & IORING_FEAT_SUBMIT_STABLE)) {
123 fprintf(stdout, "FEAT_SUBMIT_STABLE not there, skipping\n");
124 io_uring_queue_exit(&ring);
125 no_stable = 1;
126 return 0;
127 }
128
129 if (argc > 1) {
130 fname1 = argv[1];
131 do_unlink = 0;
132 } else {
133 t_create_file(fname1, FILE_SIZE);
134 }
135
136 fd1 = open(fname1, O_RDONLY);
137 if (do_unlink)
138 unlink(fname1);
139 if (fd1 < 0) {
140 if (errno == EPERM || errno == EACCES)
141 return T_EXIT_SKIP;
142 perror("open fname1");
143 goto err;
144 }
145
146 t_create_file(".reuse.2", FILE_SIZE);
147 fd2 = open(".reuse.2", O_RDONLY);
148 unlink(".reuse.2");
149 if (fd2 < 0) {
150 perror("open .reuse.2");
151 goto err;
152 }
153
154 data.fd1 = fd1;
155 data.fd2 = fd2;
156 data.do_exit = 0;
157 pthread_create(&thread, NULL, flusher, &data);
158 usleep(10000);
159
160 gettimeofday(&tv, NULL);
161 for (i = 0; i < 1000; i++) {
162 ret = prep(fd1, str1, split, async);
163 if (ret) {
164 fprintf(stderr, "prep1 failed: %d\n", ret);
165 goto err;
166 }
167 ret = prep(fd2, str2, split, async);
168 if (ret) {
169 fprintf(stderr, "prep1 failed: %d\n", ret);
170 goto err;
171 }
172 ret = wait_nr(2);
173 if (ret) {
174 fprintf(stderr, "wait_nr: %d\n", ret);
175 goto err;
176 }
177 if (mtime_since_now(&tv) > 5000)
178 break;
179 }
180
181 data.do_exit = 1;
182 pthread_join(thread, &tret);
183
184 close(fd2);
185 close(fd1);
186 io_uring_queue_exit(&ring);
187 return 0;
188 err:
189 io_uring_queue_exit(&ring);
190 return 1;
191 }
192
main(int argc,char * argv[])193 int main(int argc, char *argv[])
194 {
195 int ret, i;
196
197 for (i = 0; i < 4; i++) {
198 int split, async;
199
200 split = (i & 1) != 0;
201 async = (i & 2) != 0;
202
203 ret = test_reuse(argc, argv, split, async);
204 if (ret == T_EXIT_SKIP)
205 continue;
206 if (ret) {
207 fprintf(stderr, "test_reuse %d %d failed\n", split, async);
208 return ret;
209 }
210 if (no_stable)
211 break;
212 }
213
214 return 0;
215 }
216