From 261da2a6685c0185283dbf72ce543e9fd81e9bd8 Mon Sep 17 00:00:00 2001 From: Max Dymond Date: Sat, 2 Sep 2017 22:40:01 +0100 Subject: ossfuzz: add some more handled CURL options Add support for HEADER, COOKIE, RANGE, CUSTOMREQUEST, MAIL_RECIPIENT, MAIL_FROM and uploading data. --- tests/fuzz/Makefile.am | 2 +- tests/fuzz/curl_fuzz_data/test10 | Bin 0 -> 226 bytes tests/fuzz/curl_fuzz_data/test12 | Bin 0 -> 464 bytes tests/fuzz/curl_fuzz_data/test13 | Bin 0 -> 179 bytes tests/fuzz/curl_fuzz_data/test4 | Bin 0 -> 336 bytes tests/fuzz/curl_fuzz_data/test5 | Bin 0 -> 185 bytes tests/fuzz/curl_fuzz_data/test6 | Bin 0 -> 223 bytes tests/fuzz/curl_fuzz_data/test900 | Bin 0 -> 129 bytes tests/fuzz/curl_fuzzer.cc | 106 +++++++++++++++++++++++++++++++------- tests/fuzz/curl_fuzzer.h | 40 +++++++++++--- tests/fuzz/generate_corpus.py | 40 +++++++++++++- 11 files changed, 160 insertions(+), 28 deletions(-) create mode 100644 tests/fuzz/curl_fuzz_data/test10 create mode 100644 tests/fuzz/curl_fuzz_data/test12 create mode 100644 tests/fuzz/curl_fuzz_data/test13 create mode 100644 tests/fuzz/curl_fuzz_data/test4 create mode 100644 tests/fuzz/curl_fuzz_data/test5 create mode 100644 tests/fuzz/curl_fuzz_data/test6 create mode 100644 tests/fuzz/curl_fuzz_data/test900 diff --git a/tests/fuzz/Makefile.am b/tests/fuzz/Makefile.am index b7968d3d4..3bd24dd66 100644 --- a/tests/fuzz/Makefile.am +++ b/tests/fuzz/Makefile.am @@ -50,7 +50,7 @@ LDADD = $(top_builddir)/lib/libcurl.la \ include Makefile.inc checksrc: - @PERL@ $(top_srcdir)/lib/checksrc.pl $(srcdir)/*.c + @PERL@ $(top_srcdir)/lib/checksrc.pl $(srcdir)/*.cc noinst_PROGRAMS = $(FUZZPROGS) noinst_LIBRARIES = $(FUZZLIBS) diff --git a/tests/fuzz/curl_fuzz_data/test10 b/tests/fuzz/curl_fuzz_data/test10 new file mode 100644 index 000000000..af1ed53ca Binary files /dev/null and b/tests/fuzz/curl_fuzz_data/test10 differ diff --git a/tests/fuzz/curl_fuzz_data/test12 b/tests/fuzz/curl_fuzz_data/test12 new file mode 100644 index 000000000..9ad91dc07 Binary files /dev/null and b/tests/fuzz/curl_fuzz_data/test12 differ diff --git a/tests/fuzz/curl_fuzz_data/test13 b/tests/fuzz/curl_fuzz_data/test13 new file mode 100644 index 000000000..448077dde Binary files /dev/null and b/tests/fuzz/curl_fuzz_data/test13 differ diff --git a/tests/fuzz/curl_fuzz_data/test4 b/tests/fuzz/curl_fuzz_data/test4 new file mode 100644 index 000000000..3fa395a29 Binary files /dev/null and b/tests/fuzz/curl_fuzz_data/test4 differ diff --git a/tests/fuzz/curl_fuzz_data/test5 b/tests/fuzz/curl_fuzz_data/test5 new file mode 100644 index 000000000..bdaac4e66 Binary files /dev/null and b/tests/fuzz/curl_fuzz_data/test5 differ diff --git a/tests/fuzz/curl_fuzz_data/test6 b/tests/fuzz/curl_fuzz_data/test6 new file mode 100644 index 000000000..98d9be216 Binary files /dev/null and b/tests/fuzz/curl_fuzz_data/test6 differ diff --git a/tests/fuzz/curl_fuzz_data/test900 b/tests/fuzz/curl_fuzz_data/test900 new file mode 100644 index 000000000..eecf0cbaf Binary files /dev/null and b/tests/fuzz/curl_fuzz_data/test900 differ diff --git a/tests/fuzz/curl_fuzzer.cc b/tests/fuzz/curl_fuzzer.cc index 92bedf92e..bbf91c222 100644 --- a/tests/fuzz/curl_fuzzer.cc +++ b/tests/fuzz/curl_fuzzer.cc @@ -63,6 +63,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } /* 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: @@ -122,8 +130,14 @@ int fuzz_initialize_fuzz_data(FUZZ_DATA *fuzz, CURLOPT_SOCKOPTFUNCTION, fuzz_sockopt_callback)); - /* Can enable verbose mode */ - /* FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_VERBOSE, 1L)); */ + /* 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)); + + /* 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; @@ -143,6 +157,20 @@ void fuzz_terminate_fuzz_data(FUZZ_DATA *fuzz) 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); @@ -216,6 +244,31 @@ static int fuzz_sockopt_callback(void *ptr, 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; +} + /** * TLV access function - gets the first TLV from a data stream. */ @@ -278,14 +331,9 @@ int fuzz_get_tlv_comn(FUZZ_DATA *fuzz, int fuzz_parse_tlv(FUZZ_DATA *fuzz, TLV *tlv) { int rc; + char *tmp; 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. */ @@ -293,24 +341,42 @@ int fuzz_parse_tlv(FUZZ_DATA *fuzz, TLV *tlv) 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)); + 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_PASSWORD: - FCHECK(fuzz->password == NULL); - fuzz->password = fuzz_tlv_to_string(tlv); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_PASSWORD, fuzz->password)); + 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_POSTFIELDS: - FCHECK(fuzz->postfields == NULL); - fuzz->postfields = fuzz_tlv_to_string(tlv); - FTRY(curl_easy_setopt(fuzz->easy, CURLOPT_POSTFIELDS, fuzz->postfields)); + 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, so don't do anything if the TLV isn't known. */ diff --git a/tests/fuzz/curl_fuzzer.h b/tests/fuzz/curl_fuzzer.h index 634160648..2dd3827d0 100644 --- a/tests/fuzz/curl_fuzzer.h +++ b/tests/fuzz/curl_fuzzer.h @@ -31,6 +31,13 @@ #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. @@ -91,19 +98,29 @@ typedef struct fuzz_data /* Parser state */ FUZZ_PARSE_STATE state; - /* Current URL. */ - char *url; - /* Response data and length */ const uint8_t *rsp1_data; size_t rsp1_data_len; - /* Username and password */ + /* Upload data and length; */ + const uint8_t *upload1_data; + size_t upload1_data_len; + + /* Singleton string fields. */ + char *url; char *username; char *password; - - /* Postfields */ 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; @@ -121,6 +138,10 @@ static curl_socket_t fuzz_open_socket(void *ptr, 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); 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); @@ -146,3 +167,10 @@ char *fuzz_tlv_to_string(TLV *tlv); 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/generate_corpus.py b/tests/fuzz/generate_corpus.py index 0bb2eda3c..04c799926 100755 --- a/tests/fuzz/generate_corpus.py +++ b/tests/fuzz/generate_corpus.py @@ -36,6 +36,27 @@ def generate_corpus(options): 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 @@ -46,6 +67,13 @@ class TLVEncoder(object): 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 def __init__(self, output): self.output = output @@ -58,7 +86,7 @@ class TLVEncoder(object): self.write_tlv(tlv_type, len(bytedata), bytedata) def maybe_write_string(self, tlv_type, wstring): - if wstring: + if wstring is not None: self.write_string(tlv_type, wstring) def write_tlv(self, tlv_type, tlv_length, tlv_data=None): @@ -84,12 +112,22 @@ def get_options(): 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() -- cgit v1.2.1