From 7db7d5b73368fde010a258a5b2c48faf0d3e82c8 Mon Sep 17 00:00:00 2001 From: Jonathan Rudenberg Date: Sat, 13 Jan 2018 19:51:07 -0500 Subject: [PATCH] fuzz: add initial fuzzing infrastructure The fuzzers will be used by oss-fuzz to automatically and continuously fuzz systemd. This commit includes the build tooling necessary to build fuzz targets, and a fuzzer for the DNS packet parser. --- meson.build | 51 ++++++++++++++++++++++++++++++-- meson_options.txt | 3 ++ scripts/oss-fuzz.sh | 36 ++++++++++++++++++++++ src/fuzz/fuzz-dns-packet.c | 43 +++++++++++++++++++++++++++ src/fuzz/fuzz-dns-packet.options | 2 ++ src/fuzz/fuzz-main.c | 51 ++++++++++++++++++++++++++++++++ src/fuzz/fuzz.h | 23 ++++++++++++++ src/fuzz/meson.build | 25 ++++++++++++++++ tools/meson-check-api-docs.sh | 0 9 files changed, 232 insertions(+), 2 deletions(-) create mode 100755 scripts/oss-fuzz.sh create mode 100644 src/fuzz/fuzz-dns-packet.c create mode 100644 src/fuzz/fuzz-dns-packet.options create mode 100644 src/fuzz/fuzz-main.c create mode 100644 src/fuzz/fuzz.h create mode 100644 src/fuzz/meson.build mode change 100644 => 100755 tools/meson-check-api-docs.sh diff --git a/meson.build b/meson.build index 5066ccf4323..784a138f2ff 100644 --- a/meson.build +++ b/meson.build @@ -268,6 +268,11 @@ if get_option('tests') != 'false' endif endif +ossfuzz = get_option('oss-fuzz') +if ossfuzz + fuzzing_engine = meson.get_compiler('cpp').find_library('FuzzingEngine') +endif + foreach arg : ['-Wextra', '-Werror=undef', '-Wlogical-op', @@ -302,7 +307,6 @@ foreach arg : ['-Wextra', '-fvisibility=hidden', '-fstack-protector', '-fstack-protector-strong', - '-fPIE', '--param=ssp-buffer-size=4', ] if cc.has_argument(arg) @@ -310,6 +314,14 @@ foreach arg : ['-Wextra', endif endforeach +# the oss-fuzz fuzzers are not built with -fPIE, so don't +# enable it when we are linking against them +if not ossfuzz + if cc.has_argument('-fPIE') + add_project_arguments('-fPIE', language : 'c') + endif +endif + # "negative" arguments: gcc on purpose does not return an error for "-Wno-" # arguments, just emits a warnings. So test for the "positive" version instead. foreach arg : ['unused-parameter', @@ -360,7 +372,7 @@ foreach arg : ['-Wl,-z,relro', cc.cmd_array(), '-x', 'c', arg, '-include', link_test_c).returncode() == 0 message('Linking with @0@ supported: @1@'.format(arg, have ? 'yes' : 'no')) - if have + if have and (arg != '-pie' or not ossfuzz) add_project_link_arguments(arg, language : 'c') endif endforeach @@ -1178,6 +1190,7 @@ endforeach want_tests = get_option('tests') install_tests = get_option('install-tests') tests = [] +fuzzers = [] conf.set10('SYSTEMD_SLOW_TESTS_DEFAULT', get_option('slow-tests')) @@ -1303,6 +1316,7 @@ subdir('src/vconsole') subdir('src/boot/efi') subdir('src/test') +subdir('src/fuzz') subdir('rules') subdir('test') @@ -2457,6 +2471,39 @@ test('test-libudev-sym', ############################################################ +fuzzer_exes = [] + +foreach tuple : fuzzers + sources = tuple[0] + link_with = tuple[1].length() > 0 ? tuple[1] : [libshared] + dependencies = tuple[2] + defs = tuple.length() >= 4 ? tuple[3] : [] + incs = tuple.length() >= 5 ? tuple[4] : includes + + if ossfuzz + dependencies += fuzzing_engine + else + sources += 'src/fuzz/fuzz-main.c' + endif + + name = sources[0].split('/')[-1].split('.')[0] + + fuzzer_exes += executable( + name, + sources, + include_directories : [incs, include_directories('src/fuzz')], + link_with : link_with, + dependencies : dependencies, + c_args : defs, + install : false) +endforeach + +run_target('fuzzers', + depends : fuzzer_exes, + command : ['true']) + +############################################################ + make_directive_index_py = find_program('tools/make-directive-index.py') make_man_index_py = find_program('tools/make-man-index.py') xml_helper_py = find_program('tools/xml_helper.py') diff --git a/meson_options.txt b/meson_options.txt index af8be576a10..9e7cad7471a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -291,3 +291,6 @@ option('slow-tests', type : 'boolean', value : 'false', description : 'run the slow tests by default') option('install-tests', type : 'boolean', value : 'false', description : 'install test executables') + +option('oss-fuzz', type : 'boolean', value : 'false', + description : 'build against oss-fuzz') diff --git a/scripts/oss-fuzz.sh b/scripts/oss-fuzz.sh new file mode 100755 index 00000000000..b871d744b96 --- /dev/null +++ b/scripts/oss-fuzz.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# SPDX-License-Identifier: LGPL-2.1+ +# +# Copyright 2017 Jonathan Rudenberg +# +# systemd 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.1 of the License, or +# (at your option) any later version. +# +# systemd 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 systemd; If not, see . + +set -ex + +export LC_CTYPE=C.UTF-8 + +meson $WORK -Doss-fuzz=true -Db_lundef=false -Dseccomp=false -Dlz4=false -Dlibiptc=false -Dlibidn=false +ninja -C $WORK fuzzers + +# get DNS packet corpus +df=$WORK/dns-fuzzing +rm -rf $df +git clone --depth 1 https://github.com/CZ-NIC/dns-fuzzing $df +zip -jqr $OUT/fuzz-dns-packet_seed_corpus.zip $df/packet + +mkdir -p $OUT/src/shared +mv $WORK/src/shared/libsystemd-shared-*.so $OUT/src/shared + +find $WORK -maxdepth 1 -type f -executable -name "fuzz-*" -exec mv {} $OUT \; +mv $WORK/*.so src/fuzz/*.options $OUT diff --git a/src/fuzz/fuzz-dns-packet.c b/src/fuzz/fuzz-dns-packet.c new file mode 100644 index 00000000000..3d8d79a42df --- /dev/null +++ b/src/fuzz/fuzz-dns-packet.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + Copyright 2018 Jonathan Rudenberg + + systemd 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.1 of the License, or + (at your option) any later version. + + systemd 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 systemd; If not, see . +***/ + +#include "fuzz.h" +#include "resolved-dns-packet.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + int r; + + if (size > DNS_PACKET_SIZE_MAX) + return 0; + + r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX); + if (r < 0) + return 0; + p->size = 0; /* by default append starts after the header, undo that */ + dns_packet_append_blob(p, data, size, NULL); + if (size < DNS_PACKET_HEADER_SIZE) { + /* make sure we pad the packet back up to the minimum header size */ + assert(p->allocated >= DNS_PACKET_HEADER_SIZE); + memzero(DNS_PACKET_DATA(p) + size, DNS_PACKET_HEADER_SIZE - size); + p->size = DNS_PACKET_HEADER_SIZE; + } + dns_packet_extract(p); + + return 0; +} diff --git a/src/fuzz/fuzz-dns-packet.options b/src/fuzz/fuzz-dns-packet.options new file mode 100644 index 00000000000..0824b19fab4 --- /dev/null +++ b/src/fuzz/fuzz-dns-packet.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/src/fuzz/fuzz-main.c b/src/fuzz/fuzz-main.c new file mode 100644 index 00000000000..ca9abe90685 --- /dev/null +++ b/src/fuzz/fuzz-main.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + Copyright 2018 Jonathan Rudenberg + + systemd 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.1 of the License, or + (at your option) any later version. + + systemd 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 systemd; If not, see . +***/ + +#include "alloc-util.h" +#include "log.h" +#include "fileio.h" +#include "fuzz.h" + +/* This is a test driver for the systemd fuzzers that provides main function + * for regression testing outside of oss-fuzz (https://github.com/google/oss-fuzz) + * + * It reads files named on the command line and passes them one by one into the + * fuzzer that it is compiled into. */ + +int main(int argc, char **argv) { + int i, r; + size_t size; + char *name; + + log_set_max_level(LOG_DEBUG); + for (i = 1; i < argc; i++) { + _cleanup_free_ char *buf = NULL; + + name = argv[i]; + r = read_full_file(name, &buf, &size); + if (r < 0) { + log_error_errno(r, "Failed to open '%s': %m", name); + return EXIT_FAILURE; + } + printf("%s... ", name); + fflush(stdout); + (void)LLVMFuzzerTestOneInput((uint8_t*)buf, size); + printf("ok\n"); + } + return EXIT_SUCCESS; +} diff --git a/src/fuzz/fuzz.h b/src/fuzz/fuzz.h new file mode 100644 index 00000000000..5293c5762f2 --- /dev/null +++ b/src/fuzz/fuzz.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + Copyright 2018 Jonathan Rudenberg + + systemd 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.1 of the License, or + (at your option) any later version. + + systemd 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 systemd; If not, see . +***/ + +#include +#include + +/* The entry point into the fuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build new file mode 100644 index 00000000000..66cdf13dbd3 --- /dev/null +++ b/src/fuzz/meson.build @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# Copyright 2018 Jonathan Rudenberg +# +# systemd 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.1 of the License, or +# (at your option) any later version. +# +# systemd 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 systemd; If not, see . + +fuzzers += [ + [['src/fuzz/fuzz-dns-packet.c', + dns_type_headers], + [libsystemd_resolve_core, + libshared], + [libgcrypt, + libgpg_error, + libm]], +] diff --git a/tools/meson-check-api-docs.sh b/tools/meson-check-api-docs.sh old mode 100644 new mode 100755