aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Dymond <cmeister2@gmail.com>2017-09-02 22:40:01 +0100
committerDaniel Stenberg <daniel@haxx.se>2017-09-08 15:00:55 +0200
commit261da2a6685c0185283dbf72ce543e9fd81e9bd8 (patch)
treed112cbe762ba1eff209b0a8eadd0fef16601e368
parent1ae2704d6edf02c218b7e30fc2e13ce52a991bea (diff)
ossfuzz: add some more handled CURL options
Add support for HEADER, COOKIE, RANGE, CUSTOMREQUEST, MAIL_RECIPIENT, MAIL_FROM and uploading data.
-rw-r--r--tests/fuzz/Makefile.am2
-rw-r--r--tests/fuzz/curl_fuzz_data/test10bin0 -> 226 bytes
-rw-r--r--tests/fuzz/curl_fuzz_data/test12bin0 -> 464 bytes
-rw-r--r--tests/fuzz/curl_fuzz_data/test13bin0 -> 179 bytes
-rw-r--r--tests/fuzz/curl_fuzz_data/test4bin0 -> 336 bytes
-rw-r--r--tests/fuzz/curl_fuzz_data/test5bin0 -> 185 bytes
-rw-r--r--tests/fuzz/curl_fuzz_data/test6bin0 -> 223 bytes
-rw-r--r--tests/fuzz/curl_fuzz_data/test900bin0 -> 129 bytes
-rw-r--r--tests/fuzz/curl_fuzzer.cc106
-rw-r--r--tests/fuzz/curl_fuzzer.h40
-rwxr-xr-xtests/fuzz/generate_corpus.py40
11 files changed, 160 insertions, 28 deletions
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
--- /dev/null
+++ b/tests/fuzz/curl_fuzz_data/test10
Binary files differ
diff --git a/tests/fuzz/curl_fuzz_data/test12 b/tests/fuzz/curl_fuzz_data/test12
new file mode 100644
index 000000000..9ad91dc07
--- /dev/null
+++ b/tests/fuzz/curl_fuzz_data/test12
Binary files differ
diff --git a/tests/fuzz/curl_fuzz_data/test13 b/tests/fuzz/curl_fuzz_data/test13
new file mode 100644
index 000000000..448077dde
--- /dev/null
+++ b/tests/fuzz/curl_fuzz_data/test13
Binary files differ
diff --git a/tests/fuzz/curl_fuzz_data/test4 b/tests/fuzz/curl_fuzz_data/test4
new file mode 100644
index 000000000..3fa395a29
--- /dev/null
+++ b/tests/fuzz/curl_fuzz_data/test4
Binary files differ
diff --git a/tests/fuzz/curl_fuzz_data/test5 b/tests/fuzz/curl_fuzz_data/test5
new file mode 100644
index 000000000..bdaac4e66
--- /dev/null
+++ b/tests/fuzz/curl_fuzz_data/test5
Binary files differ
diff --git a/tests/fuzz/curl_fuzz_data/test6 b/tests/fuzz/curl_fuzz_data/test6
new file mode 100644
index 000000000..98d9be216
--- /dev/null
+++ b/tests/fuzz/curl_fuzz_data/test6
Binary files differ
diff --git a/tests/fuzz/curl_fuzz_data/test900 b/tests/fuzz/curl_fuzz_data/test900
new file mode 100644
index 000000000..eecf0cbaf
--- /dev/null
+++ b/tests/fuzz/curl_fuzz_data/test900
Binary files 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);
@@ -217,6 +245,31 @@ static int fuzz_sockopt_callback(void *ptr,
}
/**
+ * 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.
*/
int fuzz_get_first_tlv(FUZZ_DATA *fuzz,
@@ -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()