Browse Source

Merge branch 'combined-fuzzing-v4'

Nick Mathewson 7 years ago
parent
commit
558c04f5b1

+ 4 - 0
.gitignore

@@ -192,6 +192,10 @@ uptime-*.json
 /src/test/test-timers.exe
 /src/test/test_workqueue.exe
 
+# /src/test/fuzz
+/src/test/fuzz/fuzz-*
+/src/test/fuzz/lf-fuzz-*
+
 # /src/tools/
 /src/tools/tor-checkkey
 /src/tools/tor-resolve

+ 7 - 0
Makefile.am

@@ -9,6 +9,7 @@ noinst_LIBRARIES=
 EXTRA_DIST=
 noinst_HEADERS=
 bin_PROGRAMS=
+EXTRA_PROGRAMS=
 CLEANFILES=
 TESTS=
 noinst_PROGRAMS=
@@ -54,6 +55,11 @@ TEST_CPPFLAGS=-DTOR_UNIT_TESTS
 TEST_NETWORK_FLAGS=--hs-multi-client 1
 endif
 
+if LIBFUZZER_ENABLED
+TEST_CFLAGS += -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div
+# not "edge"
+endif
+
 TEST_NETWORK_ALL_LOG_DIR=$(top_builddir)/test_network_log
 TEST_NETWORK_ALL_DRIVER_FLAGS=--color-tests yes
 
@@ -189,6 +195,7 @@ if USE_PERL
 		$(top_srcdir)/src/common/*.[ch] \
 		$(top_srcdir)/src/or/*.[ch] \
 		$(top_srcdir)/src/test/*.[ch] \
+		$(top_srcdir)/src/test/*/*.[ch] \
 		$(top_srcdir)/src/tools/*.[ch]
 endif
 

+ 8 - 1
configure.ac

@@ -49,6 +49,10 @@ AC_ARG_ENABLE(asserts-in-tests,
    AS_HELP_STRING(--disable-asserts-in-tests, [disable tor_assert() calls in the unit tests, for branch coverage]))
 AC_ARG_ENABLE(system-torrc,
    AS_HELP_STRING(--disable-system-torrc, [don't look for a system-wide torrc file]))
+AC_ARG_ENABLE(libfuzzer,
+   AS_HELP_STRING(--enable-libfuzzer, [build extra fuzzers based on 'libfuzzer']))
+AC_ARG_ENABLE(oss-fuzz,
+   AS_HELP_STRING(--enable-oss-fuzz, [build extra fuzzers based on 'oss-fuzz' environment]))
 
 if test "x$enable_coverage" != "xyes" -a "x$enable_asserts_in_tests" = "xno" ; then
     AC_MSG_ERROR([Can't disable assertions outside of coverage build])
@@ -57,6 +61,8 @@ fi
 AM_CONDITIONAL(UNITTESTS_ENABLED, test "x$enable_unittests" != "xno")
 AM_CONDITIONAL(COVERAGE_ENABLED, test "x$enable_coverage" = "xyes")
 AM_CONDITIONAL(DISABLE_ASSERTS_IN_UNIT_TESTS, test "x$enable_asserts_in_tests" = "xno")
+AM_CONDITIONAL(LIBFUZZER_ENABLED, test "x$enable_libfuzzer" = "xyes")
+AM_CONDITIONAL(OSS_FUZZ_ENABLED, test "x$enable_oss_fuzz" = "xyes")
 
 if test "$enable_static_tor" = "yes"; then
   enable_static_libevent="yes";
@@ -735,6 +741,7 @@ CFLAGS_FWRAPV=
 CFLAGS_ASAN=
 CFLAGS_UBSAN=
 
+
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
 #if !defined(__clang__)
 #error
@@ -758,7 +765,7 @@ m4_ifdef([AS_VAR_IF],[
     AS_VAR_POPDEF([can_compile])
     TOR_CHECK_CFLAGS(-Wstack-protector)
     TOR_CHECK_CFLAGS(--param ssp-buffer-size=1)
-    if test "$bwin32" = "false"; then
+    if test "$bwin32" = "false" && test "$enable_libfuzzer" != "yes" && test "$enable_oss_fuzz" != "yes"; then
        TOR_CHECK_CFLAGS(-fPIE)
        TOR_CHECK_LDFLAGS(-pie, "$all_ldflags_for_check", "$all_libs_for_check")
     fi

+ 120 - 0
doc/HACKING/Fuzzing.md

@@ -0,0 +1,120 @@
+= Fuzzing Tor
+
+== The simple version (no fuzzing, only tests)
+
+Check out fuzzing-corpora, and set TOR_FUZZ_CORPORA to point to the place
+where you checked it out.
+
+To run the fuzzing test cases in a deterministic fashion, use:
+  make fuzz
+
+
+== Different kinds of fuzzing
+
+Right now we support three different kinds of fuzzer.
+
+First, there's American Fuzzy Lop (AFL), a fuzzer that works by forking
+a target binary and passing it lots of different inputs on stdin.  It's the
+trickiest one to set up, so I'll be describing it more below.
+
+Second, there's libFuzzer, a llvm-based fuzzer that you link in as a library,
+and it runs a target function over and over.  To use this one, you'll need to
+have a reasonably recent clang and libfuzzer installed.  At that point, you
+just build with --enable-expensive-hardening and --enable-libfuzzer.  That
+will produce a set of binaries in src/test/fuzz/lf-fuzz-* .  These programs
+take as input a series of directories full of fuzzing examples.  For more
+information on libfuzzer, see http://llvm.org/docs/LibFuzzer.html
+
+Third, there's Google's OSS-Fuzz infrastructure, which expects to get all of
+its.  For more on this, see https://github.com/google/oss-fuzz and the
+projects/tor subdirectory.  You'll need to mess around with Docker a bit to
+test this one out; it's meant to run on Google's infrastructure.
+
+In all cases, you'll need some starting examples to give the fuzzer when it
+starts out.  There's a set in the "fuzzing-corpora" git repository.  Try
+setting TOR_FUZZ_CORPORA to point to a checkout of that repository
+
+== Writing Tor fuzzers
+
+A tor fuzzing harness should have:
+* a fuzz_init() function to set up any necessary global state.
+* a fuzz_main() function to receive input and pass it to a parser.
+* a fuzz_cleanup() function to clear global state.
+
+Most fuzzing frameworks will produce many invalid inputs - a tor fuzzing
+harness should rejecting invalid inputs without crashing or behaving badly.
+
+But the fuzzing harness should crash if tor fails an assertion, triggers a
+bug, or accesses memory it shouldn't. This helps fuzzing frameworks detect
+"interesting" cases.
+
+
+== Guided Fuzzing with AFL
+
+There is no HTTPS, hash, or signature for American Fuzzy Lop's source code, so
+its integrity can't be verified. That said, you really shouldn't fuzz on a
+machine you care about, anyway.
+
+To Build:
+  Get AFL from http://lcamtuf.coredump.cx/afl/ and unpack it
+  cd afl
+  make
+  cd ../tor
+  PATH=$PATH:../afl/ CC="../afl/afl-gcc" ./configure --enable-expensive-hardening
+  AFL_HARDEN=1 make clean fuzz
+
+To Find The ASAN Memory Limit: (64-bit only)
+
+On 64-bit platforms, afl needs to know how much memory ASAN uses.
+Or, you can configure tor without --enable-expensive-hardening, then use
+  make fuzz
+to run the generated test cases through an ASAN-enabled fuzz-http.
+Read afl/docs/notes_for_asan.txt for more details.
+
+  Download recidivm from http://jwilk.net/software/recidivm
+  Download the signature
+  Check the signature
+  tar xvzf recidivm*.tar.gz
+  cd recidivm*
+  make
+  /path/to/recidivm -v src/test/fuzz_dir
+  Use the final "ok" figure as the input to -m when calling afl-fuzz
+  (Normally, recidivm would output a figure automatically, but in some cases,
+  the fuzzing harness will hang when the memory limit is too small.)
+
+You could also just say "none" instead of the memory limit below, if you
+don't care about memory limits.
+
+
+To Run:
+  mkdir -p src/test/fuzz/fuzz_http_findings
+  ../afl/afl-fuzz -i ${TOR_FUZZ_CORPORA}/http -o src/test/fuzz/fuzz_http_findings -m <asan-memory-limit> -- src/test/fuzz_dir
+
+
+AFL has a multi-core mode, check the documentation for details.
+You might find the included fuzz-multi.sh script useful for this.
+
+macOS (OS X) requires slightly more preparation, including:
+* using afl-clang (or afl-clang-fast from the llvm directory)
+* disabling external crash reporting (AFL will guide you through this step)
+
+== Triaging Issues
+
+Crashes are usually interesting, particularly if using AFL_HARDEN=1 and --enable-expensive-hardening. Sometimes crashes are due to bugs in the harness code.
+
+Hangs might be interesting, but they might also be spurious machine slowdowns.
+Check if a hang is reproducible before reporting it. Sometimes, processing
+valid inputs may take a second or so, particularly with the fuzzer and
+sanitizers enabled.
+
+To see what fuzz-http is doing with a test case, call it like this:
+  src/test/fuzz/fuzz-http --debug < /path/to/test.case
+
+(Logging is disabled while fuzzing to increase fuzzing speed.)
+
+== Reporting Issues
+
+Please report any issues discovered using the process in Tor's security issue
+policy:
+
+https://trac.torproject.org/projects/tor/wiki/org/meetings/2016SummerDevMeeting/Notes/SecurityIssuePolicy

+ 8 - 7
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;

+ 6 - 4
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,

+ 11 - 11
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,

+ 10 - 6
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
 

+ 1 - 1
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

+ 48 - 0
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. */

