1 /**************************************************************************
2 *
3 * Copyright (C) 2015 Red Hat Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 **************************************************************************/
24 #include <stdio.h>
25 #include <signal.h>
26 #include <stdbool.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <sys/un.h>
33 #include <fcntl.h>
34 #include <getopt.h>
35 #include <string.h>
36
37 #include "util.h"
38 #include "util/u_memory.h"
39 #include "vtest.h"
40 #include "vtest_protocol.h"
41 #include "virglrenderer.h"
42 #ifdef HAVE_SYS_SELECT_H
43 #include <sys/select.h>
44 #endif
45
46
47 struct vtest_program
48 {
49 const char *socket_name;
50 int socket;
51 const char *read_file;
52 int in_fd;
53 int out_fd;
54 struct vtest_input input;
55
56 const char *render_device;
57
58 bool do_fork;
59 bool loop;
60
61 bool use_glx;
62 bool use_egl_surfaceless;
63 bool use_gles;
64 };
65
66 struct vtest_program prog = {
67 .socket_name = VTEST_DEFAULT_SOCKET_NAME,
68 .socket = -1,
69
70 .read_file = NULL,
71
72 .in_fd = -1,
73 .out_fd = -1,
74 .input = { { -1 }, NULL },
75 .render_device = 0,
76 .do_fork = true,
77 .loop = true,
78 };
79
80 static void vtest_main_getenv(void);
81 static void vtest_main_parse_args(int argc, char **argv);
82 static void vtest_main_set_signal_child(void);
83 static void vtest_main_set_signal_segv(void);
84 static void vtest_main_open_read_file(void);
85 static void vtest_main_open_socket(void);
86 static void vtest_main_run_renderer(int in_fd, int out_fd, struct vtest_input *input,
87 int ctx_flags, const char *render_device);
88 static void vtest_main_wait_for_socket_accept(void);
89 static void vtest_main_tidy_fds(void);
90 static void vtest_main_close_socket(void);
91
92
main(int argc,char ** argv)93 int main(int argc, char **argv)
94 {
95 #ifdef __AFL_LOOP
96 while (__AFL_LOOP(1000)) {
97 #endif
98
99 vtest_main_getenv();
100 vtest_main_parse_args(argc, argv);
101
102 int ctx_flags = VIRGL_RENDERER_USE_EGL;
103 if (prog.use_glx) {
104 if (prog.use_egl_surfaceless || prog.use_gles) {
105 fprintf(stderr, "Cannot use surfaceless or GLES with GLX.\n");
106 exit(EXIT_FAILURE);
107 }
108 ctx_flags = VIRGL_RENDERER_USE_GLX;
109 } else {
110 if (prog.use_egl_surfaceless)
111 ctx_flags |= VIRGL_RENDERER_USE_SURFACELESS;
112 if (prog.use_gles)
113 ctx_flags |= VIRGL_RENDERER_USE_GLES;
114 }
115
116 if (prog.read_file != NULL) {
117 vtest_main_open_read_file();
118 goto start;
119 }
120
121 if (prog.do_fork) {
122 vtest_main_set_signal_child();
123 }
124
125 vtest_main_open_socket();
126 restart:
127 vtest_main_wait_for_socket_accept();
128
129 start:
130 if (prog.do_fork) {
131 /* fork a renderer process */
132 if (fork() == 0) {
133 vtest_main_set_signal_segv();
134 vtest_main_run_renderer(prog.in_fd, prog.out_fd, &prog.input,
135 ctx_flags, prog.render_device);
136 exit(0);
137 }
138 } else {
139 vtest_main_set_signal_segv();
140 vtest_main_run_renderer(prog.in_fd, prog.out_fd, &prog.input,
141 ctx_flags, prog.render_device);
142 }
143
144 vtest_main_tidy_fds();
145
146 if (prog.loop) {
147 goto restart;
148 }
149
150 vtest_main_close_socket();
151
152 #ifdef __AFL_LOOP
153 }
154 #endif
155 }
156
157 #define OPT_NO_FORK 'f'
158 #define OPT_NO_LOOP_OR_FORK 'l'
159 #define OPT_USE_GLX 'x'
160 #define OPT_USE_EGL_SURFACELESS 's'
161 #define OPT_USE_GLES 'e'
162 #define OPT_RENDERNODE 'r'
163
vtest_main_parse_args(int argc,char ** argv)164 static void vtest_main_parse_args(int argc, char **argv)
165 {
166 int ret;
167
168 static struct option long_options[] = {
169 {"no-fork", no_argument, NULL, OPT_NO_FORK},
170 {"no-loop-or-fork", no_argument, NULL, OPT_NO_LOOP_OR_FORK},
171 {"use-glx", no_argument, NULL, OPT_USE_GLX},
172 {"use-egl-surfaceless", no_argument, NULL, OPT_USE_EGL_SURFACELESS},
173 {"use-gles", no_argument, NULL, OPT_USE_GLES},
174 {"rendernode", required_argument, NULL, OPT_RENDERNODE},
175 {0, 0, 0, 0}
176 };
177
178 /* getopt_long stores the option index here. */
179 int option_index = 0;
180
181 do {
182 ret = getopt_long(argc, argv, "", long_options, &option_index);
183
184 switch (ret) {
185 case -1:
186 break;
187 case OPT_NO_FORK:
188 prog.do_fork = false;
189 break;
190 case OPT_NO_LOOP_OR_FORK:
191 prog.do_fork = false;
192 prog.loop = false;
193 break;
194 case OPT_USE_GLX:
195 prog.use_glx = true;
196 break;
197 case OPT_USE_EGL_SURFACELESS:
198 prog.use_egl_surfaceless = true;
199 break;
200 case OPT_USE_GLES:
201 prog.use_gles = true;
202 break;
203 case OPT_RENDERNODE:
204 prog.render_device = optarg;
205 break;
206 default:
207 printf("Usage: %s [--no-fork] [--no-loop-or-fork] [--use-glx] "
208 "[--use-egl-surfaceless] [--use-gles] [--rendernode <dev>]"
209 " [file]\n", argv[0]);
210 exit(EXIT_FAILURE);
211 break;
212 }
213
214 } while (ret >= 0);
215
216 if (optind < argc) {
217 prog.read_file = argv[optind];
218 prog.loop = false;
219 prog.do_fork = false;
220 }
221 }
222
vtest_main_getenv(void)223 static void vtest_main_getenv(void)
224 {
225 prog.use_glx = getenv("VTEST_USE_GLX") != NULL;
226 prog.use_egl_surfaceless = getenv("VTEST_USE_EGL_SURFACELESS") != NULL;
227 prog.use_gles = getenv("VTEST_USE_GLES") != NULL;
228 prog.render_device = getenv("VTEST_RENDERNODE");
229 }
230
handler(int sig,siginfo_t * si,void * unused)231 static void handler(int sig, siginfo_t *si, void *unused)
232 {
233 (void)sig; (void)si, (void)unused;
234
235 printf("SIGSEGV!\n");
236 exit(EXIT_FAILURE);
237 }
238
vtest_main_set_signal_child(void)239 static void vtest_main_set_signal_child(void)
240 {
241 struct sigaction sa;
242 int ret;
243
244 memset(&sa, 0, sizeof(sa));
245 sigemptyset(&sa.sa_mask);
246 sa.sa_handler = SIG_IGN;
247 sa.sa_flags = 0;
248
249 ret = sigaction(SIGCHLD, &sa, NULL);
250 if (ret == -1) {
251 perror("Failed to set SIGCHLD");
252 exit(1);
253 }
254 }
255
vtest_main_set_signal_segv(void)256 static void vtest_main_set_signal_segv(void)
257 {
258 struct sigaction sa;
259 int ret;
260
261 memset(&sa, 0, sizeof(sa));
262 sigemptyset(&sa.sa_mask);
263 sa.sa_flags = SA_SIGINFO;
264 sa.sa_sigaction = handler;
265
266 ret = sigaction(SIGSEGV, &sa, NULL);
267 if (ret == -1) {
268 perror("Failed to set SIGSEGV");
269 exit(1);
270 }
271 }
272
vtest_main_open_read_file(void)273 static void vtest_main_open_read_file(void)
274 {
275 int ret;
276
277 ret = open(prog.read_file, O_RDONLY);
278 if (ret == -1) {
279 perror(NULL);
280 exit(1);
281 }
282 prog.in_fd = ret;
283 prog.input.data.fd = prog.in_fd;
284 prog.input.read = vtest_block_read;
285
286 ret = open("/dev/null", O_WRONLY);
287 if (ret == -1) {
288 perror(NULL);
289 exit(1);
290 }
291 prog.out_fd = ret;
292 }
293
vtest_main_open_socket(void)294 static void vtest_main_open_socket(void)
295 {
296 struct sockaddr_un un;
297
298 prog.socket = socket(PF_UNIX, SOCK_STREAM, 0);
299 if (prog.socket < 0) {
300 goto err;
301 }
302
303 memset(&un, 0, sizeof(un));
304 un.sun_family = AF_UNIX;
305
306 snprintf(un.sun_path, sizeof(un.sun_path), "%s", prog.socket_name);
307
308 unlink(un.sun_path);
309
310 if (bind(prog.socket, (struct sockaddr *)&un, sizeof(un)) < 0) {
311 goto err;
312 }
313
314 if (listen(prog.socket, 1) < 0){
315 goto err;
316 }
317
318 return;
319
320 err:
321 perror("Failed to setup socket.");
322 exit(1);
323 }
324
vtest_main_wait_for_socket_accept(void)325 static void vtest_main_wait_for_socket_accept(void)
326 {
327 fd_set read_fds;
328 int new_fd;
329 int ret;
330 FD_ZERO(&read_fds);
331 FD_SET(prog.socket, &read_fds);
332
333 ret = select(prog.socket + 1, &read_fds, NULL, NULL, NULL);
334 if (ret < 0) {
335 perror("Failed to select on socket!");
336 exit(1);
337 }
338
339 if (!FD_ISSET(prog.socket, &read_fds)) {
340 perror("Odd state in fd_set.");
341 exit(1);
342 }
343
344 new_fd = accept(prog.socket, NULL, NULL);
345 if (new_fd < 0) {
346 perror("Failed to accept socket.");
347 exit(1);
348 }
349
350 prog.in_fd = new_fd;
351 prog.out_fd = new_fd;
352 prog.input.data.fd = prog.in_fd;
353 prog.input.read = vtest_block_read;
354 }
355
356 typedef int (*vtest_cmd_fptr_t)(uint32_t);
357
358 static const vtest_cmd_fptr_t vtest_commands[] = {
359 NULL /* CMD ids starts at 1 */,
360 vtest_send_caps,
361 vtest_create_resource,
362 vtest_resource_unref,
363 vtest_transfer_get,
364 vtest_transfer_put,
365 vtest_submit_cmd,
366 vtest_resource_busy_wait,
367 NULL, /* vtest_create_renderer is a specific case */
368 vtest_send_caps2,
369 vtest_ping_protocol_version,
370 vtest_protocol_version,
371 vtest_create_resource2,
372 vtest_transfer_get2,
373 vtest_transfer_put2,
374 };
375
vtest_main_run_renderer(int in_fd,int out_fd,struct vtest_input * input,int ctx_flags,const char * render_device)376 static void vtest_main_run_renderer(int in_fd, int out_fd,
377 struct vtest_input *input, int ctx_flags,
378 const char *render_device)
379 {
380 int err, ret;
381 uint32_t header[VTEST_HDR_SIZE];
382 int initialized = 0;
383
384 do {
385 ret = vtest_wait_for_fd_read(in_fd);
386 if (ret < 0) {
387 err = 1;
388 break;
389 }
390
391 ret = input->read(input, &header, sizeof(header));
392 if (ret < 0 || (size_t)ret < sizeof(header)) {
393 err = 2;
394 break;
395 }
396
397 if (!initialized) {
398 /* The first command MUST be VCMD_CREATE_RENDERER */
399 if (header[1] != VCMD_CREATE_RENDERER) {
400 err = 3;
401 break;
402 }
403
404 ret = vtest_create_renderer(input, out_fd, header[0], ctx_flags, render_device);
405 if (ret < 0) {
406 err = 4;
407 break;
408 }
409 initialized = 1;
410 printf("%s: vtest initialized.\n", __func__);
411 vtest_poll();
412 continue;
413 }
414
415 vtest_poll();
416 if (header[1] <= 0 || header[1] >= ARRAY_SIZE(vtest_commands)) {
417 err = 5;
418 break;
419 }
420
421 if (vtest_commands[header[1]] == NULL) {
422 err = 6;
423 break;
424 }
425
426 ret = vtest_commands[header[1]](header[0]);
427 if (ret < 0) {
428 err = 7;
429 break;
430 }
431
432 /* GL draws are fenced, while possible fence creations are too */
433 if (header[1] == VCMD_SUBMIT_CMD || header[1] == VCMD_RESOURCE_CREATE ||
434 header[1] == VCMD_RESOURCE_CREATE2)
435 vtest_renderer_create_fence();
436
437 } while (1);
438
439 fprintf(stderr, "socket failed (%d) - closing renderer\n", err);
440
441 vtest_destroy_renderer();
442 }
443
vtest_main_tidy_fds(void)444 static void vtest_main_tidy_fds(void)
445 {
446 // out_fd will be closed by the in_fd clause if they are the same.
447 if (prog.out_fd == prog.in_fd) {
448 prog.out_fd = -1;
449 }
450
451 if (prog.in_fd != -1) {
452 close(prog.in_fd);
453 prog.in_fd = -1;
454 prog.input.read = NULL;
455 }
456
457 if (prog.out_fd != -1) {
458 close(prog.out_fd);
459 prog.out_fd = -1;
460 }
461 }
462
vtest_main_close_socket(void)463 static void vtest_main_close_socket(void)
464 {
465 if (prog.socket != -1) {
466 close(prog.socket);
467 prog.socket = -1;
468 }
469 }
470