mirror of
https://gitlab.nic.cz/knot/knot-dns.git
synced 2026-06-08 16:12:37 -04:00
Add better support for LLVM libFuzzer packet parser tests
This commit is contained in:
parent
3193ab7c4e
commit
fe1454bb7d
12 changed files with 173 additions and 155 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -57,6 +57,7 @@
|
|||
/src/libknot.pc
|
||||
/src/libknot/version.h
|
||||
/src/knot/modules/static_modules.h
|
||||
/test-driver
|
||||
|
||||
# dnstap
|
||||
/src/contrib/dnstap/Makefile
|
||||
|
|
|
|||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "tests-fuzz/packet_libfuzzer.in"]
|
||||
path = tests-fuzz/packet_libfuzzer.in
|
||||
url = ../fuzzing/packet_libfuzzer.in.git
|
||||
|
|
@ -485,9 +485,9 @@ src/zscanner/tests/processing.h
|
|||
src/zscanner/tests/tests.c
|
||||
src/zscanner/tests/tests.h
|
||||
src/zscanner/tests/zscanner-tool.c
|
||||
tests-fuzz/afl-loop.h
|
||||
tests-fuzz/packet.c
|
||||
tests-fuzz/main.c
|
||||
tests-fuzz/packet_libfuzzer.c
|
||||
tests-fuzz/wrap/afl-loop.h
|
||||
tests-fuzz/wrap/server.c
|
||||
tests-fuzz/wrap/tcp-handler.c
|
||||
tests-fuzz/wrap/udp-handler.c
|
||||
|
|
|
|||
18
configure.ac
18
configure.ac
|
|
@ -523,17 +523,7 @@ AX_CODE_COVERAGE
|
|||
|
||||
AX_SANITIZER
|
||||
AS_IF([test -n "$sanitize_CFLAGS"], [CFLAGS="$CFLAGS $sanitize_CFLAGS"])
|
||||
|
||||
# LibFuzzer
|
||||
AC_ARG_WITH([libfuzzer],
|
||||
AC_HELP_STRING([--with-libfuzzer=path], [Path to LibFuzzer static library]),
|
||||
[libfuzzer_LIBS="$withval"], [libfuzzer_LIBS=no]
|
||||
)
|
||||
AS_IF([test "$libfuzzer_LIBS" != no -a "$sanitize_coverage_enabled" != yes], [
|
||||
AC_MSG_ERROR([Sanitizer coverage required for LibFuzzer.])
|
||||
])
|
||||
AM_CONDITIONAL([HAVE_LIBFUZZER], [test "$libfuzzer_LIBS" != "no"])
|
||||
AC_SUBST([libfuzzer_LIBS])
|
||||
AM_CONDITIONAL([SANITIZE_FUZZER], [test "$with_sanitize_fuzzer" != "no"])
|
||||
|
||||
AS_IF([test "$enable_documentation" = "yes"],[
|
||||
|
||||
|
|
@ -587,10 +577,12 @@ result_msg_base=" $PACKAGE $VERSION
|
|||
Utilities with Dnstap: ${opt_dnstap}
|
||||
Systemd integration: ${enable_systemd}
|
||||
PKCS #11 support: ${enable_pkcs11}
|
||||
ED25519 support: ${enable_ed25519}
|
||||
Ed25519 support: ${enable_ed25519}
|
||||
Code coverage: ${enable_code_coverage}
|
||||
Sanitizer: ${with_sanitize}
|
||||
LibFuzzer: ${libfuzzer_LIBS}"
|
||||
Sanitizer coverage: ${with_sanitize_coverage}
|
||||
LibFuzzer: ${with_sanitize_fuzzer}
|
||||
"
|
||||
|
||||
result_msg_esc=$(echo -n "$result_msg_base" | sed '$!s/$/\\n/' | tr -d '\n')
|
||||
result_msg_add="
|
||||
|
|
|
|||
6
tests-fuzz/.gitignore
vendored
6
tests-fuzz/.gitignore
vendored
|
|
@ -2,6 +2,8 @@
|
|||
/Makefile
|
||||
|
||||
/knotd_stdio
|
||||
/packet
|
||||
/packet_libfuzzer
|
||||
/wrap/main.c
|
||||
|
||||
/*.trs
|
||||
/*.log
|
||||
/packet_libfuzzer
|
||||
|
|
|
|||
|
|
@ -2,33 +2,23 @@ AM_CPPFLAGS = \
|
|||
-include $(top_builddir)/src/config.h \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/src/dnssec/lib \
|
||||
-DCONFIG_DIR='"${config_dir}"' \
|
||||
-DSTORAGE_DIR='"${storage_dir}"' \
|
||||
-DRUN_DIR='"${run_dir}"'
|
||||
-DCONFIG_DIR='"${config_dir}"' \
|
||||
-DSTORAGE_DIR='"${storage_dir}"' \
|
||||
-DRUN_DIR='"${run_dir}"' \
|
||||
-DSRCDIR=\"$(abs_srcdir)\"
|
||||
|
||||
LDADD = \
|
||||
$(top_builddir)/src/libknot.la
|
||||
FUZZERS = \
|
||||
packet_libfuzzer
|
||||
|
||||
check_PROGRAMS = \
|
||||
knotd_stdio \
|
||||
packet
|
||||
check_PROGRAMS = $(FUZZERS)
|
||||
|
||||
if HAVE_LIBFUZZER
|
||||
check_PROGRAMS += packet_libfuzzer
|
||||
packet_libfuzzer_LDADD = $(LDADD) $(libfuzzer_LIBS) -lstdc++
|
||||
packet_libfuzzer_SOURCES = packet_libfuzzer.c
|
||||
packet_libfuzzer_LDADD = $(top_builddir)/src/libknot.la
|
||||
|
||||
if SANITIZE_FUZZER
|
||||
packet_libfuzzer_LDFLAGS = -fsanitize=fuzzer
|
||||
else
|
||||
packet_libfuzzer_SOURCES += main.c
|
||||
AM_CPPFLAGS += -DTEST_RUN
|
||||
TESTS = $(FUZZERS)
|
||||
endif
|
||||
|
||||
packet_SOURCES = packet.c afl-loop.h
|
||||
knotd_stdio_SOURCES = wrap/server.c wrap/tcp-handler.c wrap/udp-handler.c afl-loop.h
|
||||
nodist_knotd_stdio_SOURCES = wrap/main.c
|
||||
knotd_stdio_CPPFLAGS = $(AM_CPPFLAGS) $(liburcu_CFLAGS)
|
||||
knotd_stdio_LDADD = \
|
||||
$(top_builddir)/src/libknotd.la $(top_builddir)/src/libcontrib.la \
|
||||
$(liburcu_LIBS)
|
||||
BUILT_SOURCES = wrap/main.c
|
||||
CLEANFILES = wrap/main.c
|
||||
wrap/main.c: Makefile $(top_builddir)/src/utils/knotd/main.c
|
||||
echo '#include "afl-loop.h"' > $@
|
||||
$(SED) -e 's/for (;;)/while (__AFL_LOOP(1000))/' $(top_srcdir)/src/utils/knotd/main.c >>$@
|
||||
|
||||
check-compile: $(check_PROGRAMS)
|
||||
|
|
|
|||
|
|
@ -1,66 +1 @@
|
|||
# Fuzzing
|
||||
|
||||
Knot DNS 2.0 includes two fuzzing tests in `tests-fuzz/`: a) a simple
|
||||
test harness that exercises the packet parsing logic in
|
||||
`packet.c` and more through test that replaces UDP handler with reads
|
||||
from stdin in `knotd_stdio.c`. This compiles into a test harness that
|
||||
is designed to be used with lcamtuf's [American Fuzzy Lop (AFL)
|
||||
fuzzer](http://lcamtuf.coredump.cx/afl/). We will use knotd_stdio in
|
||||
the following examples.
|
||||
|
||||
## How it works
|
||||
|
||||
AFL 1.83b includes an experimental feature called ["persistent
|
||||
mode"](http://lcamtuf.blogspot.com/2015/06/new-in-afl-persistent-mode.html)
|
||||
that can be used to control AFL's fork server to fuzz inputs and
|
||||
exercise the program without restarting it. You can use this new
|
||||
feature along with the included Knot DNS test harness.
|
||||
|
||||
## Using the AFL persistent harness
|
||||
|
||||
### Gathering seed inputs
|
||||
|
||||
Gathering DNS packets for use in fuzzing is left to the tester, but
|
||||
note that the fuzzing shim includes an environment variable to support
|
||||
test cases minimization with `afl-cmin`:
|
||||
|
||||
```
|
||||
$ cat > knot-afl.conf << EOF
|
||||
server:
|
||||
listen: 0.0.0.0@5353
|
||||
|
||||
log:
|
||||
- target: stderr
|
||||
any: error
|
||||
|
||||
control:
|
||||
listen: /tmp/knot.sock
|
||||
EOF
|
||||
$ afl-cmin -i ~/knot-seeds -o ~/knot-seeds-cmin -m 1000000 -t 400000 -- tests-fuzz/knotd_stdio -c knot-afl.conf
|
||||
```
|
||||
|
||||
You might want to configure some sample zones and have a test set of
|
||||
fuzzing data that would end up querying those zones.
|
||||
|
||||
### Compiling the test harness.
|
||||
|
||||
See the AFL [blog post](http://lcamtuf.blogspot.com/2015/06/new-in-afl-persistent-mode.html)
|
||||
and README for details on how to use LLVM mode and compile binaries
|
||||
for use with persistent mode. For reference, you can use these
|
||||
commands to build Knot with the fuzzing harness:
|
||||
|
||||
```
|
||||
$ CC=afl-clang-fast ./configure --disable-shared
|
||||
$ make check
|
||||
```
|
||||
|
||||
### Fuzz
|
||||
|
||||
A basic AFL run can then be kicked off as follows:
|
||||
|
||||
```
|
||||
AFL_PERSISTENT=1 afl-fuzz -i my_seeds -o my_output_dir -t 10000 -m 100000 -- tests-fuzz/knotd_stdio -c knot-afl.conf
|
||||
```
|
||||
|
||||
Note that AFL can be scaled up by supplying the `-M` flag and starting
|
||||
multiple instances of the fuzzer.
|
||||
https://gitlab.labs.nic.cz/knot/knot-dns/wikis/Fuzzing
|
||||
|
|
|
|||
136
tests-fuzz/main.c
Normal file
136
tests-fuzz/main.c
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright(c) 2017 Tim Ruehsen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||
|
||||
#ifdef TEST_RUN
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
static void test_all_from(const char *dirname)
|
||||
{
|
||||
DIR *dirp;
|
||||
struct dirent *dp;
|
||||
|
||||
if ((dirp = opendir(dirname))) {
|
||||
while ((dp = readdir(dirp))) {
|
||||
if (*dp->d_name == '.') continue;
|
||||
|
||||
char fname[strlen(dirname) + strlen(dp->d_name) + 2];
|
||||
snprintf(fname, sizeof(fname), "%s/%s", dirname, dp->d_name);
|
||||
|
||||
int fd;
|
||||
if ((fd = open(fname, O_RDONLY)) == -1) {
|
||||
fprintf(stderr, "Failed to open %s (%d)\n", fname, errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0) {
|
||||
fprintf(stderr, "Failed to stat %d (%d)\n", fd, errno);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t *data = malloc(st.st_size);
|
||||
ssize_t n;
|
||||
if ((n = read(fd, data, st.st_size)) == st.st_size) {
|
||||
printf("testing %llu bytes from '%s'\n", (unsigned long long) st.st_size, fname);
|
||||
fflush(stdout);
|
||||
LLVMFuzzerTestOneInput(data, st.st_size);
|
||||
fflush(stderr);
|
||||
} else {
|
||||
fprintf(stderr, "Failed to read %llu bytes from %s (%d), got %zd\n",
|
||||
(unsigned long long) st.st_size, fname, errno, n);
|
||||
}
|
||||
|
||||
free(data);
|
||||
close(fd);
|
||||
}
|
||||
closedir(dirp);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *target = strrchr(argv[0], '/');
|
||||
target = target ? target + 1 : argv[0];
|
||||
|
||||
char corporadir[sizeof(SRCDIR) + 1 + strlen(target) + 8];
|
||||
|
||||
if (strncmp(target, "lt-", 3) == 0) {
|
||||
target += 3;
|
||||
}
|
||||
|
||||
snprintf(corporadir, sizeof(corporadir), SRCDIR "/%s.in", target);
|
||||
|
||||
test_all_from(corporadir);
|
||||
|
||||
snprintf(corporadir, sizeof(corporadir), SRCDIR "/%s.repro", target);
|
||||
|
||||
test_all_from(corporadir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifndef __AFL_LOOP
|
||||
static int __AFL_LOOP(int n)
|
||||
{
|
||||
static int first = 1;
|
||||
|
||||
if (first) {
|
||||
first = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned char buf[64 * 1024];
|
||||
|
||||
while (__AFL_LOOP(10000)) { // only works with afl-clang-fast
|
||||
int ret = fread(buf, 1, sizeof(buf), stdin);
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LLVMFuzzerTestOneInput(buf, ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TEST_RUN */
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "libknot/libknot.h"
|
||||
#include "afl-loop.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
while (__AFL_LOOP(1000)) {
|
||||
uint8_t buffer[UINT16_MAX + 1] = { 0 };
|
||||
size_t len = fread(buffer, 1, sizeof(buffer), stdin);
|
||||
|
||||
knot_pkt_t *pkt = knot_pkt_new(buffer, len, NULL);
|
||||
assert(pkt);
|
||||
int r = knot_pkt_parse(pkt, 0);
|
||||
knot_pkt_free(&pkt);
|
||||
|
||||
return (r == KNOT_EOK ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
|
||||
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -16,22 +16,18 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "libknot/libknot.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size)
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
uint8_t *copy = malloc(size);
|
||||
assert(copy);
|
||||
uint8_t copy[size];
|
||||
memcpy(copy, data, size);
|
||||
|
||||
knot_pkt_t *pkt = knot_pkt_new(copy, size, NULL);
|
||||
assert(pkt);
|
||||
knot_pkt_parse(pkt, 0);
|
||||
knot_pkt_free(&pkt);
|
||||
|
||||
free(copy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
1
tests-fuzz/packet_libfuzzer.in
Submodule
1
tests-fuzz/packet_libfuzzer.in
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 69e4a98151063910675bce46efcdd151348dae9d
|
||||
Loading…
Reference in a new issue