+ 3 - 0
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

+ 8 - 8
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;

+ 9 - 4
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,

+ 1 - 1
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;

+ 2 - 1
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);

+ 6 - 6
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 =

+ 2 - 2
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);

+ 28 - 9
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));

+ 6 - 1
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"

+ 52 - 0
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"

+ 41 - 0
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"

+ 32 - 0
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"

+ 8 - 0
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"

+ 24 - 0
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"

+ 6 - 0
src/test/fuzz/dict/iptsv2

@@ -0,0 +1,6 @@
+"introduction-point"
+"ip-address"
+"onion-port"
+"onion-key"
+"service-key"
+

+ 7 - 0
src/test/fuzz/dict/microdesc

@@ -0,0 +1,7 @@
+"onion-key"
+"ntor-onion-key"
+"id"
+"a"
+"family"
+"p"
+"p6"

+ 19 - 0
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

+ 78 - 0
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;
+}
+

+ 79 - 0
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;
+}
+

+ 65 - 0
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;
+}
+

+ 52 - 0
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;
+}
+

+ 133 - 0
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;
+}
+

+ 46 - 0
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;
+}
+

+ 47 - 0
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;
+}
+

+ 34 - 0
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

+ 80 - 0
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;
+}
+

+ 13 - 0
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 */
+

+ 190 - 0
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
+

+ 250 - 0
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

+ 14 - 0
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}"
+

+ 27 - 0
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

+ 3 - 1
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 \