0
0
mirror of https://gitlab.torproject.org/tpo/core/tor.git synced 2024-09-20 12:22:14 +02:00

Merge branch 'ticket28755_v2_squashed'

This commit is contained in:
Nick Mathewson 2018-12-12 11:07:38 -05:00
commit 4afc6b172a
8 changed files with 623 additions and 2 deletions

View File

@ -13,6 +13,7 @@ FUZZERS = """
iptsv2
microdesc
socks
strops
vrs
"""
@ -23,12 +24,12 @@ FUZZING_CPPFLAGS = \
FUZZING_CFLAGS = \
$(AM_CFLAGS) $(TEST_CFLAGS)
FUZZING_LDFLAG = \
@TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
@TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@
FUZZING_LIBS = \
$(TOR_INTERNAL_TESTING_LIBS) \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
@TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@ \
@TOR_LZMA_LIBS@ \

View File

@ -1,5 +1,6 @@
orconfig.h
lib/cc/*.h
lib/container/*.h
lib/ctime/*.h
lib/encoding/*.h
lib/intmath/*.h

View File

@ -9,6 +9,7 @@ src_lib_libtor_encoding_a_SOURCES = \
src/lib/encoding/confline.c \
src/lib/encoding/cstring.c \
src/lib/encoding/keyval.c \
src/lib/encoding/kvline.c \
src/lib/encoding/pem.c \
src/lib/encoding/time_fmt.c
@ -22,5 +23,6 @@ noinst_HEADERS += \
src/lib/encoding/confline.h \
src/lib/encoding/cstring.h \
src/lib/encoding/keyval.h \
src/lib/encoding/kvline.h \
src/lib/encoding/pem.h \
src/lib/encoding/time_fmt.h

239
src/lib/encoding/kvline.c Normal file
View File

@ -0,0 +1,239 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file kvline.c
*
* \brief Manipulating lines of key-value pairs.
**/
#include "orconfig.h"
#include "lib/container/smartlist.h"
#include "lib/encoding/confline.h"
#include "lib/encoding/cstring.h"
#include "lib/encoding/kvline.h"
#include "lib/malloc/malloc.h"
#include "lib/string/compat_ctype.h"
#include "lib/string/printf.h"
#include "lib/string/util_string.h"
#include "lib/log/escape.h"
#include "lib/log/util_bug.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
/** Return true iff we need to quote and escape the string <b>s</b> to encode
* it. */
static bool
needs_escape(const char *s, bool as_keyless_val)
{
if (as_keyless_val && *s == 0)
return true;
for (; *s; ++s) {
if (*s >= 127 || TOR_ISSPACE(*s) || ! TOR_ISPRINT(*s) ||
*s == '\'' || *s == '\"') {
return true;
}
}
return false;
}
/**
* Return true iff the key in <b>line</b> is not set.
**/
static bool
line_has_no_key(const config_line_t *line)
{
return line->key == NULL || strlen(line->key) == 0;
}
/**
* Return true iff the all the lines in <b>line</b> can be encoded
* using <b>flags</b>.
**/
static bool
kvline_can_encode_lines(const config_line_t *line, unsigned flags)
{
for ( ; line; line = line->next) {
const bool keyless = line_has_no_key(line);
if (keyless) {
if (! (flags & KV_OMIT_KEYS)) {
/* If KV_OMIT_KEYS is not set, we can't encode a line with no key. */
return false;
}
if (strchr(line->value, '=') && !( flags & KV_QUOTED)) {
/* We can't have a keyless value with = without quoting it. */
return false;
}
}
if (needs_escape(line->value, keyless) && ! (flags & KV_QUOTED)) {
/* If KV_QUOTED is false, we can't encode a value that needs quotes. */
return false;
}
if (line->key && strlen(line->key) &&
(needs_escape(line->key, false) || strchr(line->key, '='))) {
/* We can't handle keys that need quoting. */
return false;
}
}
return true;
}
/**
* Encode a linked list of lines in <b>line</b> as a series of 'Key=Value'
* pairs, using the provided <b>flags</b> to encode it. Return a newly
* allocated string on success, or NULL on failure.
*
* If KV_QUOTED is set in <b>flags</b>, then all values that contain
* spaces or unusual characters are escaped and quoted. Otherwise, such
* values are not allowed.
*
* If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are
* allowed, and are encoded as 'Value'. Otherwise, such pairs are not
* allowed.
*/
char *
kvline_encode(const config_line_t *line,
unsigned flags)
{
if (!kvline_can_encode_lines(line, flags))
return NULL;
smartlist_t *elements = smartlist_new();
for (; line; line = line->next) {
const char *k = "";
const char *eq = "=";
const char *v = "";
const bool keyless = line_has_no_key(line);
bool esc = needs_escape(line->value, keyless);
char *tmp = NULL;
if (! keyless) {
k = line->key;
} else {
eq = "";
if (strchr(line->value, '=')) {
esc = true;
}
}
if (esc) {
tmp = esc_for_log(line->value);
v = tmp;
} else {
v = line->value;
}
smartlist_add_asprintf(elements, "%s%s%s", k, eq, v);
tor_free(tmp);
}
char *result = smartlist_join_strings(elements, " ", 0, NULL);
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
return result;
}
/**
* Decode a <b>line</b> containing a series of space-separated 'Key=Value'
* pairs, using the provided <b>flags</b> to decode it. Return a newly
* allocated list of pairs on success, or NULL on failure.
*
* If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are
* allowed. Otherwise, such values are not allowed.
*
* If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are
* allowed. Otherwise, such values are not allowed.
*/
config_line_t *
kvline_parse(const char *line, unsigned flags)
{
const char *cp = line, *cplast = NULL;
bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
bool quoted = (flags & KV_QUOTED) != 0;
config_line_t *result = NULL;
config_line_t **next_line = &result;
char *key = NULL;
char *val = NULL;
while (*cp) {
key = val = NULL;
{
size_t idx = strspn(cp, " \t\r\v\n");
cp += idx;
}
if (BUG(cp == cplast)) {
/* If we didn't parse anything, this code is broken. */
goto err; // LCOV_EXCL_LINE
}
cplast = cp;
if (! *cp)
break; /* End of string; we're done. */
/* Possible formats are K=V, K="V", V, and "V", depending on flags. */
/* Find the key. */
if (*cp != '\"') {
size_t idx = strcspn(cp, " \t\r\v\n=");
if (cp[idx] == '=') {
key = tor_memdup_nulterm(cp, idx);
cp += idx + 1;
} else {
if (!omit_keys)
goto err;
}
}
if (*cp == '\"') {
/* The type is "V". */
if (!quoted)
goto err;
size_t len=0;
cp = unescape_string(cp, &val, &len);
if (cp == NULL || len != strlen(val)) {
// The string contains a NUL or is badly coded.
goto err;
}
} else {
size_t idx = strcspn(cp, " \t\r\v\n");
val = tor_memdup_nulterm(cp, idx);
cp += idx;
}
if (key && strlen(key) == 0) {
/* We don't allow empty keys. */
goto err;
}
*next_line = tor_malloc_zero(sizeof(config_line_t));
(*next_line)->key = key ? key : tor_strdup("");
(*next_line)->value = val;
next_line = &(*next_line)->next;
key = val = NULL;
}
if (!kvline_can_encode_lines(result, flags)) {
goto err;
}
return result;
err:
tor_free(key);
tor_free(val);
config_free_lines(result);
return NULL;
}

