Fuzzing module for various string operations, currently focusing on

encoding and decoding.

There are bunches of places where we don't want to invest in a full
fuzzer, but we would like to make sure that some string operation
can handle all its possible inputs.  This fuzzer uses the first byte
of its input to decide what to do with the rest of the input.  Right
now, all the possibilities are decoding a string, and seeing whether
it is decodeable.  If it is, we try to re-encode it and do the whole
thing again, to make sure we get the same result.

This turned up a lot of bugs in the key-value parser, and I think it
will help in other cases too.

Closes ticket 28808.
This commit is contained in:
Nick Mathewson 2018-12-10 10:00:26 -05:00
parent f0a8664677
commit 845e8dbe59
3 changed files with 277 additions and 0 deletions

View File

@ -13,6 +13,7 @@ FUZZERS = """
iptsv2 iptsv2
microdesc microdesc
socks socks
strops
vrs vrs
""" """

247
src/test/fuzz/fuzz_strops.c Normal file
View File

@ -0,0 +1,247 @@
/* Copyright (c) 2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file fuzz_strops.c
* \brief Fuzzers for various string encoding/decoding operations
**/
#include "orconfig.h"
#include "lib/cc/torint.h"
#include "lib/ctime/di_ops.h"
#include "lib/encoding/binascii.h"
#include "lib/encoding/cstring.h"
#include "lib/encoding/kvline.h"
#include "lib/encoding/confline.h"
#include "lib/malloc/malloc.h"
#include "lib/log/escape.h"
#include "lib/log/util_bug.h"
#include "lib/intmath/muldiv.h"
#include "test/fuzz/fuzzing.h"
#include <stdio.h>
#include <string.h>
int
fuzz_init(void)
{
return 0;
}
int
fuzz_cleanup(void)
{
return 0;
}
typedef struct chunk_t {
uint8_t *buf;
size_t len;
} chunk_t;
#define chunk_free(ch) \
FREE_AND_NULL(chunk_t, chunk_free_, (ch))
static chunk_t *
chunk_new(size_t len)
{
chunk_t *ch = tor_malloc(sizeof(chunk_t));
ch->buf = tor_malloc(len);
ch->len = len;
return ch;
}
static void
chunk_free_(chunk_t *ch)
{
if (!ch)
return;
tor_free(ch->buf);
tor_free(ch);
}
static bool
chunk_eq(const chunk_t *a, const chunk_t *b)
{
return a->len == b->len && fast_memeq(a->buf, b->buf, a->len);
}
static chunk_t *
b16_dec(const chunk_t *inp)
{
chunk_t *ch = chunk_new(CEIL_DIV(inp->len, 2));
int r = base16_decode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len);
if (r >= 0) {
ch->len = r;
} else {
chunk_free(ch);
}
return ch;
}
static chunk_t *
b16_enc(const chunk_t *inp)
{
chunk_t *ch = chunk_new(inp->len * 2 + 1);
base16_encode((char *)ch->buf, ch->len, (char*)inp->buf, inp->len);
return ch;
}
#if 0
static chunk_t *
b32_dec(const chunk_t *inp)
{
chunk_t *ch = chunk_new(inp->len);//XXXX
int r = base32_decode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len);
if (r >= 0) {
ch->len = r; // XXXX we need some way to get the actual length of
// XXXX the output here.
} else {
chunk_free(ch);
}
return ch;
}
static chunk_t *
b32_enc(const chunk_t *inp)
{
chunk_t *ch = chunk_new(base32_encoded_size(inp->len));
base32_encode((char *)ch->buf, ch->len, (char*)inp->buf, inp->len);
ch->len = strlen((char *) ch->buf);
return ch;
}
#endif
static chunk_t *
b64_dec(const chunk_t *inp)
{
chunk_t *ch = chunk_new(inp->len);//XXXX This could be shorter.
int r = base64_decode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len);
if (r >= 0) {
ch->len = r;
} else {
chunk_free(ch);
}
return ch;
}
static chunk_t *
b64_enc(const chunk_t *inp)
{
chunk_t *ch = chunk_new(BASE64_BUFSIZE(inp->len));
base64_encode((char *)ch->buf, ch->len, (char *)inp->buf, inp->len, 0);
ch->len = strlen((char *) ch->buf);
return ch;
}
static chunk_t *
c_dec(const chunk_t *inp)
{
char *s = tor_memdup_nulterm(inp->buf, inp->len);
chunk_t *ch = tor_malloc(sizeof(chunk_t));
char *r = NULL;
(void) unescape_string(s, &r, &ch->len);
tor_free(s);
ch->buf = (uint8_t*) r;
if (!ch->buf) {
tor_free(ch);
}
return ch;
}
static chunk_t *
c_enc(const chunk_t *inp)
{
char *s = tor_memdup_nulterm(inp->buf, inp->len);
chunk_t *ch = tor_malloc(sizeof(chunk_t));
ch->buf = (uint8_t*)esc_for_log(s);
tor_free(s);
ch->len = strlen((char*)ch->buf);
return ch;
}
static int kv_flags = 0;
static config_line_t *
kv_dec(const chunk_t *inp)
{
char *s = tor_memdup_nulterm(inp->buf, inp->len);
config_line_t *res = kvline_parse(s, kv_flags);
tor_free(s);
return res;
}
static chunk_t *
kv_enc(const config_line_t *inp)
{
char *s = kvline_encode(inp, kv_flags);
if (!s)
return NULL;
chunk_t *res = tor_malloc(sizeof(chunk_t));
res->buf = (uint8_t*)s;
res->len = strlen(s);
return res;
}
/* Given an encoder function, a decoder function, and a function to free
* the decoded object, check whether any string that successfully decoded
* will then survive an encode-decode-encode round-trip unchanged.
*/
#define ENCODE_ROUNDTRIP(E,D,FREE) \
STMT_BEGIN { \
bool err = false; \
a = D(&inp); \
if (!a) \
return 0; \
b = E(a); \
tor_assert(b); \
c = D(b); \
tor_assert(c); \
d = E(c); \
tor_assert(d); \
if (!chunk_eq(b,d)) { \
printf("Unequal chunks: %s\n", \
hex_str((char*)b->buf, b->len)); \
printf(" vs %s\n", \
hex_str((char*)d->buf, d->len)); \
err = true; \
} \
FREE(a); \
chunk_free(b); \
FREE(c); \
chunk_free(d); \
tor_assert(!err); \
} STMT_END
int
fuzz_main(const uint8_t *stdin_buf, size_t data_size)
{
if (!data_size)
return 0;
chunk_t inp = { (uint8_t*)stdin_buf, data_size };
chunk_t *b=NULL,*d=NULL;
void *a=NULL,*c=NULL;
switch (stdin_buf[0]) {
case 0:
ENCODE_ROUNDTRIP(b16_enc, b16_dec, chunk_free_);
break;
case 1:
/*
XXXX see notes above about our base-32 functions.
ENCODE_ROUNDTRIP(b32_enc, b32_dec, chunk_free_);
*/
break;
case 2:
ENCODE_ROUNDTRIP(b64_enc, b64_dec, chunk_free_);
break;
case 3:
ENCODE_ROUNDTRIP(c_enc, c_dec, chunk_free_);
break;
case 5:
kv_flags = KV_QUOTED|KV_OMIT_KEYS;
ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
break;
case 6:
kv_flags = 0;
ENCODE_ROUNDTRIP(kv_enc, kv_dec, config_free_lines_);
break;
}
return 0;
}

View File

@ -152,6 +152,16 @@ src_test_fuzz_fuzz_socks_LDFLAGS = $(FUZZING_LDFLAG)
src_test_fuzz_fuzz_socks_LDADD = $(FUZZING_LIBS) src_test_fuzz_fuzz_socks_LDADD = $(FUZZING_LIBS)
endif endif
if UNITTESTS_ENABLED
src_test_fuzz_fuzz_strops_SOURCES = \
src/test/fuzz/fuzzing_common.c \
src/test/fuzz/fuzz_strops.c
src_test_fuzz_fuzz_strops_CPPFLAGS = $(FUZZING_CPPFLAGS)
src_test_fuzz_fuzz_strops_CFLAGS = $(FUZZING_CFLAGS)
src_test_fuzz_fuzz_strops_LDFLAGS = $(FUZZING_LDFLAG)
src_test_fuzz_fuzz_strops_LDADD = $(FUZZING_LIBS)
endif
if UNITTESTS_ENABLED if UNITTESTS_ENABLED
src_test_fuzz_fuzz_vrs_SOURCES = \ src_test_fuzz_fuzz_vrs_SOURCES = \
src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzzing_common.c \
@ -176,6 +186,7 @@ FUZZERS = \
src/test/fuzz/fuzz-iptsv2 \ src/test/fuzz/fuzz-iptsv2 \
src/test/fuzz/fuzz-microdesc \ src/test/fuzz/fuzz-microdesc \
src/test/fuzz/fuzz-socks \ src/test/fuzz/fuzz-socks \
src/test/fuzz/fuzz-strops \
src/test/fuzz/fuzz-vrs src/test/fuzz/fuzz-vrs
endif endif
@ -290,6 +301,15 @@ src_test_fuzz_lf_fuzz_socks_LDFLAGS = $(LIBFUZZER_LDFLAG)
src_test_fuzz_lf_fuzz_socks_LDADD = $(LIBFUZZER_LIBS) src_test_fuzz_lf_fuzz_socks_LDADD = $(LIBFUZZER_LIBS)
endif endif
if UNITTESTS_ENABLED
src_test_fuzz_lf_fuzz_strops_SOURCES = \
$(src_test_fuzz_fuzz_strops_SOURCES)
src_test_fuzz_lf_fuzz_strops_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
src_test_fuzz_lf_fuzz_strops_CFLAGS = $(LIBFUZZER_CFLAGS)
src_test_fuzz_lf_fuzz_strops_LDFLAGS = $(LIBFUZZER_LDFLAG)
src_test_fuzz_lf_fuzz_strops_LDADD = $(LIBFUZZER_LIBS)
endif
if UNITTESTS_ENABLED if UNITTESTS_ENABLED
src_test_fuzz_lf_fuzz_vrs_SOURCES = \ src_test_fuzz_lf_fuzz_vrs_SOURCES = \
$(src_test_fuzz_fuzz_vrs_SOURCES) $(src_test_fuzz_fuzz_vrs_SOURCES)
@ -312,6 +332,7 @@ LIBFUZZER_FUZZERS = \
src/test/fuzz/lf-fuzz-iptsv2 \ src/test/fuzz/lf-fuzz-iptsv2 \
src/test/fuzz/lf-fuzz-microdesc \ src/test/fuzz/lf-fuzz-microdesc \
src/test/fuzz/lf-fuzz-socks \ src/test/fuzz/lf-fuzz-socks \
src/test/fuzz/lf-fuzz-strops \
src/test/fuzz/lf-fuzz-vrs src/test/fuzz/lf-fuzz-vrs
else else
@ -405,6 +426,13 @@ src_test_fuzz_liboss_fuzz_socks_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
src_test_fuzz_liboss_fuzz_socks_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) src_test_fuzz_liboss_fuzz_socks_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
endif endif
if UNITTESTS_ENABLED
src_test_fuzz_liboss_fuzz_strops_a_SOURCES = \
$(src_test_fuzz_fuzz_strops_SOURCES)
src_test_fuzz_liboss_fuzz_strops_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
src_test_fuzz_liboss_fuzz_strops_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
endif
if UNITTESTS_ENABLED if UNITTESTS_ENABLED
src_test_fuzz_liboss_fuzz_vrs_a_SOURCES = \ src_test_fuzz_liboss_fuzz_vrs_a_SOURCES = \
$(src_test_fuzz_fuzz_vrs_SOURCES) $(src_test_fuzz_fuzz_vrs_SOURCES)
@ -425,6 +453,7 @@ OSS_FUZZ_FUZZERS = \
src/test/fuzz/liboss-fuzz-iptsv2.a \ src/test/fuzz/liboss-fuzz-iptsv2.a \
src/test/fuzz/liboss-fuzz-microdesc.a \ src/test/fuzz/liboss-fuzz-microdesc.a \
src/test/fuzz/liboss-fuzz-socks.a \ src/test/fuzz/liboss-fuzz-socks.a \
src/test/fuzz/liboss-fuzz-strops.a \
src/test/fuzz/liboss-fuzz-vrs.a src/test/fuzz/liboss-fuzz-vrs.a
else else