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