24
src/lib/encoding/kvline.h Normal file
View File

@ -0,0 +1,24 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file kvline.h
*
* \brief Header for kvline.c
**/
#ifndef TOR_KVLINE_H
#define TOR_KVLINE_H
struct config_line_t;
#define KV_QUOTED (1u<<0)
#define KV_OMIT_KEYS (1u<<1)
struct config_line_t *kvline_parse(const char *line, unsigned flags);
char *kvline_encode(const struct config_line_t *line, unsigned flags);
#endif /* !defined(TOR_KVLINE_H) */

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)
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
src_test_fuzz_fuzz_vrs_SOURCES = \
src/test/fuzz/fuzzing_common.c \
@ -176,6 +186,7 @@ FUZZERS = \
src/test/fuzz/fuzz-iptsv2 \
src/test/fuzz/fuzz-microdesc \
src/test/fuzz/fuzz-socks \
src/test/fuzz/fuzz-strops \
src/test/fuzz/fuzz-vrs
endif
@ -290,6 +301,15 @@ src_test_fuzz_lf_fuzz_socks_LDFLAGS = $(LIBFUZZER_LDFLAG)
src_test_fuzz_lf_fuzz_socks_LDADD = $(LIBFUZZER_LIBS)
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
src_test_fuzz_lf_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-microdesc \
src/test/fuzz/lf-fuzz-socks \
src/test/fuzz/lf-fuzz-strops \
src/test/fuzz/lf-fuzz-vrs
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)
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
src_test_fuzz_liboss_fuzz_vrs_a_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-microdesc.a \
src/test/fuzz/liboss-fuzz-socks.a \
src/test/fuzz/liboss-fuzz-strops.a \
src/test/fuzz/liboss-fuzz-vrs.a
else

