ossfuzz: add some more handled CURL options

Add support for HEADER, COOKIE, RANGE, CUSTOMREQUEST, MAIL_RECIPIENT,
MAIL_FROM and uploading data.
This commit is contained in:
Max Dymond 2017-09-02 22:40:01 +01:00 committed by Daniel Stenberg
parent 1ae2704d6e
commit 261da2a668
11 changed files with 160 additions and 28 deletions

View File

@ -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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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. */

View File

@ -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

View File

@ -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()