From 57001ce3bb97455d799f0a2180b7bf1287ffd71a Mon Sep 17 00:00:00 2001 From: Max Dymond Date: Fri, 1 Sep 2017 21:48:41 +0100 Subject: ossfuzz: Move to C++ for curl_fuzzer. Automake gets confused if you want to use C++ static libraries with C code - basically we need to involve the clang++ linker. The easiest way of achieving this is to rename the C code as C++ code. This gets us a bit further along the path and ought to be compatible with Google's version of clang. --- .gitignore | 1 + .travis.yml | 5 + configure.ac | 1 + tests/fuzz/Makefile.am | 12 +- tests/fuzz/Makefile.inc | 10 +- tests/fuzz/README | 2 + tests/fuzz/curl_fuzzer.c | 340 --------------------------- tests/fuzz/curl_fuzzer.cc | 343 ++++++++++++++++++++++++++++ tests/fuzz/curl_fuzzer.h | 2 +- tests/fuzz/standalone_fuzz_target_runner.c | 89 -------- tests/fuzz/standalone_fuzz_target_runner.cc | 89 ++++++++ tests/fuzz/standalone_fuzz_target_runner.h | 23 -- tests/fuzz/testinput.h | 23 ++ 13 files changed, 476 insertions(+), 464 deletions(-) delete mode 100644 tests/fuzz/curl_fuzzer.c create mode 100644 tests/fuzz/curl_fuzzer.cc delete mode 100644 tests/fuzz/standalone_fuzz_target_runner.c create mode 100644 tests/fuzz/standalone_fuzz_target_runner.cc delete mode 100644 tests/fuzz/standalone_fuzz_target_runner.h create mode 100644 tests/fuzz/testinput.h diff --git a/.gitignore b/.gitignore index 38c414e0f..e567b38c4 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ test-driver scripts/_curl curl_fuzzer curl_fuzzer_seed_corpus.zip +libstandaloneengine.a diff --git a/.travis.yml b/.travis.yml index cd8876e86..ca5ea8eba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -145,7 +145,12 @@ script: - | if [ "$T" = "fuzzer" ]; then export CC=clang + export CXX=clang++ export CFLAGS="-fsanitize=address" + + # 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 diff --git a/configure.ac b/configure.ac index 04d92d8f4..27d560f83 100755 --- a/configure.ac +++ b/configure.ac @@ -52,6 +52,7 @@ CURL_CHECK_OPTION_RT XC_CHECK_PATH_SEPARATOR AX_CODE_COVERAGE +AC_PROG_CXX # # save the configure arguments diff --git a/tests/fuzz/Makefile.am b/tests/fuzz/Makefile.am index 270b9783e..b7968d3d4 100644 --- a/tests/fuzz/Makefile.am +++ b/tests/fuzz/Makefile.am @@ -30,12 +30,12 @@ AUTOMAKE_OPTIONS = foreign nostdinc # $(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_CFLAGS = -I$(top_srcdir)/include \ - -I$(top_builddir)/lib \ - -I$(top_srcdir)/lib \ - -I$(top_srcdir)/tests/fuzz +AM_CXXFLAGS = -I$(top_srcdir)/include \ + -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/tests/fuzz -LIBS = -lpthread -lstdc++ -lm +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. @@ -53,4 +53,4 @@ checksrc: @PERL@ $(top_srcdir)/lib/checksrc.pl $(srcdir)/*.c noinst_PROGRAMS = $(FUZZPROGS) -noinst_LIBRARIES = $(FUZZLIBS) \ No newline at end of file +noinst_LIBRARIES = $(FUZZLIBS) diff --git a/tests/fuzz/Makefile.inc b/tests/fuzz/Makefile.inc index 4d475374b..f52adb89c 100644 --- a/tests/fuzz/Makefile.inc +++ b/tests/fuzz/Makefile.inc @@ -1,15 +1,15 @@ FUZZPROGS = curl_fuzzer FUZZLIBS = libstandaloneengine.a -curl_fuzzer_SOURCES = curl_fuzzer.c -curl_fuzzer_CFLAGS = $(AM_CFLAGS) +curl_fuzzer_SOURCES = curl_fuzzer.cc +curl_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) -libstandaloneengine_a_SOURCES = standalone_fuzz_target_runner.c -libstandaloneengine_a_CFLAGS = $(AM_CFLAGS) +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/* \ No newline at end of file + ./curl_fuzzer curl_fuzz_data/* diff --git a/tests/fuzz/README b/tests/fuzz/README index cdb69fe82..8b5fcd011 100644 --- a/tests/fuzz/README +++ b/tests/fuzz/README @@ -8,7 +8,9 @@ Building the fuzz target From the CURL root directory: 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 diff --git a/tests/fuzz/curl_fuzzer.c b/tests/fuzz/curl_fuzzer.c deleted file mode 100644 index f4a4ec6f9..000000000 --- a/tests/fuzz/curl_fuzzer.c +++ /dev/null @@ -1,340 +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. - */ -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) -{ - int rc = 0; - int tlv_rc; - FUZZ_DATA fuzz; - TLV tlv; - - if(size < sizeof(TLV_RAW)) { - /* Not enough data */ - 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. */ - fuzz_parse_tlv(&fuzz, &tlv); - } - - if(tlv_rc != TLV_RC_NO_MORE_TLVS) { - /* A TLV call failed. Can't continue. */ - goto EXIT_LABEL; - } - - /* Do the CURL stuff! */ - 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)); - - /* Can enable verbose mode */ - /* FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_VERBOSE, 1L)); */ - - /* 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); - - 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; -} - -/** - * 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; - - switch(tlv->type) { - case TLV_TYPE_URL: - FCHECK(fuzz->url == NULL); - fuzz->url = fuzz_tlv_to_string(tlv); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_URL, fuzz->url)); - break; - - 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_USERNAME: - FCHECK(fuzz->username == NULL); - fuzz->username = fuzz_tlv_to_string(tlv); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_USERNAME, fuzz->username)); - break; - - case TLV_TYPE_PASSWORD: - FCHECK(fuzz->password == NULL); - fuzz->password = fuzz_tlv_to_string(tlv); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_PASSWORD, fuzz->password)); - break; - - case TLV_TYPE_POSTFIELDS: - FCHECK(fuzz->postfields == NULL); - fuzz->postfields = fuzz_tlv_to_string(tlv); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_POSTFIELDS, fuzz->postfields)); - break; - - default: - /* The fuzzer generates lots of unknown TLVs, so don't do anything if - the TLV isn't known. */ - 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 = 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.cc b/tests/fuzz/curl_fuzzer.cc new file mode 100644 index 000000000..92bedf92e --- /dev/null +++ b/tests/fuzz/curl_fuzzer.cc @@ -0,0 +1,343 @@ +/*************************************************************************** + * _ _ ____ _ + * 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. */ + fuzz_parse_tlv(&fuzz, &tlv); + } + + if(tlv_rc != TLV_RC_NO_MORE_TLVS) { + /* A TLV call failed. Can't continue. */ + goto EXIT_LABEL; + } + + /* Do the CURL stuff! */ + 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)); + + /* Can enable verbose mode */ + /* FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_VERBOSE, 1L)); */ + + /* 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); + + 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; +} + +/** + * 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; + + switch(tlv->type) { + case TLV_TYPE_URL: + FCHECK(fuzz->url == NULL); + fuzz->url = fuzz_tlv_to_string(tlv); + FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_URL, fuzz->url)); + break; + + 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_USERNAME: + FCHECK(fuzz->username == NULL); + fuzz->username = fuzz_tlv_to_string(tlv); + FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_USERNAME, fuzz->username)); + break; + + case TLV_TYPE_PASSWORD: + FCHECK(fuzz->password == NULL); + fuzz->password = fuzz_tlv_to_string(tlv); + FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_PASSWORD, fuzz->password)); + break; + + case TLV_TYPE_POSTFIELDS: + FCHECK(fuzz->postfields == NULL); + fuzz->postfields = fuzz_tlv_to_string(tlv); + FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_POSTFIELDS, fuzz->postfields)); + break; + + default: + /* The fuzzer generates lots of unknown TLVs, so don't do anything if + the TLV isn't known. */ + 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 index a0c9d596a..634160648 100644 --- a/tests/fuzz/curl_fuzzer.h +++ b/tests/fuzz/curl_fuzzer.h @@ -21,6 +21,7 @@ ***************************************************************************/ #include +#include /** * TLV types. @@ -107,7 +108,6 @@ typedef struct fuzz_data } FUZZ_DATA; /* Function prototypes */ -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); uint32_t to_u32(uint8_t b[4]); uint16_t to_u16(uint8_t b[2]); int fuzz_initialize_fuzz_data(FUZZ_DATA *fuzz, diff --git a/tests/fuzz/standalone_fuzz_target_runner.c b/tests/fuzz/standalone_fuzz_target_runner.c deleted file mode 100644 index c131a2192..000000000 --- a/tests/fuzz/standalone_fuzz_target_runner.c +++ /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 "standalone_fuzz_target_runner.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/standalone_fuzz_target_runner.cc b/tests/fuzz/standalone_fuzz_target_runner.cc new file mode 100644 index 000000000..9b2316ed6 --- /dev/null +++ b/tests/fuzz/standalone_fuzz_target_runner.cc @@ -0,0 +1,89 @@ +/*************************************************************************** + * _ _ ____ _ + * 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/standalone_fuzz_target_runner.h b/tests/fuzz/standalone_fuzz_target_runner.h deleted file mode 100644 index 37302618b..000000000 --- a/tests/fuzz/standalone_fuzz_target_runner.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. - * - ***************************************************************************/ - -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); \ No newline at end of file diff --git a/tests/fuzz/testinput.h b/tests/fuzz/testinput.h new file mode 100644 index 000000000..0746cba73 --- /dev/null +++ b/tests/fuzz/testinput.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * _ _ ____ _ + * 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