View File

@ -54,6 +54,7 @@
#include "lib/meminfo/meminfo.h"
#include "lib/net/gethostname.h"
#include "lib/encoding/confline.h"
#include "lib/encoding/kvline.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@ -5813,6 +5814,82 @@ test_config_extended_fmt(void *arg)
config_free_lines(lines);
}
static void
test_config_kvline_parse(void *arg)
{
(void)arg;
config_line_t *lines = NULL;
char *enc = NULL;
lines = kvline_parse("A=B CD=EF", 0);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "A");
tt_str_op(lines->value, OP_EQ, "B");
tt_str_op(lines->next->key, OP_EQ, "CD");
tt_str_op(lines->next->value, OP_EQ, "EF");
enc = kvline_encode(lines, 0);
tt_str_op(enc, OP_EQ, "A=B CD=EF");
tor_free(enc);
enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS);
tt_str_op(enc, OP_EQ, "A=B CD=EF");
tor_free(enc);
config_free_lines(lines);
lines = kvline_parse("AB CDE=F", 0);
tt_assert(! lines);
lines = kvline_parse("AB CDE=F", KV_OMIT_KEYS);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "");
tt_str_op(lines->value, OP_EQ, "AB");
tt_str_op(lines->next->key, OP_EQ, "CDE");
tt_str_op(lines->next->value, OP_EQ, "F");
tt_assert(lines);
enc = kvline_encode(lines, 0);
tt_assert(!enc);
enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS);
tt_str_op(enc, OP_EQ, "AB CDE=F");
tor_free(enc);
config_free_lines(lines);
lines = kvline_parse("AB=C CDE=\"F G\"", 0);
tt_assert(!lines);
lines = kvline_parse("AB=C CDE=\"F G\" \"GHI\" ", KV_QUOTED|KV_OMIT_KEYS);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "AB");
tt_str_op(lines->value, OP_EQ, "C");
tt_str_op(lines->next->key, OP_EQ, "CDE");
tt_str_op(lines->next->value, OP_EQ, "F G");
tt_str_op(lines->next->next->key, OP_EQ, "");
tt_str_op(lines->next->next->value, OP_EQ, "GHI");
enc = kvline_encode(lines, 0);
tt_assert(!enc);
enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS);
tt_str_op(enc, OP_EQ, "AB=C CDE=\"F G\" GHI");
tor_free(enc);
config_free_lines(lines);
lines = kvline_parse("A\"B=C CDE=\"F\" \"GHI\" ", KV_QUOTED|KV_OMIT_KEYS);
tt_assert(! lines);
lines = kvline_parse("AB=", KV_QUOTED);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "AB");
tt_str_op(lines->value, OP_EQ, "");
config_free_lines(lines);
lines = kvline_parse("AB=", 0);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "AB");
tt_str_op(lines->value, OP_EQ, "");
done:
config_free_lines(lines);
tor_free(enc);
}
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
@ -5864,5 +5941,6 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(include_opened_file_list, 0),
CONFIG_TEST(compute_max_mem_in_queues, 0),
CONFIG_TEST(extended_fmt, 0),
CONFIG_TEST(kvline_parse, 0),
END_OF_TESTCASES
};