/* Fuzzing wrapper * Derived from libnbd fuzzing wrapper * Copyright (C) 2013-2022 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void passt (int s); static void qemu (int fd, int s); int main (int argc, char *argv[]) { int fd; pid_t pid; int sv[2]; if (argc == 2) { /* Open the test case before we fork so we know the file exists. */ fd = open (argv[1], O_RDONLY); if (fd == -1) { fprintf (stderr, "fuzz-wrapper: "); perror (argv[1]); exit (EXIT_FAILURE); } } else { fprintf (stderr, "fuzz-wrapper testcase\n"); exit (EXIT_FAILURE); } /* Create a connected socket. */ if (socketpair (AF_UNIX, SOCK_STREAM, 0, sv) == -1) { perror ("fuzz-wrapper: socketpair"); exit (EXIT_FAILURE); } /* Fork: The parent will be the passt process. The child will be * the phony qemu. */ pid = fork (); if (pid == -1) { perror ("fuzz-wrapper: fork"); exit (EXIT_FAILURE); } if (pid > 0) { /* Parent: passt. */ close (sv[1]); close (fd); passt (sv[0]); } /* Child: qemu. */ close (sv[0]); qemu (fd, sv[1]); close (sv[1]); _exit (EXIT_SUCCESS); } /* This is the parent process running passt. */ static void passt (int sock) { char sock_str[32]; snprintf (sock_str, sizeof sock_str, "%d", sock); /* XXX Assumes passt is compiled in the top directory: */ execlp ("../passt", "passt", "-f", "-e", "-1", "--fd", sock_str, NULL); perror ("fuzz-wrapper: execlp"); _exit (EXIT_FAILURE); } /* This is the child process acting like qemu. */ static void qemu (int fd, int sock) { struct pollfd pfds[1]; char rbuf[512], wbuf[512]; size_t wsize = 0; ssize_t r; for (;;) { pfds[0].fd = sock; pfds[0].events = POLLIN; if (wsize > 0 || fd >= 0) pfds[0].events |= POLLOUT; pfds[0].revents = 0; if (poll (pfds, 1, -1) == -1) { if (errno == EINTR) continue; perror ("fuzz-wrapper: poll"); /* This is not an error. */ return; } /* We can read from the passt socket. Just throw away anything sent. */ if ((pfds[0].revents & POLLIN) != 0) { r = read (sock, rbuf, sizeof rbuf); if (r == -1 && errno != EINTR) { perror ("fuzz-wrapper: read"); return; } else if (r == 0) /* end of input from the server */ return; } /* We can write to the passt socket. */ if ((pfds[0].revents & POLLOUT) != 0) { /* Write more data from the wbuf. */ if (wsize > 0) { morewrite: r = write (sock, wbuf, wsize); if (r == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { perror ("fuzz-wrapper: write"); return; } else if (r > 0) { memmove (wbuf, &wbuf[r], wsize-r); wsize -= r; } } /* Write more data from the file. */ else if (fd >= 0) { r = read (fd, wbuf, sizeof wbuf); if (r == -1) { perror ("fuzz-wrapper: read"); _exit (EXIT_FAILURE); } else if (r == 0) { fd = -1; /* ignore the file from now on */ shutdown (sock, SHUT_WR); } else { wsize = r; goto morewrite; } } } } /* for (;;) */ }