1 /*
2 * Copyright © 2019 Manuel Stoeckl
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #include "common.h"
27 #include "main.h"
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <inttypes.h>
32 #include <poll.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/mman.h>
38 #include <sys/socket.h>
39 #include <sys/wait.h>
40 #include <time.h>
41 #include <unistd.h>
42
43 #include <pthread.h>
44
45 struct copy_setup {
46 int conn;
47 int wayl;
48 bool is_display_side;
49 struct main_config *mc;
50 };
51
start_looper(void * data)52 static void *start_looper(void *data)
53 {
54 struct copy_setup *setup = (struct copy_setup *)data;
55 main_interface_loop(setup->conn, setup->wayl, -1, setup->mc,
56 setup->is_display_side);
57 return NULL;
58 }
59
60 log_handler_func_t log_funcs[2] = {NULL, NULL};
main(int argc,char ** argv)61 int main(int argc, char **argv)
62 {
63 if (argc == 1 || !strcmp(argv[1], "--help")) {
64 printf("Usage: ./fuzz_hook_ext [--log] {input_file}\n");
65 printf("A program to run and control Wayland inputs for a linked client/server pair, from a file.\n");
66 return EXIT_FAILURE;
67 }
68 if (argc > 1 && !strcmp(argv[1], "--log")) {
69 log_funcs[0] = test_atomic_log_handler;
70 log_funcs[1] = test_atomic_log_handler;
71 argc--;
72 argv++;
73 }
74 setup_video_logging();
75
76 size_t len;
77 char *buf = read_file_into_mem(argv[1], &len);
78 if (!buf) {
79 return EXIT_FAILURE;
80 }
81 printf("Loaded %zu bytes\n", len);
82
83 int srv_fds[2], cli_fds[2], conn_fds[2];
84 if (socketpair(AF_UNIX, SOCK_STREAM, 0, srv_fds) == -1 ||
85 socketpair(AF_UNIX, SOCK_STREAM, 0, cli_fds) == -1 ||
86 socketpair(AF_UNIX, SOCK_STREAM, 0, conn_fds) == -1) {
87 printf("Socketpair failed\n");
88 return EXIT_FAILURE;
89 }
90
91 struct main_config config = {
92 .drm_node = NULL,
93 .n_worker_threads = 1,
94 .compression = COMP_NONE,
95 .compression_level = 0,
96 .no_gpu = true, /* until we can construct dmabufs here
97 */
98 .only_linear_dmabuf = false,
99 .video_if_possible = true,
100 .prefer_hwvideo = false,
101 };
102
103 pthread_t thread_a, thread_b;
104 struct copy_setup server_conf = {.conn = conn_fds[0],
105 .wayl = srv_fds[1],
106 .is_display_side = true,
107 .mc = &config};
108 struct copy_setup client_conf = {.conn = conn_fds[1],
109 .wayl = cli_fds[1],
110 .is_display_side = false,
111 .mc = &config};
112 if (pthread_create(&thread_a, NULL, start_looper, &server_conf) == -1) {
113 printf("Thread failed\n");
114 }
115 if (pthread_create(&thread_b, NULL, start_looper, &client_conf) == -1) {
116 printf("Thread failed\n");
117 }
118
119 char *ignore_buf = malloc(65536);
120
121 /* Main loop: RW from socketpairs with sendmsg, with short wait */
122 int64_t file_nwords = (int64_t)len / 4;
123 int64_t cursor = 0;
124 uint32_t *data = (uint32_t *)buf;
125
126 while (cursor < file_nwords) {
127 uint32_t header = data[cursor++];
128 bool to_server = header & 0x1;
129 bool add_file = header & 0x2;
130 int new_fileno = -1;
131
132 if (add_file && cursor < file_nwords) {
133 uint32_t fsize = data[cursor++];
134 if (fsize == 0) {
135 /* 'copy' sink */
136 new_fileno = open("/dev/null", O_WRONLY);
137 if (new_fileno == -1) {
138 wp_error("Failed to open /dev/null");
139 }
140 } else {
141 /* avoid buffer overflow */
142 fsize = fsize > 1000000 ? 1000000 : fsize;
143 new_fileno = create_anon_file();
144 if (ftruncate(new_fileno, (off_t)fsize) == -1) {
145 wp_error("Failed to resize tempfile");
146 checked_close(new_fileno);
147 break;
148 }
149 }
150 }
151
152 uint32_t packet_size = header >> 2;
153 int64_t words_left = file_nwords - cursor;
154 if (packet_size > 2048) {
155 packet_size = 2048;
156 }
157 if (packet_size > (uint32_t)words_left) {
158 packet_size = (uint32_t)words_left;
159 }
160 /* 2 msec max delay for 8KB of data, assuming no system
161 * interference, should be easily attainable */
162 int max_write_delay_ms = 1;
163 int max_read_delay_ms = 2;
164
165 int send_fd = to_server ? srv_fds[0] : cli_fds[0];
166 /* Write packet to stream */
167 struct pollfd write_pfd;
168 write_pfd.fd = send_fd;
169 write_pfd.events = POLLOUT;
170 int nw;
171 retry_poll:
172 nw = poll(&write_pfd, 1, max_write_delay_ms);
173 if (nw == -1) {
174 if (new_fileno != -1) {
175 checked_close(new_fileno);
176 }
177
178 if (errno == EINTR) {
179 goto retry_poll;
180 }
181 printf("Poll error\n");
182 break;
183 } else if (nw == 1) {
184 /* Send message */
185 struct iovec the_iovec;
186 the_iovec.iov_len = packet_size * 4;
187 the_iovec.iov_base = (char *)&data[cursor];
188 struct msghdr msg;
189 msg.msg_name = NULL;
190 msg.msg_namelen = 0;
191 msg.msg_iov = &the_iovec;
192 msg.msg_iovlen = 1;
193 msg.msg_control = NULL;
194 msg.msg_controllen = 0;
195 msg.msg_flags = 0;
196
197 union {
198 char buf[CMSG_SPACE(sizeof(int))];
199 struct cmsghdr align;
200 } uc;
201 memset(uc.buf, 0, sizeof(uc.buf));
202
203 if (new_fileno != -1) {
204 msg.msg_control = uc.buf;
205 msg.msg_controllen = sizeof(uc.buf);
206 struct cmsghdr *frst = CMSG_FIRSTHDR(&msg);
207 frst->cmsg_level = SOL_SOCKET;
208 frst->cmsg_type = SCM_RIGHTS;
209 memcpy(CMSG_DATA(frst), &new_fileno,
210 sizeof(int));
211 frst->cmsg_len = CMSG_LEN(sizeof(int));
212 msg.msg_controllen = CMSG_SPACE(sizeof(int));
213 }
214
215 int target_fd = to_server ? srv_fds[0] : cli_fds[0];
216 ssize_t ret = sendmsg(target_fd, &msg, 0);
217 if (ret == -1) {
218 wp_error("Error in sendmsg");
219 break;
220 }
221 } else {
222 wp_error("Failed to send message before timeout");
223 }
224 if (new_fileno != -1) {
225 checked_close(new_fileno);
226 }
227
228 /* Wait up to max_delay for a response. Almost all packets
229 * should be passed on unmodified; a very small fraction
230 * are dropped */
231 struct pollfd read_pfds[2];
232 read_pfds[0].fd = srv_fds[0];
233 read_pfds[1].fd = cli_fds[0];
234 read_pfds[0].events = POLLIN;
235 read_pfds[1].events = POLLIN;
236 int nr = poll(read_pfds, 2,
237 packet_size > 0 ? max_read_delay_ms : 0);
238 if (nr == -1) {
239 if (errno == EINTR) {
240 continue;
241 }
242 printf("Poll error\n");
243 break;
244 } else if (nr == 0) {
245 wp_debug("No reply to sent packet %d", packet_size);
246 }
247 for (int i = 0; i < 2; i++) {
248 if (read_pfds[i].revents & POLLIN) {
249 char cmsgdata[(CMSG_LEN(28 * sizeof(int32_t)))];
250 struct iovec the_iovec;
251 the_iovec.iov_len = 65536;
252 the_iovec.iov_base = ignore_buf;
253 struct msghdr msg;
254 msg.msg_name = NULL;
255 msg.msg_namelen = 0;
256 msg.msg_iov = &the_iovec;
257 msg.msg_iovlen = 1;
258 msg.msg_control = &cmsgdata;
259 msg.msg_controllen = sizeof(cmsgdata);
260 msg.msg_flags = 0;
261 ssize_t ret = recvmsg(read_pfds[i].fd, &msg, 0);
262 if (ret == -1) {
263 wp_error("Error in recvmsg");
264 }
265 }
266 }
267
268 cursor += packet_size;
269 }
270 checked_close(srv_fds[0]);
271 checked_close(cli_fds[0]);
272
273 pthread_join(thread_a, NULL);
274 pthread_join(thread_b, NULL);
275
276 free(buf);
277 free(ignore_buf);
278 return EXIT_SUCCESS;
279 }
280