From 4f38db1d28a971f938400f558e968fdffb9233a0 Mon Sep 17 00:00:00 2001 From: Max Dymond Date: Mon, 25 Sep 2017 10:59:18 +0100 Subject: fuzzer: move to using external curl-fuzzer Use the external curl-fuzzer repository for fuzzing. Closes #1923 --- .travis.yml | 19 +- Makefile.am | 3 - configure.ac | 1 - tests/Makefile.am | 2 +- tests/fuzz/Makefile.am | 56 ---- tests/fuzz/Makefile.inc | 15 - tests/fuzz/README | 19 +- tests/fuzz/corpus.py | 96 ------ tests/fuzz/curl_fuzz_data/oss-fuzz-3327 | Bin 27 -> 0 bytes tests/fuzz/curl_fuzz_data/test1 | Bin 289 -> 0 bytes tests/fuzz/curl_fuzz_data/test10 | Bin 226 -> 0 bytes tests/fuzz/curl_fuzz_data/test100 | Bin 675 -> 0 bytes tests/fuzz/curl_fuzz_data/test12 | Bin 464 -> 0 bytes tests/fuzz/curl_fuzz_data/test13 | Bin 179 -> 0 bytes tests/fuzz/curl_fuzz_data/test1326 | Bin 62 -> 0 bytes tests/fuzz/curl_fuzz_data/test1450 | Bin 98 -> 0 bytes tests/fuzz/curl_fuzz_data/test2 | Bin 182 -> 0 bytes tests/fuzz/curl_fuzz_data/test271 | Bin 74 -> 0 bytes tests/fuzz/curl_fuzz_data/test3 | Bin 242 -> 0 bytes tests/fuzz/curl_fuzz_data/test4 | Bin 336 -> 0 bytes tests/fuzz/curl_fuzz_data/test5 | Bin 185 -> 0 bytes tests/fuzz/curl_fuzz_data/test6 | Bin 223 -> 0 bytes tests/fuzz/curl_fuzz_data/test800 | Bin 137 -> 0 bytes tests/fuzz/curl_fuzz_data/test850 | Bin 130 -> 0 bytes tests/fuzz/curl_fuzz_data/test900 | Bin 129 -> 0 bytes tests/fuzz/curl_fuzzer.cc | 447 ---------------------------- tests/fuzz/curl_fuzzer.h | 186 ------------ tests/fuzz/download_fuzzer.sh | 8 + tests/fuzz/generate_corpus.py | 132 -------- tests/fuzz/read_corpus.py | 69 ----- tests/fuzz/standalone_fuzz_target_runner.cc | 89 ------ tests/fuzz/testinput.h | 23 -- 32 files changed, 20 insertions(+), 1145 deletions(-) delete mode 100644 tests/fuzz/Makefile.am delete mode 100644 tests/fuzz/Makefile.inc delete mode 100644 tests/fuzz/corpus.py delete mode 100644 tests/fuzz/curl_fuzz_data/oss-fuzz-3327 delete mode 100644 tests/fuzz/curl_fuzz_data/test1 delete mode 100644 tests/fuzz/curl_fuzz_data/test10 delete mode 100644 tests/fuzz/curl_fuzz_data/test100 delete mode 100644 tests/fuzz/curl_fuzz_data/test12 delete mode 100644 tests/fuzz/curl_fuzz_data/test13 delete mode 100644 tests/fuzz/curl_fuzz_data/test1326 delete mode 100644 tests/fuzz/curl_fuzz_data/test1450 delete mode 100644 tests/fuzz/curl_fuzz_data/test2 delete mode 100644 tests/fuzz/curl_fuzz_data/test271 delete mode 100644 tests/fuzz/curl_fuzz_data/test3 delete mode 100644 tests/fuzz/curl_fuzz_data/test4 delete mode 100644 tests/fuzz/curl_fuzz_data/test5 delete mode 100644 tests/fuzz/curl_fuzz_data/test6 delete mode 100644 tests/fuzz/curl_fuzz_data/test800 delete mode 100644 tests/fuzz/curl_fuzz_data/test850 delete mode 100644 tests/fuzz/curl_fuzz_data/test900 delete mode 100644 tests/fuzz/curl_fuzzer.cc delete mode 100644 tests/fuzz/curl_fuzzer.h create mode 100755 tests/fuzz/download_fuzzer.sh delete mode 100755 tests/fuzz/generate_corpus.py delete mode 100755 tests/fuzz/read_corpus.py delete mode 100644 tests/fuzz/standalone_fuzz_target_runner.cc delete mode 100644 tests/fuzz/testinput.h diff --git a/.travis.yml b/.travis.yml index 541628d99..b7776c98d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -156,18 +156,15 @@ script: fi - | if [ "$T" = "fuzzer" ]; then - export CC=clang - export CXX=clang++ - export CFLAGS="-fsanitize=address" + # Download the fuzzer to a temporary folder + ./tests/fuzz/download_fuzzer.sh /tmp/curl_fuzzer - # Specifically use libstdc++ for travis as libc++ is not installed. - # This is ok because we're not compiling against libFuzzer. - export CXXFLAGS="-fsanitize=address -stdlib=libstdc++" - ./configure --disable-shared --enable-debug --enable-maintainer-mode - make - cd tests/fuzz - make clean - make check + export CURLSRC=$PWD + + # Run the mainline fuzzer test + pushd /tmp/curl_fuzzer + ./mainline.sh ${CURLSRC} + popd fi # whitelist branches to avoid testing feature branches twice (as branch and as pull request) diff --git a/Makefile.am b/Makefile.am index e517ba56e..ab8f11cbc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -210,9 +210,6 @@ test-am: endif -fuzzer: - @(cd tests/fuzz; $(MAKE) all) - examples: @(cd docs/examples; $(MAKE) check) diff --git a/configure.ac b/configure.ac index 4d5018d91..56b8756ba 100755 --- a/configure.ac +++ b/configure.ac @@ -3974,7 +3974,6 @@ AC_CONFIG_FILES([Makefile \ tests/server/Makefile \ tests/libtest/Makefile \ tests/unit/Makefile \ - tests/fuzz/Makefile \ packages/Makefile \ packages/Win32/Makefile \ packages/Win32/cygwin/Makefile \ diff --git a/tests/Makefile.am b/tests/Makefile.am index 3d1848992..d6e6cfe29 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -53,7 +53,7 @@ DIST_UNIT = unit endif SUBDIRS = certs data server libtest $(BUILD_UNIT) -DIST_SUBDIRS = $(SUBDIRS) $(DIST_UNIT) fuzz +DIST_SUBDIRS = $(SUBDIRS) $(DIST_UNIT) PERLFLAGS = -I$(srcdir) diff --git a/tests/fuzz/Makefile.am b/tests/fuzz/Makefile.am deleted file mode 100644 index 3bd24dd66..000000000 --- a/tests/fuzz/Makefile.am +++ /dev/null @@ -1,56 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. -# -# This software is licensed as described in the file COPYING, which -# you should have received as part of this distribution. The terms -# are also available at https://curl.haxx.se/docs/copyright.html. -# -# You may opt to use, copy, modify, merge, publish, distribute and/or sell -# copies of the Software, and permit persons to whom the Software is -# furnished to do so, under the terms of the COPYING file. -# -# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY -# KIND, either express or implied. -# -########################################################################### -AUTOMAKE_OPTIONS = foreign nostdinc - -# Specify our include paths here, and do it relative to $(top_srcdir) and -# $(top_builddir), to ensure that these paths which belong to the library -# being currently built and tested are searched before the library which -# might possibly already be installed in the system. -# -# $(top_srcdir)/include is for libcurl's external include files -# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file -# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files - -AM_CXXFLAGS = -I$(top_srcdir)/include \ - -I$(top_builddir)/lib \ - -I$(top_srcdir)/lib \ - -I$(top_srcdir)/tests/fuzz - -LIBS = -lpthread -lm - -# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a" -# to link the fuzzer(s) against a real fuzzing engine. -# -# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE. -LIB_FUZZING_ENGINE ?= libstandaloneengine.a - -LDADD = $(top_builddir)/lib/libcurl.la \ - $(LIB_FUZZING_ENGINE) @LDFLAGS@ @LIBCURL_LIBS@ - -# Makefile.inc provides neat definitions -include Makefile.inc - -checksrc: - @PERL@ $(top_srcdir)/lib/checksrc.pl $(srcdir)/*.cc - -noinst_PROGRAMS = $(FUZZPROGS) -noinst_LIBRARIES = $(FUZZLIBS) diff --git a/tests/fuzz/Makefile.inc b/tests/fuzz/Makefile.inc deleted file mode 100644 index f52adb89c..000000000 --- a/tests/fuzz/Makefile.inc +++ /dev/null @@ -1,15 +0,0 @@ -FUZZPROGS = curl_fuzzer -FUZZLIBS = libstandaloneengine.a - -curl_fuzzer_SOURCES = curl_fuzzer.cc -curl_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) - -libstandaloneengine_a_SOURCES = standalone_fuzz_target_runner.cc -libstandaloneengine_a_CXXFLAGS = $(AM_CXXFLAGS) - -# Some more targets. -zip: - zip -q -r curl_fuzzer_seed_corpus.zip curl_fuzz_data - -check: all - ./curl_fuzzer curl_fuzz_data/* diff --git a/tests/fuzz/README b/tests/fuzz/README index 8b5fcd011..c19e04fbe 100644 --- a/tests/fuzz/README +++ b/tests/fuzz/README @@ -1,21 +1,8 @@ Fuzz tests ========== -The goal is to add tests for *ALL* protocols supported in libcurl. +The fuzzing tests for curl have been moved to a separate repository: -Building the fuzz target -======================== -From the CURL root directory: +https://github.com/curl/curl-fuzzer -export CC=clang-5.0 -export CXX=clang++-5.0 -export CFLAGS="-fsanitize=address -fsanitize-address-use-after-scope -fsanitize-coverage=trace-pc-guard,trace-cmp" -export CXXFLAGS="-fsanitize=address -fsanitize-address-use-after-scope -fsanitize-coverage=trace-pc-guard,trace-cmp -stdlib=libc++" -./configure --disable-shared --enable-debug --enable-maintainer-mode -make -sj - -cd tests/fuzz - -(optional) export LIB_FUZZING_ENGINE= - -make check +More information on how to get started with curl fuzz testing can be found there. diff --git a/tests/fuzz/corpus.py b/tests/fuzz/corpus.py deleted file mode 100644 index 5474c99af..000000000 --- a/tests/fuzz/corpus.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python -# -# Common corpus functions -import logging -import struct -log = logging.getLogger(__name__) - - -class BaseType(object): - TYPE_URL = 1 - TYPE_RSP1 = 2 - TYPE_USERNAME = 3 - TYPE_PASSWORD = 4 - TYPE_POSTFIELDS = 5 - TYPE_HEADER = 6 - TYPE_COOKIE = 7 - TYPE_UPLOAD1 = 8 - TYPE_RANGE = 9 - TYPE_CUSTOMREQUEST = 10 - TYPE_MAIL_RECIPIENT = 11 - TYPE_MAIL_FROM = 12 - - -class TLVEncoder(BaseType): - def __init__(self, output): - self.output = output - - def write_string(self, tlv_type, wstring): - data = wstring.encode("utf-8") - self.write_tlv(tlv_type, len(data), data) - - def write_bytes(self, tlv_type, bytedata): - self.write_tlv(tlv_type, len(bytedata), bytedata) - - def maybe_write_string(self, tlv_type, wstring): - if wstring is not None: - self.write_string(tlv_type, wstring) - - def write_tlv(self, tlv_type, tlv_length, tlv_data=None): - log.debug("Writing TLV %d, length %d, data %r", - tlv_type, - tlv_length, - tlv_data) - - data = struct.pack("!H", tlv_type) - self.output.write(data) - - data = struct.pack("!L", tlv_length) - self.output.write(data) - - if tlv_data: - self.output.write(tlv_data) - - -class TLVDecoder(BaseType): - def __init__(self, inputdata): - self.inputdata = inputdata - self.pos = 0 - self.tlv = None - - def __iter__(self): - self.pos = 0 - self.tlv = None - return self - - def __next__(self): - if self.tlv: - self.pos += self.tlv.total_length() - - if (self.pos + TLVHeader.TLV_DECODE_FMT_LEN) > len(self.inputdata): - raise StopIteration - - # Get the next TLV - self.tlv = TLVHeader(self.inputdata[self.pos:]) - return self.tlv - - next = __next__ - - -class TLVHeader(BaseType): - TLV_DECODE_FMT = "!HL" - TLV_DECODE_FMT_LEN = struct.calcsize(TLV_DECODE_FMT) - - def __init__(self, data): - # Parse the data to populate the TLV fields - (self.type, self.length) = struct.unpack(self.TLV_DECODE_FMT, data[0:self.TLV_DECODE_FMT_LEN]) - - # Get the remaining data and store it. - self.data = data[self.TLV_DECODE_FMT_LEN:self.TLV_DECODE_FMT_LEN + self.length] - - def __repr__(self): - return ("{self.__class__.__name__}(type={self.type!r}, length={self.length!r}, data={self.data!r})" - .format(self=self)) - - def total_length(self): - return self.TLV_DECODE_FMT_LEN + self.length \ No newline at end of file diff --git a/tests/fuzz/curl_fuzz_data/oss-fuzz-3327 b/tests/fuzz/curl_fuzz_data/oss-fuzz-3327 deleted file mode 100644 index 064cc623a..000000000 Binary files a/tests/fuzz/curl_fuzz_data/oss-fuzz-3327 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test1 b/tests/fuzz/curl_fuzz_data/test1 deleted file mode 100644 index f7b734a9c..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test1 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test10 b/tests/fuzz/curl_fuzz_data/test10 deleted file mode 100644 index af1ed53ca..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test10 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test100 b/tests/fuzz/curl_fuzz_data/test100 deleted file mode 100644 index b58d9335a..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test100 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test12 b/tests/fuzz/curl_fuzz_data/test12 deleted file mode 100644 index 9ad91dc07..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test12 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test13 b/tests/fuzz/curl_fuzz_data/test13 deleted file mode 100644 index 448077dde..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test13 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test1326 b/tests/fuzz/curl_fuzz_data/test1326 deleted file mode 100644 index 8801fac96..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test1326 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test1450 b/tests/fuzz/curl_fuzz_data/test1450 deleted file mode 100644 index 601236168..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test1450 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test2 b/tests/fuzz/curl_fuzz_data/test2 deleted file mode 100644 index 8b44d6719..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test2 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test271 b/tests/fuzz/curl_fuzz_data/test271 deleted file mode 100644 index 3a26767f7..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test271 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test3 b/tests/fuzz/curl_fuzz_data/test3 deleted file mode 100644 index 81c6670aa..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test3 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test4 b/tests/fuzz/curl_fuzz_data/test4 deleted file mode 100644 index 3fa395a29..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test4 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test5 b/tests/fuzz/curl_fuzz_data/test5 deleted file mode 100644 index bdaac4e66..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test5 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test6 b/tests/fuzz/curl_fuzz_data/test6 deleted file mode 100644 index 98d9be216..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test6 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test800 b/tests/fuzz/curl_fuzz_data/test800 deleted file mode 100644 index a5899be43..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test800 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test850 b/tests/fuzz/curl_fuzz_data/test850 deleted file mode 100644 index 22c08dcfc..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test850 and /dev/null differ diff --git a/tests/fuzz/curl_fuzz_data/test900 b/tests/fuzz/curl_fuzz_data/test900 deleted file mode 100644 index eecf0cbaf..000000000 Binary files a/tests/fuzz/curl_fuzz_data/test900 and /dev/null differ diff --git a/tests/fuzz/curl_fuzzer.cc b/tests/fuzz/curl_fuzzer.cc deleted file mode 100644 index dd0298f36..000000000 --- a/tests/fuzz/curl_fuzzer.cc +++ /dev/null @@ -1,447 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017, Max Dymond, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include "curl_fuzzer.h" - -/** - * Fuzzing entry point. This function is passed a buffer containing a test - * case. This test case should drive the CURL API into making a request. - */ -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) -{ - int rc = 0; - int tlv_rc; - FUZZ_DATA fuzz; - TLV tlv; - - /* Have to set all fields to zero before getting to the terminate function */ - memset(&fuzz, 0, sizeof(FUZZ_DATA)); - - if(size < sizeof(TLV_RAW)) { - /* Not enough data for a single TLV - don't continue */ - goto EXIT_LABEL; - } - - /* Try to initialize the fuzz data */ - FTRY(fuzz_initialize_fuzz_data(&fuzz, data, size)); - - for(tlv_rc = fuzz_get_first_tlv(&fuzz, &tlv); - tlv_rc == 0; - tlv_rc = fuzz_get_next_tlv(&fuzz, &tlv)) { - - /* Have the TLV in hand. Parse the TLV. */ - rc = fuzz_parse_tlv(&fuzz, &tlv); - - if(rc != 0) { - /* Failed to parse the TLV. Can't continue. */ - goto EXIT_LABEL; - } - } - - if(tlv_rc != TLV_RC_NO_MORE_TLVS) { - /* A TLV call failed. Can't continue. */ - goto EXIT_LABEL; - } - - /* Do the CURL stuff! */ - if(fuzz.header_list != NULL) { - curl_easy_setopt(fuzz.easy, CURLOPT_HTTPHEADER, fuzz.header_list); - } - - if(fuzz.mail_recipients_list != NULL) { - curl_easy_setopt(fuzz.easy, CURLOPT_MAIL_RCPT, fuzz.mail_recipients_list); - } - - curl_easy_perform(fuzz.easy); - -EXIT_LABEL: - - fuzz_terminate_fuzz_data(&fuzz); - - /* This function must always return 0. Non-zero codes are reserved. */ - return 0; -} - -/** - * Utility function to convert 4 bytes to a u32 predictably. - */ -uint32_t to_u32(uint8_t b[4]) -{ - uint32_t u; - u = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; - return u; -} - -/** - * Utility function to convert 2 bytes to a u16 predictably. - */ -uint16_t to_u16(uint8_t b[2]) -{ - uint16_t u; - u = (b[0] << 8) + b[1]; - return u; -} - -/** - * Initialize the local fuzz data structure. - */ -int fuzz_initialize_fuzz_data(FUZZ_DATA *fuzz, - const uint8_t *data, - size_t data_len) -{ - int rc = 0; - - /* Initialize the fuzz data. */ - memset(fuzz, 0, sizeof(FUZZ_DATA)); - - /* Create an easy handle. This will have all of the settings configured on - it. */ - fuzz->easy = curl_easy_init(); - FCHECK(fuzz->easy != NULL); - - /* Set some standard options on the CURL easy handle. We need to override the - socket function so that we create our own sockets to present to CURL. */ - FTRY(curl_easy_setopt(fuzz->easy, - CURLOPT_OPENSOCKETFUNCTION, - fuzz_open_socket)); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_OPENSOCKETDATA, fuzz)); - - /* In case something tries to set a socket option, intercept this. */ - FTRY(curl_easy_setopt(fuzz->easy, - CURLOPT_SOCKOPTFUNCTION, - fuzz_sockopt_callback)); - - /* Set the standard read function callback. */ - FTRY(curl_easy_setopt(fuzz->easy, - CURLOPT_READFUNCTION, - fuzz_read_callback)); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_READDATA, fuzz)); - - /* Set the standard write function callback. */ - FTRY(curl_easy_setopt(fuzz->easy, - CURLOPT_WRITEFUNCTION, - fuzz_write_callback)); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_WRITEDATA, fuzz)); - - /* Can enable verbose mode by changing 0L to 1L */ - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_VERBOSE, 0L)); - - /* Set up the state parser */ - fuzz->state.data = data; - fuzz->state.data_len = data_len; - -EXIT_LABEL: - - return rc; -} - -/** - * Terminate the fuzz data structure, including freeing any allocated memory. - */ -void fuzz_terminate_fuzz_data(FUZZ_DATA *fuzz) -{ - fuzz_free((void **)&fuzz->url); - fuzz_free((void **)&fuzz->username); - fuzz_free((void **)&fuzz->password); - fuzz_free((void **)&fuzz->postfields); - fuzz_free((void **)&fuzz->cookie); - fuzz_free((void **)&fuzz->range); - fuzz_free((void **)&fuzz->customrequest); - fuzz_free((void **)&fuzz->mail_from); - - if(fuzz->header_list != NULL) { - curl_slist_free_all(fuzz->header_list); - fuzz->header_list = NULL; - } - - if(fuzz->mail_recipients_list != NULL) { - curl_slist_free_all(fuzz->mail_recipients_list); - fuzz->mail_recipients_list = NULL; - } - - if(fuzz->easy != NULL) { - curl_easy_cleanup(fuzz->easy); - fuzz->easy = NULL; - } -} - -/** - * If a pointer has been allocated, free that pointer. - */ -void fuzz_free(void **ptr) -{ - if(*ptr != NULL) { - free(*ptr); - *ptr = NULL; - } -} - -/** - * Function for providing a socket to CURL already primed with data. - */ -static curl_socket_t fuzz_open_socket(void *ptr, - curlsocktype purpose, - struct curl_sockaddr *address) -{ - FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr; - int fds[2]; - curl_socket_t server_fd; - curl_socket_t client_fd; - - /* Handle unused parameters */ - (void)purpose; - (void)address; - - if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { - /* Failed to create a pair of sockets. */ - return CURL_SOCKET_BAD; - } - - server_fd = fds[0]; - client_fd = fds[1]; - - /* Try and write the response data to the server file descriptor so the - client can read it. */ - if(write(server_fd, - fuzz->rsp1_data, - fuzz->rsp1_data_len) != (ssize_t)fuzz->rsp1_data_len) { - /* Failed to write the data. */ - return CURL_SOCKET_BAD; - } - - if(shutdown(server_fd, SHUT_WR)) { - return CURL_SOCKET_BAD; - } - - return client_fd; -} - -/** - * Callback function for setting socket options on the sockets created by - * fuzz_open_socket. In our testbed the sockets are "already connected". - */ -static int fuzz_sockopt_callback(void *ptr, - curl_socket_t curlfd, - curlsocktype purpose) -{ - (void)ptr; - (void)curlfd; - (void)purpose; - - return CURL_SOCKOPT_ALREADY_CONNECTED; -} - -/** - * Callback function for doing data uploads. - */ -static size_t fuzz_read_callback(char *buffer, - size_t size, - size_t nitems, - void *ptr) -{ - FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr; - curl_off_t nread; - - /* If no upload data has been specified, then return an error code. */ - if(fuzz->upload1_data_len == 0) { - /* No data to upload */ - return CURL_READFUNC_ABORT; - } - - /* Send the upload data. */ - memcpy(buffer, - fuzz->upload1_data, - fuzz->upload1_data_len); - - return fuzz->upload1_data_len; -} - -/** - * Callback function for handling data output quietly. - */ -static size_t fuzz_write_callback(void *contents, - size_t size, - size_t nmemb, - void *ptr) -{ - size_t total = size * nmemb; - FUZZ_DATA *fuzz = (FUZZ_DATA *)ptr; - size_t copy_len = total; - - /* Restrict copy_len to at most TEMP_WRITE_ARRAY_SIZE. */ - if(copy_len > TEMP_WRITE_ARRAY_SIZE) { - copy_len = TEMP_WRITE_ARRAY_SIZE; - } - - /* Copy bytes to the temp store just to ensure the parameters are - exercised. */ - memcpy(fuzz->write_array, contents, copy_len); - - return total; -} - -/** - * TLV access function - gets the first TLV from a data stream. - */ -int fuzz_get_first_tlv(FUZZ_DATA *fuzz, - TLV *tlv) -{ - /* Reset the cursor. */ - fuzz->state.data_pos = 0; - return fuzz_get_tlv_comn(fuzz, tlv); -} - -/** - * TLV access function - gets the next TLV from a data stream. -*/ -int fuzz_get_next_tlv(FUZZ_DATA *fuzz, - TLV *tlv) -{ - /* Advance the cursor by the full length of the previous TLV. */ - fuzz->state.data_pos += sizeof(TLV_RAW) + tlv->length; - - /* Work out if there's a TLV's worth of data to read */ - if(fuzz->state.data_pos + sizeof(TLV_RAW) > fuzz->state.data_len) { - /* No more TLVs to parse */ - return TLV_RC_NO_MORE_TLVS; - } - - return fuzz_get_tlv_comn(fuzz, tlv); -} - -/** - * Common TLV function for accessing TLVs in a data stream. - */ -int fuzz_get_tlv_comn(FUZZ_DATA *fuzz, - TLV *tlv) -{ - int rc = 0; - size_t data_offset; - TLV_RAW *raw; - - /* Start by casting the data stream to a TLV. */ - raw = (TLV_RAW *)&fuzz->state.data[fuzz->state.data_pos]; - data_offset = fuzz->state.data_pos + sizeof(TLV_RAW); - - /* Set the TLV values. */ - tlv->type = to_u16(raw->raw_type); - tlv->length = to_u32(raw->raw_length); - tlv->value = &fuzz->state.data[data_offset]; - - /* Sanity check that the TLV length is ok. */ - if(data_offset + tlv->length > fuzz->state.data_len) { - rc = TLV_RC_SIZE_ERROR; - } - - return rc; -} - -/** - * Do different actions on the CURL handle for different received TLVs. - */ -int fuzz_parse_tlv(FUZZ_DATA *fuzz, TLV *tlv) -{ - int rc; - char *tmp; - - switch(tlv->type) { - case TLV_TYPE_RESPONSE1: - /* The pointers in the TLV will always be valid as long as the fuzz data - is in scope, which is the entirety of this file. */ - fuzz->rsp1_data = tlv->value; - fuzz->rsp1_data_len = tlv->length; - break; - - case TLV_TYPE_UPLOAD1: - /* The pointers in the TLV will always be valid as long as the fuzz data - is in scope, which is the entirety of this file. */ - fuzz->upload1_data = tlv->value; - fuzz->upload1_data_len = tlv->length; - - curl_easy_setopt(fuzz->easy, CURLOPT_UPLOAD, 1L); - curl_easy_setopt(fuzz->easy, - CURLOPT_INFILESIZE_LARGE, - (curl_off_t)fuzz->upload1_data_len); - break; - - case TLV_TYPE_HEADER: - tmp = fuzz_tlv_to_string(tlv); - fuzz->header_list = curl_slist_append(fuzz->header_list, tmp); - fuzz_free((void **)&tmp); - break; - - case TLV_TYPE_MAIL_RECIPIENT: - tmp = fuzz_tlv_to_string(tlv); - fuzz->mail_recipients_list = - curl_slist_append(fuzz->mail_recipients_list, tmp); - fuzz_free((void **)&tmp); - break; - - /* Define a set of singleton TLVs - they can only have their value set once - and all follow the same pattern. */ - FSINGLETONTLV(TLV_TYPE_URL, url, CURLOPT_URL); - FSINGLETONTLV(TLV_TYPE_USERNAME, username, CURLOPT_USERNAME); - FSINGLETONTLV(TLV_TYPE_PASSWORD, password, CURLOPT_PASSWORD); - FSINGLETONTLV(TLV_TYPE_POSTFIELDS, postfields, CURLOPT_POSTFIELDS); - FSINGLETONTLV(TLV_TYPE_COOKIE, cookie, CURLOPT_COOKIE); - FSINGLETONTLV(TLV_TYPE_RANGE, range, CURLOPT_RANGE); - FSINGLETONTLV(TLV_TYPE_CUSTOMREQUEST, customrequest, CURLOPT_CUSTOMREQUEST); - FSINGLETONTLV(TLV_TYPE_MAIL_FROM, mail_from, CURLOPT_MAIL_FROM); - - default: - /* The fuzzer generates lots of unknown TLVs - we don't want these in the - corpus so we reject any unknown TLVs. */ - rc = 255; - goto EXIT_LABEL; - break; - } - - rc = 0; - -EXIT_LABEL: - - return rc; -} - -/** - * Converts a TLV data and length into an allocated string. - */ -char *fuzz_tlv_to_string(TLV *tlv) -{ - char *tlvstr; - - /* Allocate enough space, plus a null terminator */ - tlvstr = (char *)malloc(tlv->length + 1); - - if(tlvstr != NULL) { - memcpy(tlvstr, tlv->value, tlv->length); - tlvstr[tlv->length] = 0; - } - - return tlvstr; -} diff --git a/tests/fuzz/curl_fuzzer.h b/tests/fuzz/curl_fuzzer.h deleted file mode 100644 index 219ac3ee3..000000000 --- a/tests/fuzz/curl_fuzzer.h +++ /dev/null @@ -1,186 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017, Max Dymond, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include -#include - -/** - * TLV types. - */ -#define TLV_TYPE_URL 1 -#define TLV_TYPE_RESPONSE1 2 -#define TLV_TYPE_USERNAME 3 -#define TLV_TYPE_PASSWORD 4 -#define TLV_TYPE_POSTFIELDS 5 -#define TLV_TYPE_HEADER 6 -#define TLV_TYPE_COOKIE 7 -#define TLV_TYPE_UPLOAD1 8 -#define TLV_TYPE_RANGE 9 -#define TLV_TYPE_CUSTOMREQUEST 10 -#define TLV_TYPE_MAIL_RECIPIENT 11 -#define TLV_TYPE_MAIL_FROM 12 - -/** - * TLV function return codes. - */ -#define TLV_RC_NO_ERROR 0 -#define TLV_RC_NO_MORE_TLVS 1 -#define TLV_RC_SIZE_ERROR 2 - -/* Temporary write array size */ -#define TEMP_WRITE_ARRAY_SIZE 10 - -/** - * Byte stream representation of the TLV header. Casting the byte stream - * to a TLV_RAW allows us to examine the type and length. - */ -typedef struct tlv_raw -{ - /* Type of the TLV - 16 bits. */ - uint8_t raw_type[2]; - - /* Length of the TLV data - 32 bits. */ - uint8_t raw_length[4]; - -} TLV_RAW; - -typedef struct tlv -{ - /* Type of the TLV */ - uint16_t type; - - /* Length of the TLV data */ - uint32_t length; - - /* Pointer to data if length > 0. */ - const uint8_t *value; - -} TLV; - -/** - * Internal state when parsing a TLV data stream. - */ -typedef struct fuzz_parse_state -{ - /* Data stream */ - const uint8_t *data; - size_t data_len; - - /* Current position of our "cursor" in processing the data stream. */ - size_t data_pos; - -} FUZZ_PARSE_STATE; - -/** - * Data local to a fuzzing run. - */ -typedef struct fuzz_data -{ - /* CURL easy object */ - CURL *easy; - - /* Parser state */ - FUZZ_PARSE_STATE state; - - /* Temporary writefunction state */ - char write_array[TEMP_WRITE_ARRAY_SIZE]; - - /* Response data and length */ - const uint8_t *rsp1_data; - size_t rsp1_data_len; - - /* Upload data and length; */ - const uint8_t *upload1_data; - size_t upload1_data_len; - - /* Singleton string fields. */ - char *url; - char *username; - char *password; - char *postfields; - char *cookie; - char *range; - char *customrequest; - char *mail_from; - - /* List of headers */ - struct curl_slist *header_list; - - /* List of mail recipients */ - struct curl_slist *mail_recipients_list; - -} FUZZ_DATA; - -/* Function prototypes */ -uint32_t to_u32(uint8_t b[4]); -uint16_t to_u16(uint8_t b[2]); -int fuzz_initialize_fuzz_data(FUZZ_DATA *fuzz, - const uint8_t *data, - size_t data_len); -void fuzz_terminate_fuzz_data(FUZZ_DATA *fuzz); -void fuzz_free(void **ptr); -static curl_socket_t fuzz_open_socket(void *ptr, - curlsocktype purpose, - struct curl_sockaddr *address); -static int fuzz_sockopt_callback(void *ptr, - curl_socket_t curlfd, - curlsocktype purpose); -static size_t fuzz_read_callback(char *buffer, - size_t size, - size_t nitems, - void *ptr); -static size_t fuzz_write_callback(void *contents, - size_t size, - size_t nmemb, - void *ptr); -int fuzz_get_first_tlv(FUZZ_DATA *fuzz, TLV *tlv); -int fuzz_get_next_tlv(FUZZ_DATA *fuzz, TLV *tlv); -int fuzz_get_tlv_comn(FUZZ_DATA *fuzz, TLV *tlv); -int fuzz_parse_tlv(FUZZ_DATA *fuzz, TLV *tlv); -char *fuzz_tlv_to_string(TLV *tlv); - -/* Macros */ -#define FTRY(FUNC) \ - { \ - int _func_rc = (FUNC); \ - if (_func_rc) \ - { \ - rc = _func_rc; \ - goto EXIT_LABEL; \ - } \ - } - -#define FCHECK(COND) \ - { \ - if (!(COND)) \ - { \ - rc = 255; \ - goto EXIT_LABEL; \ - } \ - } - -#define FSINGLETONTLV(TLVNAME, FIELDNAME, OPTNAME) \ - case TLVNAME: \ - FCHECK(fuzz->FIELDNAME == NULL); \ - fuzz->FIELDNAME = fuzz_tlv_to_string(tlv); \ - FTRY(curl_easy_setopt(fuzz->easy, OPTNAME, fuzz->FIELDNAME)); \ - break \ No newline at end of file diff --git a/tests/fuzz/download_fuzzer.sh b/tests/fuzz/download_fuzzer.sh new file mode 100755 index 000000000..28228e004 --- /dev/null +++ b/tests/fuzz/download_fuzzer.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# If any commands fail, fail the script immediately. +set -ex + +# Clone the curl-fuzzer repository to the specified directory. +git clone http://github.com/curl/curl-fuzzer $1 + diff --git a/tests/fuzz/generate_corpus.py b/tests/fuzz/generate_corpus.py deleted file mode 100755 index cffdd37bf..000000000 --- a/tests/fuzz/generate_corpus.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python -# -# Simple script which generates corpus files. - -import argparse -import logging -import corpus -import sys -sys.path.append("..") -import curl_test_data -log = logging.getLogger(__name__) - - -def generate_corpus(options): - td = curl_test_data.TestData("../data") - - with open(options.output, "wb") as f: - enc = corpus.TLVEncoder(f) - - # Write the URL to the file. - enc.write_string(enc.TYPE_URL, options.url) - - # Write the first response to the file. - if options.rsp1: - enc.write_bytes(enc.TYPE_RSP1, options.rsp1.encode("utf-8")) - - elif options.rsp1file: - with open(options.rsp1file, "rb") as g: - enc.write_bytes(enc.TYPE_RSP1, g.read()) - - elif options.rsp1test: - wstring = td.get_test_data(options.rsp1test) - enc.write_bytes(enc.TYPE_RSP1, wstring.encode("utf-8")) - - # Write other options to file. - enc.maybe_write_string(enc.TYPE_USERNAME, options.username) - enc.maybe_write_string(enc.TYPE_PASSWORD, options.password) - enc.maybe_write_string(enc.TYPE_POSTFIELDS, options.postfields) - enc.maybe_write_string(enc.TYPE_COOKIE, options.cookie) - enc.maybe_write_string(enc.TYPE_RANGE, options.range) - enc.maybe_write_string(enc.TYPE_CUSTOMREQUEST, options.customrequest) - enc.maybe_write_string(enc.TYPE_MAIL_FROM, options.mailfrom) - - # Write the first upload to the file. - if options.upload1: - enc.write_bytes(enc.TYPE_UPLOAD1, options.upload1.encode("utf-8")) - elif options.upload1file: - with open(options.upload1file, "rb") as g: - enc.write_bytes(enc.TYPE_UPLOAD1, g.read()) - - # Write an array of headers to the file. - if options.header: - for header in options.header: - enc.write_string(enc.TYPE_HEADER, header) - - # Write an array of headers to the file. - if options.mailrecipient: - for mailrecipient in options.mailrecipient: - enc.write_string(enc.TYPE_MAIL_RECIPIENT, mailrecipient) - - return ScriptRC.SUCCESS - - -def get_options(): - parser = argparse.ArgumentParser() - parser.add_argument("--output", required=True) - parser.add_argument("--url", required=True) - parser.add_argument("--username") - parser.add_argument("--password") - parser.add_argument("--postfields") - parser.add_argument("--header", action="append") - parser.add_argument("--cookie") - parser.add_argument("--range") - parser.add_argument("--customrequest") - parser.add_argument("--mailfrom") - parser.add_argument("--mailrecipient", action="append") - - rsp1 = parser.add_mutually_exclusive_group(required=True) - rsp1.add_argument("--rsp1") - rsp1.add_argument("--rsp1file") - rsp1.add_argument("--rsp1test", type=int) - - upload1 = parser.add_mutually_exclusive_group() - upload1.add_argument("--upload1") - upload1.add_argument("--upload1file") - - return parser.parse_args() - - -def setup_logging(): - """ - Set up logging from the command line options - """ - root_logger = logging.getLogger() - formatter = logging.Formatter("%(asctime)s %(levelname)-5.5s %(message)s") - stdout_handler = logging.StreamHandler(sys.stdout) - stdout_handler.setFormatter(formatter) - stdout_handler.setLevel(logging.DEBUG) - root_logger.addHandler(stdout_handler) - root_logger.setLevel(logging.DEBUG) - - -class ScriptRC(object): - """Enum for script return codes""" - SUCCESS = 0 - FAILURE = 1 - EXCEPTION = 2 - - -class ScriptException(Exception): - pass - - -def main(): - # Get the options from the user. - options = get_options() - - setup_logging() - - # Run main script. - try: - rc = generate_corpus(options) - except Exception as e: - log.exception(e) - rc = ScriptRC.EXCEPTION - - log.info("Returning %d", rc) - return rc - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/tests/fuzz/read_corpus.py b/tests/fuzz/read_corpus.py deleted file mode 100755 index bb8fcedcd..000000000 --- a/tests/fuzz/read_corpus.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -# Simple script which reads corpus files. - -import argparse -import logging -import sys -import corpus -log = logging.getLogger(__name__) - - -def read_corpus(options): - with open(options.input, "rb") as f: - dec = corpus.TLVDecoder(f.read()) - for tlv in dec: - print(tlv) - - return ScriptRC.SUCCESS - - -def get_options(): - parser = argparse.ArgumentParser() - parser.add_argument("--input", required=True) - return parser.parse_args() - - -def setup_logging(): - """ - Set up logging from the command line options - """ - root_logger = logging.getLogger() - formatter = logging.Formatter("%(asctime)s %(levelname)-5.5s %(message)s") - stdout_handler = logging.StreamHandler(sys.stdout) - stdout_handler.setFormatter(formatter) - stdout_handler.setLevel(logging.DEBUG) - root_logger.addHandler(stdout_handler) - root_logger.setLevel(logging.DEBUG) - - -class ScriptRC(object): - """Enum for script return codes""" - SUCCESS = 0 - FAILURE = 1 - EXCEPTION = 2 - - -class ScriptException(Exception): - pass - - -def main(): - # Get the options from the user. - options = get_options() - - setup_logging() - - # Run main script. - try: - rc = read_corpus(options) - except Exception as e: - log.exception(e) - rc = ScriptRC.EXCEPTION - - log.info("Returning %d", rc) - return rc - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/tests/fuzz/standalone_fuzz_target_runner.cc b/tests/fuzz/standalone_fuzz_target_runner.cc deleted file mode 100644 index 9b2316ed6..000000000 --- a/tests/fuzz/standalone_fuzz_target_runner.cc +++ /dev/null @@ -1,89 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017, Max Dymond, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include -#include -#include - -#include "testinput.h" - -/** - * Main procedure for standalone fuzzing engine. - * - * Reads filenames from the argument array. For each filename, read the file - * into memory and then call the fuzzing interface with the data. - */ -int main(int argc, char **argv) -{ - int ii; - FILE *infile; - uint8_t *buffer = NULL; - size_t buffer_len; - - for(ii = 1; ii < argc; ii++) { - /* Try and open the file. */ - infile = fopen(argv[ii], "rb"); - if(infile) { - printf("[%s] Open succeeded! \n", argv[ii]); - - /* Get the length of the file. */ - fseek(infile, 0L, SEEK_END); - buffer_len = ftell(infile); - - /* Reset the file indicator to the beginning of the file. */ - fseek(infile, 0L, SEEK_SET); - - /* Allocate a buffer for the file contents. */ - buffer = (uint8_t *)calloc(buffer_len, sizeof(uint8_t)); - if(buffer) { - /* Read all the text from the file into the buffer. */ - fread(buffer, sizeof(uint8_t), buffer_len, infile); - printf("[%s] Read %zu bytes, calling fuzzer\n", argv[ii], buffer_len); - - /* Call the fuzzer with the data. */ - LLVMFuzzerTestOneInput(buffer, buffer_len); - - printf("[%s] Fuzzing complete\n", argv[ii]); - - /* Free the buffer as it's no longer needed. */ - free(buffer); - buffer = NULL; - } - else - { - fprintf(stderr, - "[%s] Failed to allocate %zu bytes \n", - argv[ii], - buffer_len); - } - - /* Close the file as it's no longer needed. */ - fclose(infile); - infile = NULL; - } - else - { - /* Failed to open the file. Maybe wrong name or wrong permissions? */ - fprintf(stderr, "[%s] Open failed. \n", argv[ii]); - } - } -} diff --git a/tests/fuzz/testinput.h b/tests/fuzz/testinput.h deleted file mode 100644 index 0746cba73..000000000 --- a/tests/fuzz/testinput.h +++ /dev/null @@ -1,23 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017, Max Dymond, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); \ No newline at end of file -- cgit v1.2.3