And adjust it so it can be used to fuzz passt. Follow the instructions in README.fuzzing.md Signed-off-by: Richard W.M. Jones <rjones(a)redhat.com> --- .gitignore | 2 + fuzzing/Makefile | 15 +++ fuzzing/README.fuzzing.md | 36 +++++++ fuzzing/fuzz-wrapper.c | 173 +++++++++++++++++++++++++++++++++ fuzzing/testcase_dir/empty_tap | Bin 0 -> 4 bytes 5 files changed, 226 insertions(+) diff --git a/.gitignore b/.gitignore index d3d0e2c..2d001da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *~ +/fuzzing/fuzz-wrapper +/fuzzing/sync_dir /passt /passt.avx2 /pasta diff --git a/fuzzing/Makefile b/fuzzing/Makefile new file mode 100644 index 0000000..ae5ecd8 --- /dev/null +++ b/fuzzing/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio <sbrivio(a)redhat.com> +# Author: Richard W.M. Jones <rjones(a)redhat.com> + +all: fuzz-wrapper + +CFLAGS := -g -O2 + +fuzz-wrapper: fuzz-wrapper.c + $(CC) $(FLAGS) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +.PHONY: clean +clean: + rm -f fuzz-wrapper *~ *.o diff --git a/fuzzing/README.fuzzing.md b/fuzzing/README.fuzzing.md new file mode 100644 index 0000000..4d86b2f --- /dev/null +++ b/fuzzing/README.fuzzing.md @@ -0,0 +1,36 @@ +## Fuzzing with AFL++ (https://aflplus.plus/) + +1. In the top directory rebuild passt with AFL instrumentation, Clang + and ASAN: + +``` +make clean +AFL_USE_ASAN=1 make CC=/usr/bin/afl-clang-lto passt +``` + +2. In the fuzzing/ subdirectory, build the fuzzing wrapper *without* + instrumentation: + +``` +make fuzz-wrapper +``` + +3. Run AFL++ + +Create `fuzzing/sync_dir` and run multiple copies of afl-fuzz. +Usually you should run 1 master (-M) and as many slaves (-S) as you +can. + +Master: + +``` +mkdir -p sync_dir +afl-fuzz -i testcase_dir -o sync_dir -M fuzz01 ./fuzz-wrapper @@ +``` + +Slaves: + +``` +# replace fuzzNN with fuzz02, fuzz03, etc. +afl-fuzz -i testcase_dir -o sync_dir -S fuzzNN ./fuzz-wrapper @@ +``` diff --git a/fuzzing/fuzz-wrapper.c b/fuzzing/fuzz-wrapper.c new file mode 100644 index 0000000..9b5f0d8 --- /dev/null +++ b/fuzzing/fuzz-wrapper.c @@ -0,0 +1,173 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <poll.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <libnbd.h> + +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 (;;) */ +} diff --git a/fuzzing/testcase_dir/empty_tap b/fuzzing/testcase_dir/empty_tap new file mode 100644 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 -- 2.37.0.rc2