diff options
author | Nick Mathewson <nickm@torproject.org> | 2017-01-30 08:40:46 -0500 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2017-01-30 08:40:46 -0500 |
commit | 558c04f5b119b62f5a6ef00739468b31f65fcc19 (patch) | |
tree | 6e9b638f24d252a5bf2c5a8b779bc3707196e78a /src | |
parent | 67eb6470d711b36d1b855e6423ce7bbb302af834 (diff) | |
parent | d71fc474385281453eaa93522479d32af85c94ef (diff) | |
download | tor-558c04f5b119b62f5a6ef00739468b31f65fcc19.tar.gz tor-558c04f5b119b62f5a6ef00739468b31f65fcc19.zip |
Merge branch 'combined-fuzzing-v4'
Diffstat (limited to 'src')
38 files changed, 1449 insertions, 62 deletions
diff --git a/src/common/crypto.c b/src/common/crypto.c index 062179deda..7cb3330bde 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1107,10 +1107,10 @@ crypto_pk_private_decrypt(crypto_pk_t *env, char *to, * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be * at least the length of the modulus of <b>env</b>. */ -int -crypto_pk_public_checksig(const crypto_pk_t *env, char *to, - size_t tolen, - const char *from, size_t fromlen) +MOCK_IMPL(int, +crypto_pk_public_checksig,(const crypto_pk_t *env, char *to, + size_t tolen, + const char *from, size_t fromlen)) { int r; tor_assert(env); @@ -1134,9 +1134,10 @@ crypto_pk_public_checksig(const crypto_pk_t *env, char *to, * in <b>env</b>. Return 0 if <b>sig</b> is a correct signature for * SHA1(data). Else return -1. */ -int -crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, - size_t datalen, const char *sig, size_t siglen) +MOCK_IMPL(int, +crypto_pk_public_checksig_digest,(crypto_pk_t *env, const char *data, + size_t datalen, const char *sig, + size_t siglen)) { char digest[DIGEST_LEN]; char *buf; diff --git a/src/common/crypto.h b/src/common/crypto.h index 64df52cb58..42345f80e8 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -180,10 +180,12 @@ int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int warnOnFailure); -int crypto_pk_public_checksig(const crypto_pk_t *env, char *to, size_t tolen, - const char *from, size_t fromlen); -int crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, - size_t datalen, const char *sig, size_t siglen); +MOCK_DECL(int, crypto_pk_public_checksig,(const crypto_pk_t *env, + char *to, size_t tolen, + const char *from, size_t fromlen)); +MOCK_DECL(int, crypto_pk_public_checksig_digest,(crypto_pk_t *env, + const char *data, size_t datalen, + const char *sig, size_t siglen)); int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c index 8977e7a325..525d25a3e0 100644 --- a/src/common/crypto_ed25519.c +++ b/src/common/crypto_ed25519.c @@ -15,6 +15,7 @@ * keys to and from the corresponding Curve25519 keys. */ +#define CRYPTO_ED25519_PRIVATE #include "orconfig.h" #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> @@ -34,7 +35,6 @@ #include <openssl/sha.h> static void pick_ed25519_impl(void); -static int ed25519_impl_spot_check(void); /** An Ed25519 implementation, as a set of function pointers. */ typedef struct { @@ -308,10 +308,10 @@ ed25519_sign_prefixed,(ed25519_signature_t *signature_out, * * Return 0 if the signature is valid; -1 if it isn't. */ -int -ed25519_checksig(const ed25519_signature_t *signature, - const uint8_t *msg, size_t len, - const ed25519_public_key_t *pubkey) +MOCK_IMPL(int, +ed25519_checksig,(const ed25519_signature_t *signature, + const uint8_t *msg, size_t len, + const ed25519_public_key_t *pubkey)) { return get_ed_impl()->open(signature->sig, msg, len, pubkey->pubkey) < 0 ? -1 : 0; @@ -354,10 +354,10 @@ ed25519_checksig_prefixed(const ed25519_signature_t *signature, * was valid. Otherwise return -N, where N is the number of invalid * signatures. */ -int -ed25519_checksig_batch(int *okay_out, - const ed25519_checkable_t *checkable, - int n_checkable) +MOCK_IMPL(int, +ed25519_checksig_batch,(int *okay_out, + const ed25519_checkable_t *checkable, + int n_checkable)) { int i, res; const ed25519_impl_t *impl = get_ed_impl(); @@ -642,8 +642,8 @@ ed25519_pubkey_copy(ed25519_public_key_t *dest, /** Check whether the given Ed25519 implementation seems to be working. * If so, return 0; otherwise return -1. */ -static int -ed25519_impl_spot_check(void) +MOCK_IMPL(STATIC int, +ed25519_impl_spot_check,(void)) { static const uint8_t alicesk[32] = { 0xc5,0xaa,0x8d,0xf4,0x3f,0x9f,0x83,0x7b, diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h index 56782cc12e..f4a4adad68 100644 --- a/src/common/crypto_ed25519.h +++ b/src/common/crypto_ed25519.h @@ -51,9 +51,9 @@ int ed25519_keypair_generate(ed25519_keypair_t *keypair_out, int extra_strong); int ed25519_sign(ed25519_signature_t *signature_out, const uint8_t *msg, size_t len, const ed25519_keypair_t *key); -int ed25519_checksig(const ed25519_signature_t *signature, - const uint8_t *msg, size_t len, - const ed25519_public_key_t *pubkey); +MOCK_DECL(int,ed25519_checksig,(const ed25519_signature_t *signature, + const uint8_t *msg, size_t len, + const ed25519_public_key_t *pubkey)); MOCK_DECL(int, ed25519_sign_prefixed,(ed25519_signature_t *signature_out, @@ -84,9 +84,9 @@ typedef struct { size_t len; } ed25519_checkable_t; -int ed25519_checksig_batch(int *okay_out, - const ed25519_checkable_t *checkable, - int n_checkable); +MOCK_DECL(int, ed25519_checksig_batch,(int *okay_out, + const ed25519_checkable_t *checkable, + int n_checkable)); int ed25519_keypair_from_curve25519_keypair(ed25519_keypair_t *out, int *signbit_out, @@ -132,5 +132,9 @@ void crypto_ed25519_testing_force_impl(const char *name); void crypto_ed25519_testing_restore_impl(void); #endif +#ifdef CRYPTO_ED25519_PRIVATE +MOCK_DECL(STATIC int, ed25519_impl_spot_check, (void)); +#endif + #endif diff --git a/src/include.am b/src/include.am index c468af3649..d12684e187 100644 --- a/src/include.am +++ b/src/include.am @@ -6,4 +6,4 @@ include src/test/include.am include src/tools/include.am include src/win32/include.am include src/config/include.am - +include src/test/fuzz/include.am diff --git a/src/or/buffers.c b/src/or/buffers.c index 8981fd283b..58ce5733eb 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -281,6 +281,7 @@ buf_pullup(buf_t *buf, size_t bytes) } #ifdef TOR_UNIT_TESTS +/* Return the data from the first chunk of buf in cp, and its length in sz. */ void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz) { @@ -292,6 +293,53 @@ buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz) *sz = buf->head->datalen; } } + +/* Write sz bytes from cp into a newly allocated buffer buf. + * Returns NULL when passed a NULL cp or zero sz. + * Asserts on failure: only for use in unit tests. + * buf must be freed using buf_free(). */ +buf_t * +buf_new_with_data(const char *cp, size_t sz) +{ + /* Validate arguments */ + if (!cp || sz <= 0) { + return NULL; + } + + tor_assert(sz < SSIZE_T_CEILING); + + /* Allocate a buffer */ + buf_t *buf = buf_new_with_capacity(sz); + tor_assert(buf); + assert_buf_ok(buf); + tor_assert(!buf->head); + + /* Allocate a chunk that is sz bytes long */ + buf->head = chunk_new_with_alloc_size(CHUNK_ALLOC_SIZE(sz)); + buf->tail = buf->head; + tor_assert(buf->head); + assert_buf_ok(buf); + tor_assert(buf_allocation(buf) >= sz); + + /* Copy the data and size the buffers */ + tor_assert(sz <= buf_slack(buf)); + tor_assert(sz <= CHUNK_REMAINING_CAPACITY(buf->head)); + memcpy(&buf->head->mem[0], cp, sz); + buf->datalen = sz; + buf->head->datalen = sz; + buf->head->data = &buf->head->mem[0]; + assert_buf_ok(buf); + + /* Make sure everything is large enough */ + tor_assert(buf_allocation(buf) >= sz); + tor_assert(buf_allocation(buf) >= buf_datalen(buf) + buf_slack(buf)); + /* Does the buffer implementation allocate more than the requested size? + * (for example, by rounding up). If so, these checks will fail. */ + tor_assert(buf_datalen(buf) == sz); + tor_assert(buf_slack(buf) == 0); + + return buf; +} #endif /** Remove the first <b>n</b> bytes from buf. */ diff --git a/src/or/buffers.h b/src/or/buffers.h index 52b21d5885..08d9471fb7 100644 --- a/src/or/buffers.h +++ b/src/or/buffers.h @@ -64,7 +64,10 @@ void assert_buf_ok(buf_t *buf); #ifdef BUFFERS_PRIVATE STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); STATIC void buf_pullup(buf_t *buf, size_t bytes); +#ifdef TOR_UNIT_TESTS void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz); +buf_t *buf_new_with_data(const char *cp, size_t sz); +#endif STATIC size_t preferred_chunk_size(size_t target); #define DEBUG_CHUNK_ALLOC diff --git a/src/or/directory.c b/src/or/directory.c index 43d65dcf97..4c94fe4d19 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -14,6 +14,7 @@ #include "connection.h" #include "connection_edge.h" #include "control.h" +#define DIRECTORY_PRIVATE #include "directory.h" #include "dirserv.h" #include "dirvote.h" @@ -99,7 +100,6 @@ static void directory_send_command(dir_connection_t *conn, int purpose, int direct, const char *resource, const char *payload, size_t payload_len, time_t if_modified_since); -static int directory_handle_command(dir_connection_t *conn); static int body_is_plausible(const char *body, size_t body_len, int purpose); static char *http_get_header(const char *headers, const char *which); static void http_set_address_origin(const char *headers, connection_t *conn); @@ -2894,9 +2894,9 @@ static const url_table_ent_t url_table[] = { * conn-\>outbuf. If the request is unrecognized, send a 404. * Return 0 if we handled this successfully, or -1 if we need to close * the connection. */ -STATIC int -directory_handle_command_get(dir_connection_t *conn, const char *headers, - const char *req_body, size_t req_body_len) +MOCK_IMPL(STATIC int, +directory_handle_command_get,(dir_connection_t *conn, const char *headers, + const char *req_body, size_t req_body_len)) { char *url, *url_mem, *header; time_t if_modified_since = 0; @@ -3693,9 +3693,9 @@ handle_post_hs_descriptor(const char *url, const char *body) * service descriptor. On finding one, process it and write a * response into conn-\>outbuf. If the request is unrecognized, send a * 400. Always return 0. */ -static int -directory_handle_command_post(dir_connection_t *conn, const char *headers, - const char *body, size_t body_len) +MOCK_IMPL(STATIC int, +directory_handle_command_post,(dir_connection_t *conn, const char *headers, + const char *body, size_t body_len)) { char *url = NULL; const or_options_t *options = get_options(); @@ -3827,7 +3827,7 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers, * from the inbuf, try to process it; otherwise, leave it on the * buffer. Return a 0 on success, or -1 on error. */ -static int +STATIC int directory_handle_command(dir_connection_t *conn) { char *headers=NULL, *body=NULL; diff --git a/src/or/directory.h b/src/or/directory.h index ee0a198c52..8be1f8e76e 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -148,6 +148,7 @@ int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose, struct get_handler_args_t; STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn, const struct get_handler_args_t *args); +STATIC int directory_handle_command(dir_connection_t *conn); #endif @@ -157,10 +158,14 @@ STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn, STATIC int parse_http_url(const char *headers, char **url); STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose, const char *resource); -STATIC int directory_handle_command_get(dir_connection_t *conn, - const char *headers, - const char *req_body, - size_t req_body_len); +MOCK_DECL(STATIC int, directory_handle_command_get,(dir_connection_t *conn, + const char *headers, + const char *req_body, + size_t req_body_len)); +MOCK_DECL(STATIC int, directory_handle_command_post,(dir_connection_t *conn, + const char *headers, + const char *body, + size_t body_len)); STATIC int download_status_schedule_get_delay(download_status_t *dls, const smartlist_t *schedule, int min_delay, int max_delay, diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 49ff12bd6c..508cf6c5b6 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -243,7 +243,7 @@ router_reload_consensus_networkstatus(void) } /** Free all storage held by the vote_routerstatus object <b>rs</b>. */ -STATIC void +void vote_routerstatus_free(vote_routerstatus_t *rs) { vote_microdesc_hash_t *h, *next; diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index edf2dc7b7a..66cd84c88e 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -132,8 +132,9 @@ document_signature_t *document_signature_dup(const document_signature_t *sig); void networkstatus_free_all(void); int networkstatus_get_weight_scale_param(networkstatus_t *ns); +void vote_routerstatus_free(vote_routerstatus_t *rs); + #ifdef NETWORKSTATUS_PRIVATE -STATIC void vote_routerstatus_free(vote_routerstatus_t *rs); #ifdef TOR_UNIT_TESTS STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c, const char *flavor); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index e20787123e..6259e3f5a0 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -1210,12 +1210,12 @@ make_tap_onion_key_crosscert(const crypto_pk_t *onion_key, /** Check whether an RSA-TAP cross-certification is correct. Return 0 if it * is, -1 if it isn't. */ -int -check_tap_onion_key_crosscert(const uint8_t *crosscert, - int crosscert_len, - const crypto_pk_t *onion_pkey, - const ed25519_public_key_t *master_id_pkey, - const uint8_t *rsa_id_digest) +MOCK_IMPL(int, +check_tap_onion_key_crosscert,(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest)) { uint8_t *cc = tor_malloc(crypto_pk_keysize(onion_pkey)); int cc_len = diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index 98894cdc0b..d2027f4bbe 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -57,11 +57,11 @@ uint8_t *make_tap_onion_key_crosscert(const crypto_pk_t *onion_key, const crypto_pk_t *rsa_id_key, int *len_out); -int check_tap_onion_key_crosscert(const uint8_t *crosscert, +MOCK_DECL(int, check_tap_onion_key_crosscert,(const uint8_t *crosscert, int crosscert_len, const crypto_pk_t *onion_pkey, const ed25519_public_key_t *master_id_pkey, - const uint8_t *rsa_id_digest); + const uint8_t *rsa_id_digest)); int load_ed_keys(const or_options_t *options, time_t now); int should_make_new_ed_keys(const or_options_t *options, const time_t now); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index d763a63d84..58b9a22438 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -863,8 +863,8 @@ dump_desc_populate_fifo_from_directory(const char *dirname) * type *<b>type</b> to file $DATADIR/unparseable-desc. Do not write more * than one descriptor to disk per minute. If there is already such a * file in the data directory, overwrite it. */ -STATIC void -dump_desc(const char *desc, const char *type) +MOCK_IMPL(STATIC void, +dump_desc,(const char *desc, const char *type)) { tor_assert(desc); tor_assert(type); @@ -1172,6 +1172,12 @@ tor_version_is_obsolete(const char *myversion, const char *versionlist) return ret; } +MOCK_IMPL(STATIC int, +signed_digest_equals, (const uint8_t *d1, const uint8_t *d2, size_t len)) +{ + return tor_memeq(d1, d2, len); +} + /** Check whether the object body of the token in <b>tok</b> has a good * signature for <b>digest</b> using key <b>pkey</b>. * If <b>CST_NO_CHECK_OBJTYPE</b> is set, do not check @@ -1214,7 +1220,8 @@ check_signature_token(const char *digest, } // log_debug(LD_DIR,"Signed %s hash starts %s", doctype, // hex_str(signed_digest,4)); - if (tor_memneq(digest, signed_digest, digest_len)) { + if (! signed_digest_equals((const uint8_t *)digest, + (const uint8_t *)signed_digest, digest_len)) { log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype); tor_free(signed_digest); return -1; @@ -3704,11 +3711,10 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, if (ns->type != NS_TYPE_CONSENSUS) { vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t)); if (routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens, ns, - rs, 0, 0)) + rs, 0, 0)) { smartlist_add(ns->routerstatus_list, rs); - else { - tor_free(rs->version); - tor_free(rs); + } else { + vote_routerstatus_free(rs); } } else { routerstatus_t *rs; @@ -4508,13 +4514,24 @@ router_get_hash_impl(const char *s, size_t s_len, char *digest, &start,&end)<0) return -1; + return router_compute_hash_final(digest, start, end-start, alg); +} + +/** Compute the digest of the <b>len</b>-byte directory object at + * <b>start</b>, using <b>alg</b>. Store the result in <b>digest</b>, which + * must be long enough to hold it. */ +MOCK_IMPL(STATIC int, +router_compute_hash_final,(char *digest, + const char *start, size_t len, + digest_algorithm_t alg)) +{ if (alg == DIGEST_SHA1) { - if (crypto_digest(digest, start, end-start) < 0) { + if (crypto_digest(digest, start, len) < 0) { log_warn(LD_BUG,"couldn't compute digest"); return -1; } } else { - if (crypto_digest256(digest, start, end-start, alg) < 0) { + if (crypto_digest256(digest, start, len, alg) < 0) { log_warn(LD_BUG,"couldn't compute digest"); return -1; } @@ -4700,11 +4717,13 @@ microdescs_parse_from_string(const char *s, const char *eos, if (!strcmp(t->args[0], "ed25519")) { if (md->ed25519_identity_pkey) { log_warn(LD_DIR, "Extra ed25519 key in microdesc"); + smartlist_free(id_lines); goto next; } ed25519_public_key_t k; if (ed25519_public_from_base64(&k, t->args[1])<0) { log_warn(LD_DIR, "Bogus ed25519 key in microdesc"); + smartlist_free(id_lines); goto next; } md->ed25519_identity_pkey = tor_memdup(&k, sizeof(k)); diff --git a/src/or/routerparse.h b/src/or/routerparse.h index 9a3fadca1f..8eea989e31 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -110,7 +110,6 @@ STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str, MOCK_DECL(STATIC dumped_desc_t *, dump_desc_populate_one_file, (const char *dirname, const char *f)); STATIC void dump_desc_populate_fifo_from_directory(const char *dirname); -STATIC void dump_desc(const char *desc, const char *type); STATIC void dump_desc_fifo_cleanup(void); struct memarea_t; STATIC routerstatus_t *routerstatus_parse_entry_from_string( @@ -120,6 +119,12 @@ STATIC routerstatus_t *routerstatus_parse_entry_from_string( vote_routerstatus_t *vote_rs, int consensus_method, consensus_flavor_t flav); +MOCK_DECL(STATIC void,dump_desc,(const char *desc, const char *type)); +MOCK_DECL(STATIC int, router_compute_hash_final,(char *digest, + const char *start, size_t len, + digest_algorithm_t alg)); +MOCK_DECL(STATIC int, signed_digest_equals, + (const uint8_t *d1, const uint8_t *d2, size_t len)); #endif #define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1" diff --git a/src/test/fuzz/dict/consensus b/src/test/fuzz/dict/consensus new file mode 100644 index 0000000000..3fcd9ee7ff --- /dev/null +++ b/src/test/fuzz/dict/consensus @@ -0,0 +1,52 @@ +"a" +"additional-digest" +"additional-signature" +"bandwidth-weights" +"client-versions" +"consensus-digest" +"consensus-method" +"consensus-methods" +"contact" +"dir-address" +"directory-footer" +"directory-signature" +"dir-identity-key" +"dir-key-certificate-version" +"dir-key-certification" +"dir-key-crosscert" +"dir-key-expires" +"dir-key-published" +"dir-signing-key" +"dir-source" +"fingerprint" +"fresh-until" +"id" +"known-flags" +"legacy-dir-key" +"m" +"network-status-version" +"opt" +"p" +"package" +"params" +"pr" +"published" +"r" +"recommended-client-protocols" +"recommended-relay-protocols" +"required-client-protocols" +"required-relay-protocols" +"s" +"server-versions" +"shared-rand-commit" +"shared-rand-current-value" +"shared-rand-participate" +"shared-rand-previous-value" +"signing-ed25519" +"v" +"valid-after" +"valid-until" +"vote-digest" +"vote-status" +"voting-delay" +"w" diff --git a/src/test/fuzz/dict/descriptor b/src/test/fuzz/dict/descriptor new file mode 100644 index 0000000000..110ee3e820 --- /dev/null +++ b/src/test/fuzz/dict/descriptor @@ -0,0 +1,41 @@ +"reject" +"accept" +"reject6" +"accept6" +"router" +"ipv6-policy" +"signing-key" +"onion-key" +"ntor-onion-key" +"router-signature" +"published" +"uptime" +"fingerprint" +"hibernating" +"platform" +"proto" +"contact" +"read-history" +"write-history" +"extra-info-digest" +"hidden-service-dir" +"identity-ed25519" +"master-key-ed25519" +"router-sig-ed25519" +"onion-key-crosscert" +"ntor-onion-key-crosscert" +"allow-single-hop-exits" +"family" +"caches-extra-info" +"or-address" +"opt" + "bandwidth" +"@purpose" +"tunnelled-dir-server" +"-----BEGIN" +"-----END" +"-----" +"ED25519 CERT" +"RSA PUBLIC KEY" +"CROSSCERT" +"SIGNATURE" diff --git a/src/test/fuzz/dict/extrainfo b/src/test/fuzz/dict/extrainfo new file mode 100644 index 0000000000..eba7a1e4ce --- /dev/null +++ b/src/test/fuzz/dict/extrainfo @@ -0,0 +1,32 @@ +"cell-circuits-per-decile" +"cell-processed-cells" +"cell-queued-cells" +"cell-stats-end" +"cell-time-in-queue" +"dirreq-stats-end" +"dirreq-v2-direct-dl" +"dirreq-v2-ips" +"dirreq-v2-reqs" +"dirreq-v2-resp" +"dirreq-v2-share" +"dirreq-v2-tunneled-dl" +"dirreq-v3-direct-dl" +"dirreq-v3-ips" +"dirreq-v3-reqs" +"dirreq-v3-resp" +"dirreq-v3-share" +"dirreq-v3-tunneled-dl" +"entry-ips" +"entry-stats-end" +"exit-kibibytes-read" +"exit-kibibytes-written" +"exit-stats-end" +"exit-streams-opened" +"extra-info" +"identity-ed25519" +"opt" +"published" +"read-history" +"router-sig-ed25519" +"router-signature" +"write-history" diff --git a/src/test/fuzz/dict/hsdescv2 b/src/test/fuzz/dict/hsdescv2 new file mode 100644 index 0000000000..48788301dc --- /dev/null +++ b/src/test/fuzz/dict/hsdescv2 @@ -0,0 +1,8 @@ +"introduction-points" +"permanent-key" +"protocol-versions" +"publication-time" +"rendezvous-service-descriptor" +"secret-id-part" +"signature" +"version" diff --git a/src/test/fuzz/dict/http b/src/test/fuzz/dict/http new file mode 100644 index 0000000000..1a7b61e8d4 --- /dev/null +++ b/src/test/fuzz/dict/http @@ -0,0 +1,24 @@ +# +# AFL dictionary for the Tor Directory protocol's HTTP headers +# ------------------------------------------------------------ +# +# Extracted from directory_handle_command() in the tor source code +# +# Copyright (c) 2016, The Tor Project, Inc. +# See LICENSE for licensing information +# +# Usage: +# Select the dictionaries relevant to the part of the directory protocol you +# are fuzzing, and feed them to your fuzzer (if it supports dictionaries). + +http_header_body_delimiter = "\x0d\x0a\x0d\x0a" +http_header_header_delimiter = "\x0d\x0a" +# multi-character tokens only +#http_header_value_delimiter = " " + +content_length_header = "Content-Length:" +forwarded_for_header = "Forwarded-For:" +x_forwarded_for_header = "X-Forwarded-For:" + +get_command = "GET" +post_command = "POST" diff --git a/src/test/fuzz/dict/iptsv2 b/src/test/fuzz/dict/iptsv2 new file mode 100644 index 0000000000..57791c5e3c --- /dev/null +++ b/src/test/fuzz/dict/iptsv2 @@ -0,0 +1,6 @@ +"introduction-point" +"ip-address" +"onion-port" +"onion-key" +"service-key" + diff --git a/src/test/fuzz/dict/microdesc b/src/test/fuzz/dict/microdesc new file mode 100644 index 0000000000..fdd0567b65 --- /dev/null +++ b/src/test/fuzz/dict/microdesc @@ -0,0 +1,7 @@ +"onion-key" +"ntor-onion-key" +"id" +"a" +"family" +"p" +"p6" diff --git a/src/test/fuzz/fixup_filenames.sh b/src/test/fuzz/fixup_filenames.sh new file mode 100755 index 0000000000..68efc1abc5 --- /dev/null +++ b/src/test/fuzz/fixup_filenames.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +if [ ! -d "$1" ] ; then + echo "I need a directory" + exit 1 +fi + +for fn in "$1"/* ; do + prev=`basename "$fn"` + post=`sha256sum "$fn" | sed -e 's/ .*//;'` + if [ "$prev" == "$post" ] ; then + echo "OK $prev" + else + echo "mv $prev $post" + mv "$fn" "$1/$post" + fi +done diff --git a/src/test/fuzz/fuzz_consensus.c b/src/test/fuzz/fuzz_consensus.c new file mode 100644 index 0000000000..f5d22f69ae --- /dev/null +++ b/src/test/fuzz/fuzz_consensus.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "networkstatus.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +static int +mock_signed_digest_equals__yes(const uint8_t *d1, const uint8_t *d2, + size_t len) +{ + (void) tor_memeq(d1, d2, len); + return 1; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + MOCK(signed_digest_equals, mock_signed_digest_equals__yes); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + networkstatus_t *ns; + char *str = tor_memdup_nulterm(data, sz); + const char *eos = NULL; + networkstatus_type_t tp = NS_TYPE_CONSENSUS; + if (tor_memstr(data, MIN(sz, 1024), "tus vote")) + tp = NS_TYPE_VOTE; + const char *what = (tp == NS_TYPE_CONSENSUS) ? "consensus" : "vote"; + ns = networkstatus_parse_vote_from_string(str, + &eos, + tp); + if (ns) { + log_debug(LD_GENERAL, "Parsing as %s okay", what); + networkstatus_vote_free(ns); + } else { + log_debug(LD_GENERAL, "Parsing as %s failed", what); + } + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzz_descriptor.c b/src/test/fuzz/fuzz_descriptor.c new file mode 100644 index 0000000000..d19386d77f --- /dev/null +++ b/src/test/fuzz/fuzz_descriptor.c @@ -0,0 +1,79 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "routerlist.h" +#include "routerkeys.h" +#include "fuzzing.h" + +static int +mock_check_tap_onion_key_crosscert__nocheck(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest) +{ + tor_assert(crosscert && onion_pkey && master_id_pkey && rsa_id_digest); + /* we could look at crosscert[..] */ + (void) crosscert_len; + return 0; +} + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(check_tap_onion_key_crosscert, + mock_check_tap_onion_key_crosscert__nocheck); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + routerinfo_t *ri; + const char *str = (const char*) data; + ri = router_parse_entry_from_string((const char *)str, + str+sz, + 0, 0, 0, NULL); + if (ri) { + log_debug(LD_GENERAL, "Parsing okay"); + routerinfo_free(ri); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_extrainfo.c b/src/test/fuzz/fuzz_extrainfo.c new file mode 100644 index 0000000000..6251e606d0 --- /dev/null +++ b/src/test/fuzz/fuzz_extrainfo.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "routerlist.h" +#include "routerkeys.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + extrainfo_t *ei; + const char *str = (const char*) data; + int again = 0; + ei = extrainfo_parse_entry_from_string((const char *)str, + str+sz, + 0, NULL, &again); + if (ei) { + log_debug(LD_GENERAL, "Parsing okay"); + extrainfo_free(ei); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_hsdescv2.c b/src/test/fuzz/fuzz_hsdescv2.c new file mode 100644 index 0000000000..53b7cbe2f7 --- /dev/null +++ b/src/test/fuzz/fuzz_hsdescv2.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "rendcommon.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + rend_service_descriptor_t *desc = NULL; + char desc_id[64]; + char *ipts = NULL; + size_t ipts_size, esize; + const char *next; + char *str = tor_memdup_nulterm(data, sz); + (void) rend_parse_v2_service_descriptor(&desc, desc_id, &ipts, &ipts_size, + &esize, &next, str, 1); + if (desc) { + log_debug(LD_GENERAL, "Parsing okay"); + rend_service_descriptor_free(desc); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + tor_free(ipts); + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzz_http.c b/src/test/fuzz/fuzz_http.c new file mode 100644 index 0000000000..caad0b2f02 --- /dev/null +++ b/src/test/fuzz/fuzz_http.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define BUFFERS_PRIVATE +#define DIRECTORY_PRIVATE + +#include "or.h" +#include "backtrace.h" +#include "buffers.h" +#include "config.h" +#include "connection.h" +#include "directory.h" +#include "torlog.h" + +#include "fuzzing.h" + +static void +mock_connection_write_to_buf_impl_(const char *string, size_t len, + connection_t *conn, int zlib) +{ + log_debug(LD_GENERAL, "%sResponse:\n%zu\nConnection: %p\n%s\n", + zlib ? "Compressed " : "", len, conn, string); +} + +static int +mock_directory_handle_command_get(dir_connection_t *conn, + const char *headers, + const char *body, + size_t body_len) +{ + (void)conn; + + log_debug(LD_GENERAL, "Method:\nGET\n"); + + if (headers) { + log_debug(LD_GENERAL, "Header-Length:\n%zu\n", strlen(headers)); + log_debug(LD_GENERAL, "Headers:\n%s\n", headers); + } + + log_debug(LD_GENERAL, "Body-Length:\n%zu\n", body_len); + if (body) { + log_debug(LD_GENERAL, "Body:\n%s\n", body); + } + + /* Always tell the caller we succeeded */ + return 0; +} + +static int +mock_directory_handle_command_post(dir_connection_t *conn, + const char *headers, + const char *body, + size_t body_len) +{ + (void)conn; + + log_debug(LD_GENERAL, "Method:\nPOST\n"); + + if (headers) { + log_debug(LD_GENERAL, "Header-Length:\n%zu\n", strlen(headers)); + log_debug(LD_GENERAL, "Headers:\n%s\n", headers); + } + + log_debug(LD_GENERAL, "Body-Length:\n%zu\n", body_len); + if (body) { + log_debug(LD_GENERAL, "Body:\n%s\n", body); + } + + /* Always tell the caller we succeeded */ + return 0; +} + +int +fuzz_init(void) +{ + /* Set up fake response handler */ + MOCK(connection_write_to_buf_impl_, mock_connection_write_to_buf_impl_); + /* Set up the fake handler functions */ + MOCK(directory_handle_command_get, mock_directory_handle_command_get); + MOCK(directory_handle_command_post, mock_directory_handle_command_post); + + return 0; +} + +int +fuzz_cleanup(void) +{ + UNMOCK(connection_write_to_buf_impl_); + UNMOCK(directory_handle_command_get); + UNMOCK(directory_handle_command_post); + return 0; +} + +int +fuzz_main(const uint8_t *stdin_buf, size_t data_size) +{ + dir_connection_t dir_conn; + + /* Set up the fake connection */ + memset(&dir_conn, 0, sizeof(dir_connection_t)); + dir_conn.base_.type = CONN_TYPE_DIR; + /* Apparently tor sets this before directory_handle_command() is called. */ + dir_conn.base_.address = tor_strdup("replace-this-address.example.com"); + + dir_conn.base_.inbuf = buf_new_with_data((char*)stdin_buf, data_size); + if (!dir_conn.base_.inbuf) { + log_debug(LD_GENERAL, "Zero-Length-Input\n"); + goto done; + } + + /* Parse the headers */ + int rv = directory_handle_command(&dir_conn); + + /* TODO: check the output is correctly parsed based on the input */ + + /* Report the parsed origin address */ + if (dir_conn.base_.address) { + log_debug(LD_GENERAL, "Address:\n%s\n", dir_conn.base_.address); + } + + log_debug(LD_GENERAL, "Result:\n%d\n", rv); + + done: + /* Reset. */ + tor_free(dir_conn.base_.address); + buf_free(dir_conn.base_.inbuf); + dir_conn.base_.inbuf = NULL; + + return 0; +} + diff --git a/src/test/fuzz/fuzz_iptsv2.c b/src/test/fuzz/fuzz_iptsv2.c new file mode 100644 index 0000000000..341d4880bd --- /dev/null +++ b/src/test/fuzz/fuzz_iptsv2.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "rendcommon.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + rend_service_descriptor_t *desc = + tor_malloc_zero(sizeof(rend_service_descriptor_t)); + const char *str = (const char*) data; + int r = rend_parse_introduction_points(desc, str, sz); + if (r >= 0) { + log_debug(LD_GENERAL, "Parsing okay: %d", r); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + rend_service_descriptor_free(desc); + return 0; +} + diff --git a/src/test/fuzz/fuzz_microdesc.c b/src/test/fuzz/fuzz_microdesc.c new file mode 100644 index 0000000000..bb89546191 --- /dev/null +++ b/src/test/fuzz/fuzz_microdesc.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "microdesc.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + const char *str = (const char*) data; + smartlist_t *result = microdescs_parse_from_string((const char *)str, + str+sz, + 0, SAVED_NOWHERE, NULL); + if (result) { + log_debug(LD_GENERAL, "Parsing okay: %d", smartlist_len(result)); + SMARTLIST_FOREACH(result, microdesc_t *, md, microdesc_free(md)); + smartlist_free(result); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_multi.sh b/src/test/fuzz/fuzz_multi.sh new file mode 100755 index 0000000000..b4a17ed8cb --- /dev/null +++ b/src/test/fuzz/fuzz_multi.sh @@ -0,0 +1,34 @@ +MEMLIMIT_BYTES=21990500990976 + +N_CPUS=1 +if [ $# -ge 1 ]; then + N_CPUS="$1" + shift +fi + +FILTER=echo + +for i in `seq -w "$N_CPUS"`; do + if [ "$i" -eq 1 ]; then + if [ "$N_CPUS" -eq 1 ]; then + INSTANCE="" + NUMBER="" + else + INSTANCE="-M" + NUMBER="$i" + fi + else + INSTANCE="-S" + NUMBER="$i" + fi + # use whatever remains on the command-line to prefix the fuzzer command + # you have to copy and paste and run these commands yourself + "$FILTER" "$@" \ + ../afl/afl-fuzz \ + -i src/test/fuzz/fuzz_dir_testcase \ + -o src/test/fuzz/fuzz_dir_findings \ + -x src/test/fuzz/fuzz_dir_dictionary/fuzz_dir_http_header.dct \ + -m "$MEMLIMIT_BYTES" \ + "$INSTANCE" "$NUMBER" \ + -- src/test/fuzz_dir +done diff --git a/src/test/fuzz/fuzz_vrs.c b/src/test/fuzz/fuzz_vrs.c new file mode 100644 index 0000000000..d2c0369f27 --- /dev/null +++ b/src/test/fuzz/fuzz_vrs.c @@ -0,0 +1,80 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#define NETWORKSTATUS_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "memarea.h" +#include "microdesc.h" +#include "networkstatus.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static networkstatus_t *dummy_vote = NULL; +static memarea_t *area = NULL; + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + area = memarea_new(); + dummy_vote = tor_malloc_zero(sizeof(*dummy_vote)); + dummy_vote->known_flags = smartlist_new(); + smartlist_split_string(dummy_vote->known_flags, + "Authority BadExit Exit Fast Guard HSDir " + "NoEdConsensus Running Stable V2Dir Valid", + " ", 0, 0); + return 0; +} + +int +fuzz_cleanup(void) +{ + tor_free(dummy_vote); + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + char *str = tor_memdup_nulterm(data, sz); + const char *s; + routerstatus_t *rs_ns = NULL, *rs_md = NULL, *rs_vote = NULL; + vote_routerstatus_t *vrs = tor_malloc_zero(sizeof(*vrs)); + smartlist_t *tokens = smartlist_new(); + + s = str; + rs_ns = routerstatus_parse_entry_from_string(area, &s, tokens, + NULL, NULL, 26, FLAV_NS); + tor_assert(smartlist_len(tokens) == 0); + + s = str; + rs_md = routerstatus_parse_entry_from_string(area, &s, tokens, + NULL, NULL, 26, FLAV_MICRODESC); + tor_assert(smartlist_len(tokens) == 0); + + s = str; + rs_vote = routerstatus_parse_entry_from_string(area, &s, tokens, + dummy_vote, vrs, 26, FLAV_NS); + tor_assert(smartlist_len(tokens) == 0); + + log_debug(LD_GENERAL, + "ns=%p, md=%p, vote=%p", rs_ns, rs_md, rs_vote); + + routerstatus_free(rs_md); + routerstatus_free(rs_ns); + vote_routerstatus_free(vrs); + memarea_clear(area); + smartlist_free(tokens); + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzzing.h b/src/test/fuzz/fuzzing.h new file mode 100644 index 0000000000..4295743458 --- /dev/null +++ b/src/test/fuzz/fuzzing.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#ifndef FUZZING_H +#define FUZZING_H + +int fuzz_init(void); +int fuzz_cleanup(void); +int fuzz_main(const uint8_t *data, size_t sz); + +void disable_signature_checking(void); + +#endif /* FUZZING_H */ + diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c new file mode 100644 index 0000000000..e4920d3ee7 --- /dev/null +++ b/src/test/fuzz/fuzzing_common.c @@ -0,0 +1,190 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define CRYPTO_ED25519_PRIVATE +#include "orconfig.h" +#include "or.h" +#include "backtrace.h" +#include "config.h" +#include "fuzzing.h" +#include "crypto.h" +#include "crypto_ed25519.h" + +extern const char tor_git_revision[]; +const char tor_git_revision[] = ""; + +static or_options_t *mock_options = NULL; +static const or_options_t * +mock_get_options(void) +{ + return mock_options; +} + +static int +mock_crypto_pk_public_checksig__nocheck(const crypto_pk_t *env, char *to, + size_t tolen, + const char *from, size_t fromlen) +{ + tor_assert(env && to && from); + (void)fromlen; + /* We could look at from[0..fromlen-1] ... */ + tor_assert(tolen >= crypto_pk_keysize(env)); + memset(to, 0x01, 20); + return 20; +} + +static int +mock_crypto_pk_public_checksig_digest__nocheck(crypto_pk_t *env, + const char *data, + size_t datalen, + const char *sig, + size_t siglen) +{ + tor_assert(env && data && sig); + (void)datalen; + (void)siglen; + /* We could look at data[..] and sig[..] */ + return 0; +} + +static int +mock_ed25519_checksig__nocheck(const ed25519_signature_t *signature, + const uint8_t *msg, size_t len, + const ed25519_public_key_t *pubkey) +{ + tor_assert(signature && msg && pubkey); + /* We could look at msg[0..len-1] ... */ + (void)len; + return 0; +} + +static int +mock_ed25519_checksig_batch__nocheck(int *okay_out, + const ed25519_checkable_t *checkable, + int n_checkable) +{ + tor_assert(checkable); + int i; + for (i = 0; i < n_checkable; ++i) { + /* We could look at messages and signatures XXX */ + tor_assert(checkable[i].pubkey); + tor_assert(checkable[i].msg); + if (okay_out) + okay_out[i] = 1; + } + return 0; +} + +static int +mock_ed25519_impl_spot_check__nocheck(void) +{ + return 0; +} + +void +disable_signature_checking(void) +{ + MOCK(crypto_pk_public_checksig, + mock_crypto_pk_public_checksig__nocheck); + MOCK(crypto_pk_public_checksig_digest, + mock_crypto_pk_public_checksig_digest__nocheck); + MOCK(ed25519_checksig, mock_ed25519_checksig__nocheck); + MOCK(ed25519_checksig_batch, mock_ed25519_checksig_batch__nocheck); + MOCK(ed25519_impl_spot_check, mock_ed25519_impl_spot_check__nocheck); +} + +static void +global_init(void) +{ + tor_threads_init(); + { + struct sipkey sipkey = { 1337, 7331 }; + siphash_set_global_key(&sipkey); + } + + /* Initialise logging first */ + init_logging(1); + configure_backtrace_handler(get_version()); + + /* set up the options. */ + mock_options = tor_malloc(sizeof(or_options_t)); + MOCK(get_options, mock_get_options); + + /* Make BUG() and nonfatal asserts crash */ + tor_set_failed_assertion_callback(abort); +} + +#ifdef LLVM_FUZZ +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int +LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + static int initialized = 0; + if (!initialized) { + global_init(); + if (fuzz_init() < 0) + abort(); + initialized = 1; + } + + return fuzz_main(Data, Size); +} + +#else /* Not LLVM_FUZZ, so AFL. */ + +int +main(int argc, char **argv) +{ + size_t size; + + global_init(); + + /* Disable logging by default to speed up fuzzing. */ + int loglevel = LOG_ERR; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--warn")) { + loglevel = LOG_WARN; + } else if (!strcmp(argv[i], "--notice")) { + loglevel = LOG_NOTICE; + } else if (!strcmp(argv[i], "--info")) { + loglevel = LOG_INFO; + } else if (!strcmp(argv[i], "--debug")) { + loglevel = LOG_DEBUG; + } + } + + { + log_severity_list_t s; + memset(&s, 0, sizeof(s)); + set_log_severity_config(loglevel, LOG_ERR, &s); + /* ALWAYS log bug warnings. */ + s.masks[LOG_WARN-LOG_ERR] |= LD_BUG; + add_stream_log(&s, "", fileno(stdout)); + } + + if (fuzz_init() < 0) + abort(); + +#ifdef __AFL_HAVE_MANUAL_CONTROL + /* Tell AFL to pause and fork here - ignored if not using AFL */ + __AFL_INIT(); +#endif + +#define MAX_FUZZ_SIZE (128*1024) + char *input = read_file_to_str_until_eof(0, MAX_FUZZ_SIZE, &size); + tor_assert(input); + char *raw = tor_memdup(input, size); /* Because input is nul-terminated */ + tor_free(input); + fuzz_main((const uint8_t*)raw, size); + tor_free(raw); + + if (fuzz_cleanup() < 0) + abort(); + + tor_free(mock_options); + UNMOCK(get_options); + return 0; +} + +#endif + diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am new file mode 100644 index 0000000000..c9c1747769 --- /dev/null +++ b/src/test/fuzz/include.am @@ -0,0 +1,250 @@ + +FUZZING_CPPFLAGS = \ + $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) +FUZZING_CFLAGS = \ + $(AM_CFLAGS) $(TEST_CFLAGS) +FUZZING_LDFLAG = \ + @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ +FUZZING_LIBS = \ + src/or/libtor-testing.a \ + src/common/libor-crypto-testing.a \ + $(LIBKECCAK_TINY) \ + $(LIBDONNA) \ + src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ + src/common/libor-event-testing.a \ + src/trunnel/libor-trunnel-testing.a \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ + @TOR_LIBEVENT_LIBS@ \ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_SYSTEMD_LIBS@ + +oss-fuzz-prereqs: \ + src/or/libtor-testing.a \ + src/common/libor-crypto-testing.a \ + $(LIBKECCAK_TINY) \ + $(LIBDONNA) \ + src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ + src/common/libor-event-testing.a \ + src/trunnel/libor-trunnel-testing.a + +noinst_HEADERS += \ + src/test/fuzz/fuzzing.h + +src_test_fuzz_fuzz_consensus_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_consensus.c +src_test_fuzz_fuzz_consensus_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_consensus_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_consensus_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_consensus_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_descriptor_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_descriptor.c +src_test_fuzz_fuzz_descriptor_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_descriptor_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_descriptor_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_descriptor_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_http_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_http.c +src_test_fuzz_fuzz_http_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_http_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_hsdescv2_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_hsdescv2.c +src_test_fuzz_fuzz_hsdescv2_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_hsdescv2_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_hsdescv2_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_hsdescv2_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_iptsv2_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_iptsv2.c +src_test_fuzz_fuzz_iptsv2_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_iptsv2_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_iptsv2_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_iptsv2_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_extrainfo_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_extrainfo.c +src_test_fuzz_fuzz_extrainfo_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_extrainfo_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_extrainfo_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_extrainfo_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_microdesc_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_microdesc.c +src_test_fuzz_fuzz_microdesc_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_microdesc_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_microdesc_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_microdesc_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_vrs_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_vrs.c +src_test_fuzz_fuzz_vrs_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_vrs_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_vrs_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_vrs_LDADD = $(FUZZING_LIBS) + +FUZZERS = \ + src/test/fuzz/fuzz-consensus \ + src/test/fuzz/fuzz-descriptor \ + src/test/fuzz/fuzz-extrainfo \ + src/test/fuzz/fuzz-http \ + src/test/fuzz/fuzz-hsdescv2 \ + src/test/fuzz/fuzz-iptsv2 \ + src/test/fuzz/fuzz-microdesc \ + src/test/fuzz/fuzz-vrs + + +LIBFUZZER = /home/nickm/build/libfuzz/libFuzzer.a +LIBFUZZER_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ +LIBFUZZER_CFLAGS = $(FUZZING_CFLAGS) +LIBFUZZER_LDFLAG = $(FUZZING_LDFLAG) +LIBFUZZER_LIBS = $(FUZZING_LIBS) $(LIBFUZZER) -lstdc++ + +if LIBFUZZER_ENABLED +src_test_fuzz_lf_fuzz_consensus_SOURCES = \ + $(src_test_fuzz_fuzz_consensus_SOURCES) +src_test_fuzz_lf_fuzz_consensus_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_consensus_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_consensus_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_consensus_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_descriptor_SOURCES = \ + $(src_test_fuzz_fuzz_descriptor_SOURCES) +src_test_fuzz_lf_fuzz_descriptor_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_descriptor_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_descriptor_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_descriptor_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_extrainfo_SOURCES = \ + $(src_test_fuzz_fuzz_extrainfo_SOURCES) +src_test_fuzz_lf_fuzz_extrainfo_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_extrainfo_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_extrainfo_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_extrainfo_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_http_SOURCES = \ + $(src_test_fuzz_fuzz_http_SOURCES) +src_test_fuzz_lf_fuzz_http_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_http_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_hsdescv2_SOURCES = \ + $(src_test_fuzz_fuzz_hsdescv2_SOURCES) +src_test_fuzz_lf_fuzz_hsdescv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_hsdescv2_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_hsdescv2_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_hsdescv2_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \ + $(src_test_fuzz_fuzz_iptsv2_SOURCES) +src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_iptsv2_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_iptsv2_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_iptsv2_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_microdesc_SOURCES = \ + $(src_test_fuzz_fuzz_microdesc_SOURCES) +src_test_fuzz_lf_fuzz_microdesc_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_microdesc_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_microdesc_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_microdesc_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_vrs_SOURCES = \ + $(src_test_fuzz_fuzz_vrs_SOURCES) +src_test_fuzz_lf_fuzz_vrs_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_vrs_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_vrs_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_vrs_LDADD = $(LIBFUZZER_LIBS) + +LIBFUZZER_FUZZERS = \ + src/test/fuzz/lf-fuzz-consensus \ + src/test/fuzz/lf-fuzz-descriptor \ + src/test/fuzz/lf-fuzz-extrainfo \ + src/test/fuzz/lf-fuzz-http \ + src/test/fuzz/lf-fuzz-hsdescv2 \ + src/test/fuzz/lf-fuzz-iptsv2 \ + src/test/fuzz/lf-fuzz-microdesc \ + src/test/fuzz/lf-fuzz-vrs + +else +LIBFUZZER_FUZZERS = +endif + +if OSS_FUZZ_ENABLED +LIBOSS_FUZZ_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ +LIBOSS_FUZZ_CFLAGS = $(FUZZING_CFLAGS) + +src_test_fuzz_liboss_fuzz_consensus_a_SOURCES = \ + $(src_test_fuzz_fuzz_consensus_SOURCES) +src_test_fuzz_liboss_fuzz_consensus_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_consensus_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_descriptor_a_SOURCES = \ + $(src_test_fuzz_fuzz_descriptor_SOURCES) +src_test_fuzz_liboss_fuzz_descriptor_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_descriptor_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_extrainfo_a_SOURCES = \ + $(src_test_fuzz_fuzz_extrainfo_SOURCES) +src_test_fuzz_liboss_fuzz_extrainfo_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_extrainfo_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_http_a_SOURCES = \ + $(src_test_fuzz_fuzz_http_SOURCES) +src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_hsdescv2_a_SOURCES = \ + $(src_test_fuzz_fuzz_hsdescv2_SOURCES) +src_test_fuzz_liboss_fuzz_hsdescv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_hsdescv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \ + $(src_test_fuzz_fuzz_iptsv2_SOURCES) +src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_iptsv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_microdesc_a_SOURCES = \ + $(src_test_fuzz_fuzz_microdesc_SOURCES) +src_test_fuzz_liboss_fuzz_microdesc_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_microdesc_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_vrs_a_SOURCES = \ + $(src_test_fuzz_fuzz_vrs_SOURCES) +src_test_fuzz_liboss_fuzz_vrs_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_vrs_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +OSS_FUZZ_FUZZERS = \ + src/test/fuzz/liboss-fuzz-consensus.a \ + src/test/fuzz/liboss-fuzz-descriptor.a \ + src/test/fuzz/liboss-fuzz-extrainfo.a \ + src/test/fuzz/liboss-fuzz-http.a \ + src/test/fuzz/liboss-fuzz-hsdescv2.a \ + src/test/fuzz/liboss-fuzz-iptsv2.a \ + src/test/fuzz/liboss-fuzz-microdesc.a \ + src/test/fuzz/liboss-fuzz-vrs.a +else +OSS_FUZZ_FUZZERS = +endif + +noinst_PROGRAMS += $(FUZZERS) $(LIBFUZZER_FUZZERS) +noinst_LIBRARIES += $(OSS_FUZZ_FUZZERS) +oss-fuzz-fuzzers: oss-fuzz-prereqs $(OSS_FUZZ_FUZZERS) +fuzzers: $(FUZZERS) $(LIBFUZZER_FUZZERS) + +fuzz: $(FUZZERS) + $(top_srcdir)/src/test/fuzz_static_testcases.sh diff --git a/src/test/fuzz/minimize.sh b/src/test/fuzz/minimize.sh new file mode 100755 index 0000000000..87d3dda13c --- /dev/null +++ b/src/test/fuzz/minimize.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +if [ ! -d "$1" ] ; then + echo "I need a directory" + exit 1 +fi + +which=`basename "$1"` + +mkdir "$1.out" +afl-cmin -i "$1" -o "$1.out" -m none "./src/test/fuzz/fuzz-${which}" + diff --git a/src/test/fuzz_static_testcases.sh b/src/test/fuzz_static_testcases.sh new file mode 100755 index 0000000000..bfe1677573 --- /dev/null +++ b/src/test/fuzz_static_testcases.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Copyright (c) 2016, The Tor Project, Inc. +# See LICENSE for licensing information + +set -e + +if [ -z "${TOR_FUZZ_CORPORA}" ] || [ ! -d "${TOR_FUZZ_CORPORA}" ] ; then + echo "You need to set TOR_FUZZ_CORPORA to point to a checkout of " + echo "the 'fuzzing-corpora' repository." + exit 77 +fi + + + +for fuzzer in "${builddir:-.}"/src/test/fuzz/fuzz-* ; do + f=`basename $fuzzer` + case="${f#fuzz-}" + if [ -d "${TOR_FUZZ_CORPORA}/${case}" ]; then + echo "Running tests for ${case}" + for entry in "${TOR_FUZZ_CORPORA}/${case}/"*; do + "${fuzzer}" "--err" < "$entry" + done + else + echo "No tests found for ${case}" + fi +done diff --git a/src/test/include.am b/src/test/include.am index d406d6d324..9f30443496 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -8,7 +8,9 @@ TESTS_ENVIRONMENT = \ export builddir="$(builddir)"; \ export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; -TESTSCRIPTS = src/test/test_zero_length_keys.sh \ +TESTSCRIPTS = \ + src/test/fuzz_static_testcases.sh \ + src/test/test_zero_length_keys.sh \ src/test/test_workqueue_cancel.sh \ src/test/test_workqueue_efd.sh \ src/test/test_workqueue_efd2.sh \ |