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