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_int [--server] [--log] {input_file}\n");
65 printf("A program to run and control Wayland and channel inputs for a waypipe main loop\n");
66 return EXIT_FAILURE;
67 }
68 bool display_side = true;
69 if (argc > 1 && !strcmp(argv[1], "--server")) {
70 display_side = false;
71 argc--;
72 argv++;
73 }
74 if (argc > 1 && !strcmp(argv[1], "--log")) {
75 log_funcs[0] = test_atomic_log_handler;
76 log_funcs[1] = test_atomic_log_handler;
77 argc--;
78 argv++;
79 }
80 setup_video_logging();
81
82 size_t len;
83 char *buf = read_file_into_mem(argv[1], &len);
84 if (!buf) {
85 return EXIT_FAILURE;
86 }
87 printf("Loaded %zu bytes\n", len);
88
89 int way_fds[2], conn_fds[2];
90 if (socketpair(AF_UNIX, SOCK_STREAM, 0, way_fds) == -1 ||
91 socketpair(AF_UNIX, SOCK_STREAM, 0, conn_fds) == -1) {
92 printf("Socketpair failed\n");
93 return EXIT_FAILURE;
94 }
95
96 struct main_config config = {
97 .drm_node = NULL,
98 .n_worker_threads = 1,
99 .compression = COMP_NONE,
100 .compression_level = 0,
101 .no_gpu = true, /* until we can construct dmabufs here
102 */
103 .only_linear_dmabuf = false,
104 .video_if_possible = true,
105 .prefer_hwvideo = false,
106 };
107
108 pthread_t thread;
109 struct copy_setup conf = {.conn = conn_fds[1],
110 .wayl = way_fds[1],
111 .is_display_side = display_side,
112 .mc = &config};
113 if (pthread_create(&thread, NULL, start_looper, &conf) == -1) {
114 printf("Thread failed\n");
115 return EXIT_FAILURE;
116 }
117
118 char *ignore_buf = malloc(65536);
119
120 /* Main loop: RW from socketpairs with sendmsg, with short wait */
121 int64_t file_nwords = (int64_t)len / 4;
122 int64_t cursor = 0;
123 uint32_t *data = (uint32_t *)buf;
124 while (cursor < file_nwords) {
125 uint32_t header = data[cursor++];
126 bool wayland_side = header & 0x1;
127 bool add_file = header & 0x2;
128 int new_fileno = -1;
129
130 if (add_file && wayland_side && cursor < file_nwords) {
131 uint32_t fsize = data[cursor++];
132 if (fsize == 0) {
133 /* 'copy' sink */
134 new_fileno = open("/dev/null", O_WRONLY);
135 if (new_fileno == -1) {
136 wp_error("Failed to open /dev/null");
137 }
138 } else {
139 /* avoid buffer overflow */
140 fsize = fsize > 1000000 ? 1000000 : fsize;
141 new_fileno = create_anon_file();
142 if (ftruncate(new_fileno, (off_t)fsize) == -1) {
143 wp_error("Failed to resize tempfile");
144 checked_close(new_fileno);
145 break;
146 }
147 }
148 }
149
150 uint32_t packet_size = header >> 2;
151 int64_t words_left = file_nwords - cursor;
152 if (packet_size > 2048) {
153 packet_size = 2048;
154 }
155 if (packet_size > (uint32_t)words_left) {
156 packet_size = (uint32_t)words_left;
157 }
158 /* 2 msec max delay for 8KB of data, assuming no system
159 * interference, should be easily attainable */
160 int max_write_delay_ms = 1;
161 int max_read_delay_ms = 2;
162
163 int send_fd = wayland_side ? way_fds[0] : conn_fds[0];
164 /* Write packet to stream */
165 struct pollfd write_pfd;
166 write_pfd.fd = send_fd;
167 write_pfd.events = POLLOUT;
168 int nw;
169 retry_poll:
170 nw = poll(&write_pfd, 1, max_write_delay_ms);
171 if (nw == -1) {
172 if (new_fileno != -1) {
173 checked_close(new_fileno);
174 }
175
176 if (errno == EINTR) {
177 goto retry_poll;
178 }
179 printf("Poll error\n");
180 break;
181 } else if (nw == 1 && wayland_side) {
182 /* Send message */
183 struct iovec the_iovec;
184 the_iovec.iov_len = packet_size * 4;
185 the_iovec.iov_base = (char *)&data[cursor];
186 struct msghdr msg;
187 msg.msg_name = NULL;
188 msg.msg_namelen = 0;
189 msg.msg_iov = &the_iovec;
190 msg.msg_iovlen = 1;
191 msg.msg_control = NULL;
192 msg.msg_controllen = 0;
193 msg.msg_flags = 0;
194
195 union {
196 char buf[CMSG_SPACE(sizeof(int))];
197 struct cmsghdr align;
198 } uc;
199 memset(uc.buf, 0, sizeof(uc.buf));
200
201 if (new_fileno != -1) {
202 msg.msg_control = uc.buf;
203 msg.msg_controllen = sizeof(uc.buf);
204 struct cmsghdr *frst = CMSG_FIRSTHDR(&msg);
205 frst->cmsg_level = SOL_SOCKET;
206 frst->cmsg_type = SCM_RIGHTS;
207 memcpy(CMSG_DATA(frst), &new_fileno,
208 sizeof(int));
209 frst->cmsg_len = CMSG_LEN(sizeof(int));
210 msg.msg_controllen = CMSG_SPACE(sizeof(int));
211 }
212
213 ssize_t ret = sendmsg(way_fds[0], &msg, 0);
214 if (ret == -1) {
215 wp_error("Error in sendmsg");
216 break;
217 }
218 } else if (nw == 1 && !wayland_side) {
219 ssize_t ret = write(conn_fds[0], (char *)&data[cursor],
220 packet_size * 4);
221 if (ret == -1) {
222 wp_error("Error in write");
223 break;
224 }
225 } else {
226 wp_error("Failed to send message before timeout");
227 }
228 if (new_fileno != -1) {
229 checked_close(new_fileno);
230 }
231
232 /* Wait up to max_delay for a response. Almost all packets
233 * should be passed on unmodified; a very small fraction
234 * are dropped */
235 struct pollfd read_pfds[2];
236 read_pfds[0].fd = way_fds[0];
237 read_pfds[1].fd = conn_fds[0];
238 read_pfds[0].events = POLLIN;
239 read_pfds[1].events = POLLIN;
240 int nr = poll(read_pfds, 2,
241 packet_size > 0 ? max_read_delay_ms : 0);
242 if (nr == -1) {
243 if (errno == EINTR) {
244 continue;
245 }
246 printf("Poll error\n");
247 break;
248 } else if (nr == 0) {
249 wp_debug("No reply to sent packet %d", packet_size);
250 }
251 for (int i = 0; i < 2; i++) {
252 if (read_pfds[i].revents & POLLIN) {
253 char cmsgdata[(CMSG_LEN(28 * sizeof(int32_t)))];
254 struct iovec the_iovec;
255 the_iovec.iov_len = 65536;
256 the_iovec.iov_base = ignore_buf;
257 struct msghdr msg;
258 msg.msg_name = NULL;
259 msg.msg_namelen = 0;
260 msg.msg_iov = &the_iovec;
261 msg.msg_iovlen = 1;
262 msg.msg_control = &cmsgdata;
263 msg.msg_controllen = sizeof(cmsgdata);
264 msg.msg_flags = 0;
265 ssize_t ret = recvmsg(read_pfds[i].fd, &msg, 0);
266 if (ret == -1) {
267 wp_error("Error in recvmsg");
268 }
269 }
270 }
271
272 cursor += packet_size;
273 }
274 checked_close(conn_fds[0]);
275 checked_close(way_fds[0]);
276
277 pthread_join(thread, NULL);
278
279 free(buf);
280 free(ignore_buf);
281 return EXIT_SUCCESS;
282 }
283