From 7322de15dc2f0858291f40df1a55a66c80b7fda9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 23 Jan 2015 11:15:53 -0500 Subject: [PATCH] Split the slow unit tests into their own binary This can run in parallel with the faster ones and the other tests. --- .gitignore | 2 + changes/ticket13243 | 3 + src/test/include.am | 22 +- src/test/test.c | 271 +----------------------- src/test/test_crypto.c | 393 ---------------------------------- src/test/test_crypto_slow.c | 410 ++++++++++++++++++++++++++++++++++++ src/test/test_slow.c | 29 +++ src/test/test_util.c | 369 -------------------------------- src/test/test_util_slow.c | 388 ++++++++++++++++++++++++++++++++++ src/test/testing_common.c | 298 ++++++++++++++++++++++++++ 10 files changed, 1151 insertions(+), 1034 deletions(-) create mode 100644 changes/ticket13243 create mode 100644 src/test/test_crypto_slow.c create mode 100644 src/test/test_slow.c create mode 100644 src/test/test_util_slow.c create mode 100644 src/test/testing_common.c diff --git a/.gitignore b/.gitignore index e63576cfd..0cdf2c4e6 100644 --- a/.gitignore +++ b/.gitignore @@ -160,11 +160,13 @@ cscope.* /src/test/bench /src/test/bench.exe /src/test/test +/src/test/test-slow /src/test/test-bt-cl /src/test/test-child /src/test/test-ntor-cl /src/test/test_workqueue /src/test/test.exe +/src/test/test-slow.exe /src/test/test-bt-cl.exe /src/test/test-child.exe /src/test/test-ntor-cl.exe diff --git a/changes/ticket13243 b/changes/ticket13243 new file mode 100644 index 000000000..ad6e4de50 --- /dev/null +++ b/changes/ticket13243 @@ -0,0 +1,3 @@ + o Testing: + - Move the slower unit tests into a new "./src/test/test-slow" binary + that can be run independently of the other tests. Closes ticket 13243. diff --git a/src/test/include.am b/src/test/include.am index d20603300..595be0b7c 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -1,8 +1,12 @@ -TESTS += src/test/test +TESTS += src/test/test src/test/test-slow noinst_PROGRAMS+= src/test/bench if UNITTESTS_ENABLED -noinst_PROGRAMS+= src/test/test src/test/test-child src/test/test_workqueue +noinst_PROGRAMS+= \ + src/test/test \ + src/test/test-slow \ + src/test/test-child \ + src/test/test_workqueue endif src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ @@ -57,8 +61,17 @@ src_test_test_SOURCES = \ src/test/test_status.c \ src/test/test_threads.c \ src/test/test_util.c \ + src/test/testing_common.c \ src/ext/tinytest.c +src_test_test_slow_SOURCES = \ + src/test/test_slow.c \ + src/test/test_crypto_slow.c \ + src/test/test_util_slow.c \ + src/test/testing_common.c \ + src/ext/tinytest.c + + src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS) @@ -80,6 +93,11 @@ src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ @TOR_SYSTEMD_LIBS@ +src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS) +src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS) +src_test_test_slow_LDADD = $(src_test_test_LDADD) +src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS) + src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \ diff --git a/src/test/test.c b/src/test/test.c index d75233615..ff4f517ac 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -3,10 +3,6 @@ * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -/* Ordinarily defined in tor_main.c; this bit is just here to provide one - * since we're not linking to tor_main.c */ -const char tor_git_revision[] = ""; - /** * \file test.c * \brief Unit tests for many pieces of the lower level Tor modules. @@ -67,171 +63,6 @@ double fabs(double x); #include "crypto_curve25519.h" #include "onion_ntor.h" -#ifdef USE_DMALLOC -#include -#include -#include "main.h" -#endif - -/** Set to true if any unit test has failed. Mostly, this is set by the macros - * in test.h */ -int have_failed = 0; - -/** Temporary directory (set up by setup_directory) under which we store all - * our files during testing. */ -static char temp_dir[256]; -#ifdef _WIN32 -#define pid_t int -#endif -static pid_t temp_dir_setup_in_pid = 0; - -/** Select and create the temporary directory we'll use to run our unit tests. - * Store it in temp_dir. Exit immediately if we can't create it. - * idempotent. */ -static void -setup_directory(void) -{ - static int is_setup = 0; - int r; - char rnd[256], rnd32[256]; - if (is_setup) return; - -/* Due to base32 limitation needs to be a multiple of 5. */ -#define RAND_PATH_BYTES 5 - crypto_rand(rnd, RAND_PATH_BYTES); - base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES); - -#ifdef _WIN32 - { - char buf[MAX_PATH]; - const char *tmp = buf; - const char *extra_backslash = ""; - /* If this fails, we're probably screwed anyway */ - if (!GetTempPathA(sizeof(buf),buf)) - tmp = "c:\\windows\\temp\\"; - if (strcmpend(tmp, "\\")) { - /* According to MSDN, it should be impossible for GetTempPath to give us - * an answer that doesn't end with \. But let's make sure. */ - extra_backslash = "\\"; - } - tor_snprintf(temp_dir, sizeof(temp_dir), - "%s%stor_test_%d_%s", tmp, extra_backslash, - (int)getpid(), rnd32); - r = mkdir(temp_dir); - } -#else - tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s", - (int) getpid(), rnd32); - r = mkdir(temp_dir, 0700); - if (!r) { - /* undo sticky bit so tests don't get confused. */ - r = chown(temp_dir, getuid(), getgid()); - } -#endif - if (r) { - fprintf(stderr, "Can't create directory %s:", temp_dir); - perror(""); - exit(1); - } - is_setup = 1; - temp_dir_setup_in_pid = getpid(); -} - -/** Return a filename relative to our testing temporary directory */ -const char * -get_fname(const char *name) -{ - static char buf[1024]; - setup_directory(); - if (!name) - return temp_dir; - tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name); - return buf; -} - -/* Remove a directory and all of its subdirectories */ -static void -rm_rf(const char *dir) -{ - struct stat st; - smartlist_t *elements; - - elements = tor_listdir(dir); - if (elements) { - SMARTLIST_FOREACH_BEGIN(elements, const char *, cp) { - char *tmp = NULL; - tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp); - if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) { - rm_rf(tmp); - } else { - if (unlink(tmp)) { - fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno)); - } - } - tor_free(tmp); - } SMARTLIST_FOREACH_END(cp); - SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); - smartlist_free(elements); - } - if (rmdir(dir)) - fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno)); -} - -/** Remove all files stored under the temporary directory, and the directory - * itself. Called by atexit(). */ -static void -remove_directory(void) -{ - if (getpid() != temp_dir_setup_in_pid) { - /* Only clean out the tempdir when the main process is exiting. */ - return; - } - - rm_rf(temp_dir); -} - -/** Define this if unit tests spend too much time generating public keys*/ -#undef CACHE_GENERATED_KEYS - -static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL}; -#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys) - -/** Generate and return a new keypair for use in unit tests. If we're using - * the key cache optimization, we might reuse keys: we only guarantee that - * keys made with distinct values for idx are different. The value of - * idx must be at least 0, and less than N_PREGEN_KEYS. */ -crypto_pk_t * -pk_generate(int idx) -{ -#ifdef CACHE_GENERATED_KEYS - tor_assert(idx < N_PREGEN_KEYS); - if (! pregen_keys[idx]) { - pregen_keys[idx] = crypto_pk_new(); - tor_assert(!crypto_pk_generate_key(pregen_keys[idx])); - } - return crypto_pk_dup_key(pregen_keys[idx]); -#else - crypto_pk_t *result; - (void) idx; - result = crypto_pk_new(); - tor_assert(!crypto_pk_generate_key(result)); - return result; -#endif -} - -/** Free all storage used for the cached key optimization. */ -static void -free_pregenerated_keys(void) -{ - unsigned idx; - for (idx = 0; idx < N_PREGEN_KEYS; ++idx) { - if (pregen_keys[idx]) { - crypto_pk_free(pregen_keys[idx]); - pregen_keys[idx] = NULL; - } - } -} - /** Run unit tests for the onion handshake code. */ static void test_onion_handshake(void *arg) @@ -1258,23 +1089,6 @@ test_stats(void *arg) tor_free(s); } -static void * -passthrough_test_setup(const struct testcase_t *testcase) -{ - return testcase->setup_data; -} -static int -passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr) -{ - (void)testcase; - (void)ptr; - return 1; -} - -const struct testcase_setup_t passthrough_setup = { - passthrough_test_setup, passthrough_test_cleanup -}; - #define ENT(name) \ { #name, test_ ## name , 0, NULL, NULL } #define FORK(name) \ @@ -1335,7 +1149,7 @@ extern struct testcase_t status_tests[]; extern struct testcase_t thread_tests[]; extern struct testcase_t util_tests[]; -static struct testgroup_t testgroups[] = { +struct testgroup_t testgroups[] = { { "", test_array }, { "accounting/", accounting_tests }, { "addr/", addr_tests }, @@ -1379,86 +1193,3 @@ static struct testgroup_t testgroups[] = { END_OF_GROUPS }; -/** Main entry point for unit test code: parse the command line, and run - * some unit tests. */ -int -main(int c, const char **v) -{ - or_options_t *options; - char *errmsg = NULL; - int i, i_out; - int loglevel = LOG_ERR; - int accel_crypto = 0; - -#ifdef USE_DMALLOC - { - int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_); - tor_assert(r); - } -#endif - - update_approx_time(time(NULL)); - options = options_new(); - tor_threads_init(); - init_logging(1); - configure_backtrace_handler(get_version()); - - for (i_out = i = 1; i < c; ++i) { - if (!strcmp(v[i], "--warn")) { - loglevel = LOG_WARN; - } else if (!strcmp(v[i], "--notice")) { - loglevel = LOG_NOTICE; - } else if (!strcmp(v[i], "--info")) { - loglevel = LOG_INFO; - } else if (!strcmp(v[i], "--debug")) { - loglevel = LOG_DEBUG; - } else if (!strcmp(v[i], "--accel")) { - accel_crypto = 1; - } else { - v[i_out++] = v[i]; - } - } - c = i_out; - - { - log_severity_list_t s; - memset(&s, 0, sizeof(s)); - set_log_severity_config(loglevel, LOG_ERR, &s); - add_stream_log(&s, "", fileno(stdout)); - } - - options->command = CMD_RUN_UNITTESTS; - if (crypto_global_init(accel_crypto, NULL, NULL)) { - printf("Can't initialize crypto subsystem; exiting.\n"); - return 1; - } - crypto_set_tls_dh_prime(NULL); - crypto_seed_rng(1); - rep_hist_init(); - network_init(); - setup_directory(); - options_init(options); - options->DataDirectory = tor_strdup(temp_dir); - options->EntryStatistics = 1; - if (set_options(options, &errmsg) < 0) { - printf("Failed to set initial options: %s\n", errmsg); - tor_free(errmsg); - return 1; - } - - atexit(remove_directory); - - have_failed = (tinytest_main(c, v, testgroups) != 0); - - free_pregenerated_keys(); -#ifdef USE_DMALLOC - tor_free_all(0); - dmalloc_log_unfreed(); -#endif - - if (have_failed) - return 1; - else - return 0; -} - diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 8426c715a..e9fb8bf08 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -5,7 +5,6 @@ #include "orconfig.h" #define CRYPTO_CURVE25519_PRIVATE -#define CRYPTO_S2K_PRIVATE #include "or.h" #include "test.h" #include "aes.h" @@ -14,8 +13,6 @@ #include "crypto_curve25519.h" #include "crypto_ed25519.h" #include "ed25519_vectors.inc" -#include "crypto_s2k.h" -#include "crypto_pwbox.h" extern const char AUTHORITY_SIGNKEY_3[]; extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; @@ -712,379 +709,6 @@ test_crypto_formats(void *arg) tor_free(data3); } -/** Run unit tests for our secret-to-key passphrase hashing functionality. */ -static void -test_crypto_s2k_rfc2440(void *arg) -{ - char buf[29]; - char buf2[29]; - char *buf3 = NULL; - int i; - - (void)arg; - memset(buf, 0, sizeof(buf)); - memset(buf2, 0, sizeof(buf2)); - buf3 = tor_malloc(65536); - memset(buf3, 0, 65536); - - secret_to_key_rfc2440(buf+9, 20, "", 0, buf); - crypto_digest(buf2+9, buf3, 1024); - tt_mem_op(buf,OP_EQ, buf2, 29); - - memcpy(buf,"vrbacrda",8); - memcpy(buf2,"vrbacrda",8); - buf[8] = 96; - buf2[8] = 96; - secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf); - for (i = 0; i < 65536; i += 16) { - memcpy(buf3+i, "vrbacrda12345678", 16); - } - crypto_digest(buf2+9, buf3, 65536); - tt_mem_op(buf,OP_EQ, buf2, 29); - - done: - tor_free(buf3); -} - -static void -run_s2k_tests(const unsigned flags, const unsigned type, - int speclen, const int keylen, int legacy) -{ - uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN]; - int r; - size_t sz; - const char pw1[] = "You can't come in here unless you say swordfish!"; - const char pw2[] = "Now, I give you one more guess."; - - r = secret_to_key_new(buf, sizeof(buf), &sz, - pw1, strlen(pw1), flags); - tt_int_op(r, OP_EQ, S2K_OKAY); - tt_int_op(buf[0], OP_EQ, type); - - tt_int_op(sz, OP_EQ, keylen + speclen); - - if (legacy) { - memmove(buf, buf+1, sz-1); - --sz; - --speclen; - } - - tt_int_op(S2K_OKAY, OP_EQ, - secret_to_key_check(buf, sz, pw1, strlen(pw1))); - - tt_int_op(S2K_BAD_SECRET, OP_EQ, - secret_to_key_check(buf, sz, pw2, strlen(pw2))); - - /* Move key to buf2, and clear it. */ - memset(buf3, 0, sizeof(buf3)); - memcpy(buf2, buf+speclen, keylen); - memset(buf+speclen, 0, sz - speclen); - - /* Derivekey should produce the same results. */ - tt_int_op(S2K_OKAY, OP_EQ, - secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1))); - - tt_mem_op(buf2, OP_EQ, buf3, keylen); - - /* Derivekey with a longer output should fill the output. */ - memset(buf2, 0, sizeof(buf2)); - tt_int_op(S2K_OKAY, OP_EQ, - secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen, - pw1, strlen(pw1))); - - tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2)); - - memset(buf3, 0, sizeof(buf3)); - tt_int_op(S2K_OKAY, OP_EQ, - secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen, - pw1, strlen(pw1))); - tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3)); - tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen)); - - done: - ; -} - -static void -test_crypto_s2k_general(void *arg) -{ - const char *which = arg; - - if (!strcmp(which, "scrypt")) { - run_s2k_tests(0, 2, 19, 32, 0); - } else if (!strcmp(which, "scrypt-low")) { - run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0); - } else if (!strcmp(which, "pbkdf2")) { - run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0); - } else if (!strcmp(which, "rfc2440")) { - run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0); - } else if (!strcmp(which, "rfc2440-legacy")) { - run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1); - } else { - tt_fail(); - } -} - -static void -test_crypto_s2k_errors(void *arg) -{ - uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN]; - size_t sz; - - (void)arg; - - /* Bogus specifiers: simple */ - tt_int_op(S2K_BAD_LEN, OP_EQ, - secret_to_key_derivekey(buf, sizeof(buf), - (const uint8_t*)"", 0, "ABC", 3)); - tt_int_op(S2K_BAD_ALGORITHM, OP_EQ, - secret_to_key_derivekey(buf, sizeof(buf), - (const uint8_t*)"\x10", 1, "ABC", 3)); - tt_int_op(S2K_BAD_LEN, OP_EQ, - secret_to_key_derivekey(buf, sizeof(buf), - (const uint8_t*)"\x01\x02", 2, "ABC", 3)); - - tt_int_op(S2K_BAD_LEN, OP_EQ, - secret_to_key_check((const uint8_t*)"", 0, "ABC", 3)); - tt_int_op(S2K_BAD_ALGORITHM, OP_EQ, - secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3)); - tt_int_op(S2K_BAD_LEN, OP_EQ, - secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3)); - - /* too long gets "BAD_LEN" too */ - memset(buf, 0, sizeof(buf)); - buf[0] = 2; - tt_int_op(S2K_BAD_LEN, OP_EQ, - secret_to_key_derivekey(buf2, sizeof(buf2), - buf, sizeof(buf), "ABC", 3)); - - /* Truncated output */ -#ifdef HAVE_LIBSCRYPT_H - tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz, - "ABC", 3, 0)); - tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz, - "ABC", 3, S2K_FLAG_LOW_MEM)); -#endif - tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz, - "ABC", 3, S2K_FLAG_USE_PBKDF2)); - tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz, - "ABC", 3, S2K_FLAG_NO_SCRYPT)); - -#ifdef HAVE_LIBSCRYPT_H - tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0)); - tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, - S2K_FLAG_LOW_MEM)); -#endif - tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17, - S2K_FLAG_USE_PBKDF2)); - tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9, - S2K_FLAG_NO_SCRYPT)); - - /* Now try using type-specific bogus specifiers. */ - - /* It's a bad pbkdf2 buffer if it has an iteration count that would overflow - * int32_t. */ - memset(buf, 0, sizeof(buf)); - buf[0] = 1; /* pbkdf2 */ - buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */ - tt_int_op(S2K_BAD_PARAMS, OP_EQ, - secret_to_key_derivekey(buf2, sizeof(buf2), - buf, 18, "ABC", 3)); - -#ifdef HAVE_LIBSCRYPT_H - /* It's a bad scrypt buffer if N would overflow uint64 */ - memset(buf, 0, sizeof(buf)); - buf[0] = 2; /* scrypt */ - buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */ - tt_int_op(S2K_BAD_PARAMS, OP_EQ, - secret_to_key_derivekey(buf2, sizeof(buf2), - buf, 19, "ABC", 3)); -#endif - - done: - ; -} - -static void -test_crypto_scrypt_vectors(void *arg) -{ - char *mem_op_hex_tmp = NULL; - uint8_t spec[64], out[64]; - - (void)arg; -#ifndef HAVE_LIBSCRYPT_H - if (1) - tt_skip(); -#endif - - /* Test vectors from - http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11. - - Note that the names of 'r' and 'N' are switched in that section. Or - possibly in libscrypt. - */ - - base16_decode((char*)spec, sizeof(spec), - "0400", 4); - memset(out, 0x00, sizeof(out)); - tt_int_op(64, OP_EQ, - secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2)); - test_memeq_hex(out, - "77d6576238657b203b19ca42c18a0497" - "f16b4844e3074ae8dfdffa3fede21442" - "fcd0069ded0948f8326a753a0fc81f17" - "e8d3e0fb2e0d3628cf35e20c38d18906"); - - base16_decode((char*)spec, sizeof(spec), - "4e61436c" "0A34", 12); - memset(out, 0x00, sizeof(out)); - tt_int_op(64, OP_EQ, - secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2)); - test_memeq_hex(out, - "fdbabe1c9d3472007856e7190d01e9fe" - "7c6ad7cbc8237830e77376634b373162" - "2eaf30d92e22a3886ff109279d9830da" - "c727afb94a83ee6d8360cbdfa2cc0640"); - - base16_decode((char*)spec, sizeof(spec), - "536f6469756d43686c6f72696465" "0e30", 32); - memset(out, 0x00, sizeof(out)); - tt_int_op(64, OP_EQ, - secret_to_key_compute_key(out, 64, spec, 16, - "pleaseletmein", 13, 2)); - test_memeq_hex(out, - "7023bdcb3afd7348461c06cd81fd38eb" - "fda8fbba904f8e3ea9b543f6545da1f2" - "d5432955613f0fcf62d49705242a9af9" - "e61e85dc0d651e40dfcf017b45575887"); - - base16_decode((char*)spec, sizeof(spec), - "536f6469756d43686c6f72696465" "1430", 32); - memset(out, 0x00, sizeof(out)); - tt_int_op(64, OP_EQ, - secret_to_key_compute_key(out, 64, spec, 16, - "pleaseletmein", 13, 2)); - test_memeq_hex(out, - "2101cb9b6a511aaeaddbbe09cf70f881" - "ec568d574a2ffd4dabe5ee9820adaa47" - "8e56fd8f4ba5d09ffa1c6d927c40f4c3" - "37304049e8a952fbcbf45c6fa77a41a4"); - - done: - tor_free(mem_op_hex_tmp); -} - -static void -test_crypto_pbkdf2_vectors(void *arg) -{ - char *mem_op_hex_tmp = NULL; - uint8_t spec[64], out[64]; - (void)arg; - - /* Test vectors from RFC6070, section 2 */ - base16_decode((char*)spec, sizeof(spec), - "73616c74" "00" , 10); - memset(out, 0x00, sizeof(out)); - tt_int_op(20, OP_EQ, - secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); - test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6"); - - base16_decode((char*)spec, sizeof(spec), - "73616c74" "01" , 10); - memset(out, 0x00, sizeof(out)); - tt_int_op(20, OP_EQ, - secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); - test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"); - - base16_decode((char*)spec, sizeof(spec), - "73616c74" "0C" , 10); - memset(out, 0x00, sizeof(out)); - tt_int_op(20, OP_EQ, - secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); - test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1"); - - base16_decode((char*)spec, sizeof(spec), - "73616c74" "18" , 10); - memset(out, 0x00, sizeof(out)); - tt_int_op(20, OP_EQ, - secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); - test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"); - - base16_decode((char*)spec, sizeof(spec), - "73616c7453414c5473616c7453414c5473616c745" - "3414c5473616c7453414c5473616c74" "0C" , 74); - memset(out, 0x00, sizeof(out)); - tt_int_op(25, OP_EQ, - secret_to_key_compute_key(out, 25, spec, 37, - "passwordPASSWORDpassword", 24, 1)); - test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"); - - base16_decode((char*)spec, sizeof(spec), - "7361006c74" "0c" , 12); - memset(out, 0x00, sizeof(out)); - tt_int_op(16, OP_EQ, - secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1)); - test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3"); - - done: - tor_free(mem_op_hex_tmp); -} - -static void -test_crypto_pwbox(void *arg) -{ - uint8_t *boxed=NULL, *decoded=NULL; - size_t len, dlen; - unsigned i; - const char msg[] = "This bunny reminds you that you still have a " - "salamander in your sylladex. She is holding the bunny Dave got you. " - "It’s sort of uncanny how similar they are, aside from the knitted " - "enhancements. Seriously, what are the odds?? So weird."; - const char pw[] = "I'm a night owl and a wise bird too"; - - const unsigned flags[] = { 0, - S2K_FLAG_NO_SCRYPT, - S2K_FLAG_LOW_MEM, - S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM, - S2K_FLAG_USE_PBKDF2 }; - (void)arg; - - for (i = 0; i < ARRAY_LENGTH(flags); ++i) { - tt_int_op(0, OP_EQ, crypto_pwbox(&boxed, &len, - (const uint8_t*)msg, strlen(msg), - pw, strlen(pw), flags[i])); - tt_assert(boxed); - tt_assert(len > 128+32); - - tt_int_op(0, OP_EQ, crypto_unpwbox(&decoded, &dlen, boxed, len, - pw, strlen(pw))); - - tt_assert(decoded); - tt_uint_op(dlen, OP_EQ, strlen(msg)); - tt_mem_op(decoded, OP_EQ, msg, dlen); - - tor_free(decoded); - - tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen, - boxed, len, - pw, strlen(pw)-1)); - boxed[len-1] ^= 1; - tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen, - boxed, len, - pw, strlen(pw))); - boxed[0] = 255; - tt_int_op(UNPWBOX_CORRUPTED, OP_EQ, crypto_unpwbox(&decoded, &dlen, - boxed, len, - pw, strlen(pw))); - - tor_free(boxed); - } - - done: - tor_free(boxed); - tor_free(decoded); -} - /** Test AES-CTR encryption and decryption with IV. */ static void test_crypto_aes_iv(void *arg) @@ -1988,23 +1612,6 @@ struct testcase_t crypto_tests[] = { { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL }, CRYPTO_LEGACY(digests), CRYPTO_LEGACY(dh), - CRYPTO_LEGACY(s2k_rfc2440), -#ifdef HAVE_LIBSCRYPT_H - { "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup, - (void*)"scrypt" }, - { "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup, - (void*)"scrypt-low" }, -#endif - { "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup, - (void*)"pbkdf2" }, - { "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup, - (void*)"rfc2440" }, - { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup, - (void*)"rfc2440-legacy" }, - { "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL }, - { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL }, - { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL }, - { "pwbox", test_crypto_pwbox, 0, NULL, NULL }, { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup, (void*)"aes" }, { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &passthrough_setup, diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c new file mode 100644 index 000000000..94fd69d45 --- /dev/null +++ b/src/test/test_crypto_slow.c @@ -0,0 +1,410 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#define CRYPTO_S2K_PRIVATE +#include "or.h" +#include "test.h" +#include "crypto_s2k.h" +#include "crypto_pwbox.h" + + +/** Run unit tests for our secret-to-key passphrase hashing functionality. */ +static void +test_crypto_s2k_rfc2440(void *arg) +{ + char buf[29]; + char buf2[29]; + char *buf3 = NULL; + int i; + + (void)arg; + memset(buf, 0, sizeof(buf)); + memset(buf2, 0, sizeof(buf2)); + buf3 = tor_malloc(65536); + memset(buf3, 0, 65536); + + secret_to_key_rfc2440(buf+9, 20, "", 0, buf); + crypto_digest(buf2+9, buf3, 1024); + tt_mem_op(buf,OP_EQ, buf2, 29); + + memcpy(buf,"vrbacrda",8); + memcpy(buf2,"vrbacrda",8); + buf[8] = 96; + buf2[8] = 96; + secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf); + for (i = 0; i < 65536; i += 16) { + memcpy(buf3+i, "vrbacrda12345678", 16); + } + crypto_digest(buf2+9, buf3, 65536); + tt_mem_op(buf,OP_EQ, buf2, 29); + + done: + tor_free(buf3); +} + +static void +run_s2k_tests(const unsigned flags, const unsigned type, + int speclen, const int keylen, int legacy) +{ + uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN]; + int r; + size_t sz; + const char pw1[] = "You can't come in here unless you say swordfish!"; + const char pw2[] = "Now, I give you one more guess."; + + r = secret_to_key_new(buf, sizeof(buf), &sz, + pw1, strlen(pw1), flags); + tt_int_op(r, OP_EQ, S2K_OKAY); + tt_int_op(buf[0], OP_EQ, type); + + tt_int_op(sz, OP_EQ, keylen + speclen); + + if (legacy) { + memmove(buf, buf+1, sz-1); + --sz; + --speclen; + } + + tt_int_op(S2K_OKAY, OP_EQ, + secret_to_key_check(buf, sz, pw1, strlen(pw1))); + + tt_int_op(S2K_BAD_SECRET, OP_EQ, + secret_to_key_check(buf, sz, pw2, strlen(pw2))); + + /* Move key to buf2, and clear it. */ + memset(buf3, 0, sizeof(buf3)); + memcpy(buf2, buf+speclen, keylen); + memset(buf+speclen, 0, sz - speclen); + + /* Derivekey should produce the same results. */ + tt_int_op(S2K_OKAY, OP_EQ, + secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1))); + + tt_mem_op(buf2, OP_EQ, buf3, keylen); + + /* Derivekey with a longer output should fill the output. */ + memset(buf2, 0, sizeof(buf2)); + tt_int_op(S2K_OKAY, OP_EQ, + secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen, + pw1, strlen(pw1))); + + tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2)); + + memset(buf3, 0, sizeof(buf3)); + tt_int_op(S2K_OKAY, OP_EQ, + secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen, + pw1, strlen(pw1))); + tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3)); + tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen)); + + done: + ; +} + +static void +test_crypto_s2k_general(void *arg) +{ + const char *which = arg; + + if (!strcmp(which, "scrypt")) { + run_s2k_tests(0, 2, 19, 32, 0); + } else if (!strcmp(which, "scrypt-low")) { + run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0); + } else if (!strcmp(which, "pbkdf2")) { + run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0); + } else if (!strcmp(which, "rfc2440")) { + run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0); + } else if (!strcmp(which, "rfc2440-legacy")) { + run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1); + } else { + tt_fail(); + } +} + +static void +test_crypto_s2k_errors(void *arg) +{ + uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN]; + size_t sz; + + (void)arg; + + /* Bogus specifiers: simple */ + tt_int_op(S2K_BAD_LEN, OP_EQ, + secret_to_key_derivekey(buf, sizeof(buf), + (const uint8_t*)"", 0, "ABC", 3)); + tt_int_op(S2K_BAD_ALGORITHM, OP_EQ, + secret_to_key_derivekey(buf, sizeof(buf), + (const uint8_t*)"\x10", 1, "ABC", 3)); + tt_int_op(S2K_BAD_LEN, OP_EQ, + secret_to_key_derivekey(buf, sizeof(buf), + (const uint8_t*)"\x01\x02", 2, "ABC", 3)); + + tt_int_op(S2K_BAD_LEN, OP_EQ, + secret_to_key_check((const uint8_t*)"", 0, "ABC", 3)); + tt_int_op(S2K_BAD_ALGORITHM, OP_EQ, + secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3)); + tt_int_op(S2K_BAD_LEN, OP_EQ, + secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3)); + + /* too long gets "BAD_LEN" too */ + memset(buf, 0, sizeof(buf)); + buf[0] = 2; + tt_int_op(S2K_BAD_LEN, OP_EQ, + secret_to_key_derivekey(buf2, sizeof(buf2), + buf, sizeof(buf), "ABC", 3)); + + /* Truncated output */ +#ifdef HAVE_LIBSCRYPT_H + tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz, + "ABC", 3, 0)); + tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz, + "ABC", 3, S2K_FLAG_LOW_MEM)); +#endif + tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz, + "ABC", 3, S2K_FLAG_USE_PBKDF2)); + tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz, + "ABC", 3, S2K_FLAG_NO_SCRYPT)); + +#ifdef HAVE_LIBSCRYPT_H + tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0)); + tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, + S2K_FLAG_LOW_MEM)); +#endif + tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17, + S2K_FLAG_USE_PBKDF2)); + tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9, + S2K_FLAG_NO_SCRYPT)); + + /* Now try using type-specific bogus specifiers. */ + + /* It's a bad pbkdf2 buffer if it has an iteration count that would overflow + * int32_t. */ + memset(buf, 0, sizeof(buf)); + buf[0] = 1; /* pbkdf2 */ + buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */ + tt_int_op(S2K_BAD_PARAMS, OP_EQ, + secret_to_key_derivekey(buf2, sizeof(buf2), + buf, 18, "ABC", 3)); + +#ifdef HAVE_LIBSCRYPT_H + /* It's a bad scrypt buffer if N would overflow uint64 */ + memset(buf, 0, sizeof(buf)); + buf[0] = 2; /* scrypt */ + buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */ + tt_int_op(S2K_BAD_PARAMS, OP_EQ, + secret_to_key_derivekey(buf2, sizeof(buf2), + buf, 19, "ABC", 3)); +#endif + + done: + ; +} + +static void +test_crypto_scrypt_vectors(void *arg) +{ + char *mem_op_hex_tmp = NULL; + uint8_t spec[64], out[64]; + + (void)arg; +#ifndef HAVE_LIBSCRYPT_H + if (1) + tt_skip(); +#endif + + /* Test vectors from + http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11. + + Note that the names of 'r' and 'N' are switched in that section. Or + possibly in libscrypt. + */ + + base16_decode((char*)spec, sizeof(spec), + "0400", 4); + memset(out, 0x00, sizeof(out)); + tt_int_op(64, OP_EQ, + secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2)); + test_memeq_hex(out, + "77d6576238657b203b19ca42c18a0497" + "f16b4844e3074ae8dfdffa3fede21442" + "fcd0069ded0948f8326a753a0fc81f17" + "e8d3e0fb2e0d3628cf35e20c38d18906"); + + base16_decode((char*)spec, sizeof(spec), + "4e61436c" "0A34", 12); + memset(out, 0x00, sizeof(out)); + tt_int_op(64, OP_EQ, + secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2)); + test_memeq_hex(out, + "fdbabe1c9d3472007856e7190d01e9fe" + "7c6ad7cbc8237830e77376634b373162" + "2eaf30d92e22a3886ff109279d9830da" + "c727afb94a83ee6d8360cbdfa2cc0640"); + + base16_decode((char*)spec, sizeof(spec), + "536f6469756d43686c6f72696465" "0e30", 32); + memset(out, 0x00, sizeof(out)); + tt_int_op(64, OP_EQ, + secret_to_key_compute_key(out, 64, spec, 16, + "pleaseletmein", 13, 2)); + test_memeq_hex(out, + "7023bdcb3afd7348461c06cd81fd38eb" + "fda8fbba904f8e3ea9b543f6545da1f2" + "d5432955613f0fcf62d49705242a9af9" + "e61e85dc0d651e40dfcf017b45575887"); + + base16_decode((char*)spec, sizeof(spec), + "536f6469756d43686c6f72696465" "1430", 32); + memset(out, 0x00, sizeof(out)); + tt_int_op(64, OP_EQ, + secret_to_key_compute_key(out, 64, spec, 16, + "pleaseletmein", 13, 2)); + test_memeq_hex(out, + "2101cb9b6a511aaeaddbbe09cf70f881" + "ec568d574a2ffd4dabe5ee9820adaa47" + "8e56fd8f4ba5d09ffa1c6d927c40f4c3" + "37304049e8a952fbcbf45c6fa77a41a4"); + + done: + tor_free(mem_op_hex_tmp); +} + +static void +test_crypto_pbkdf2_vectors(void *arg) +{ + char *mem_op_hex_tmp = NULL; + uint8_t spec[64], out[64]; + (void)arg; + + /* Test vectors from RFC6070, section 2 */ + base16_decode((char*)spec, sizeof(spec), + "73616c74" "00" , 10); + memset(out, 0x00, sizeof(out)); + tt_int_op(20, OP_EQ, + secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); + test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6"); + + base16_decode((char*)spec, sizeof(spec), + "73616c74" "01" , 10); + memset(out, 0x00, sizeof(out)); + tt_int_op(20, OP_EQ, + secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); + test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"); + + base16_decode((char*)spec, sizeof(spec), + "73616c74" "0C" , 10); + memset(out, 0x00, sizeof(out)); + tt_int_op(20, OP_EQ, + secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); + test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1"); + + base16_decode((char*)spec, sizeof(spec), + "73616c74" "18" , 10); + memset(out, 0x00, sizeof(out)); + tt_int_op(20, OP_EQ, + secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); + test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"); + + base16_decode((char*)spec, sizeof(spec), + "73616c7453414c5473616c7453414c5473616c745" + "3414c5473616c7453414c5473616c74" "0C" , 74); + memset(out, 0x00, sizeof(out)); + tt_int_op(25, OP_EQ, + secret_to_key_compute_key(out, 25, spec, 37, + "passwordPASSWORDpassword", 24, 1)); + test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"); + + base16_decode((char*)spec, sizeof(spec), + "7361006c74" "0c" , 12); + memset(out, 0x00, sizeof(out)); + tt_int_op(16, OP_EQ, + secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1)); + test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3"); + + done: + tor_free(mem_op_hex_tmp); +} + +static void +test_crypto_pwbox(void *arg) +{ + uint8_t *boxed=NULL, *decoded=NULL; + size_t len, dlen; + unsigned i; + const char msg[] = "This bunny reminds you that you still have a " + "salamander in your sylladex. She is holding the bunny Dave got you. " + "It’s sort of uncanny how similar they are, aside from the knitted " + "enhancements. Seriously, what are the odds?? So weird."; + const char pw[] = "I'm a night owl and a wise bird too"; + + const unsigned flags[] = { 0, + S2K_FLAG_NO_SCRYPT, + S2K_FLAG_LOW_MEM, + S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM, + S2K_FLAG_USE_PBKDF2 }; + (void)arg; + + for (i = 0; i < ARRAY_LENGTH(flags); ++i) { + tt_int_op(0, OP_EQ, crypto_pwbox(&boxed, &len, + (const uint8_t*)msg, strlen(msg), + pw, strlen(pw), flags[i])); + tt_assert(boxed); + tt_assert(len > 128+32); + + tt_int_op(0, OP_EQ, crypto_unpwbox(&decoded, &dlen, boxed, len, + pw, strlen(pw))); + + tt_assert(decoded); + tt_uint_op(dlen, OP_EQ, strlen(msg)); + tt_mem_op(decoded, OP_EQ, msg, dlen); + + tor_free(decoded); + + tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen, + boxed, len, + pw, strlen(pw)-1)); + boxed[len-1] ^= 1; + tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen, + boxed, len, + pw, strlen(pw))); + boxed[0] = 255; + tt_int_op(UNPWBOX_CORRUPTED, OP_EQ, crypto_unpwbox(&decoded, &dlen, + boxed, len, + pw, strlen(pw))); + + tor_free(boxed); + } + + done: + tor_free(boxed); + tor_free(decoded); +} + +#define CRYPTO_LEGACY(name) \ + { #name, test_crypto_ ## name , 0, NULL, NULL } + +struct testcase_t slow_crypto_tests[] = { + CRYPTO_LEGACY(s2k_rfc2440), +#ifdef HAVE_LIBSCRYPT_H + { "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup, + (void*)"scrypt" }, + { "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup, + (void*)"scrypt-low" }, +#endif + { "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup, + (void*)"pbkdf2" }, + { "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup, + (void*)"rfc2440" }, + { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup, + (void*)"rfc2440-legacy" }, + { "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL }, + { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL }, + { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL }, + { "pwbox", test_crypto_pwbox, 0, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_slow.c b/src/test/test_slow.c new file mode 100644 index 000000000..32386b485 --- /dev/null +++ b/src/test/test_slow.c @@ -0,0 +1,29 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_slow.c + * \brief Slower unit tests for many pieces of the lower level Tor modules. + **/ + +#include "orconfig.h" + +#include +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "or.h" +#include "test.h" + +extern struct testcase_t slow_crypto_tests[]; +extern struct testcase_t slow_util_tests[]; + +struct testgroup_t testgroups[] = { + { "slow/crypto/", slow_crypto_tests }, + { "slow/util/", slow_util_tests }, + END_OF_GROUPS +}; + diff --git a/src/test/test_util.c b/src/test/test_util.c index b53c8fc7a..e5df5b449 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -3380,370 +3380,6 @@ test_util_fgets_eagain(void *ptr) } #endif -#ifndef BUILDDIR -#define BUILDDIR "." -#endif - -#ifdef _WIN32 -#define notify_pending_waitpid_callbacks() STMT_NIL -#define TEST_CHILD "test-child.exe" -#define EOL "\r\n" -#else -#define TEST_CHILD (BUILDDIR "/src/test/test-child") -#define EOL "\n" -#endif - -#ifdef _WIN32 -/* I've assumed Windows doesn't have the gap between fork and exec - * that causes the race condition on unix-like platforms */ -#define MATCH_PROCESS_STATUS(s1,s2) ((s1) == (s2)) - -#else -/* work around a race condition of the timing of SIGCHLD handler updates - * to the process_handle's fields, and checks of those fields - * - * TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to - * PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */ -#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING (PROCESS_STATUS_RUNNING+1) -#define IS_RUNNING_OR_NOTRUNNING(s) \ - ((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING) -/* well, this is ugly */ -#define MATCH_PROCESS_STATUS(s1,s2) \ - ( (s1) == (s2) \ - ||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \ - && IS_RUNNING_OR_NOTRUNNING(s2)) \ - ||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \ - && IS_RUNNING_OR_NOTRUNNING(s1))) - -#endif // _WIN32 - -/** Helper function for testing tor_spawn_background */ -static void -run_util_spawn_background(const char *argv[], const char *expected_out, - const char *expected_err, int expected_exit, - int expected_status) -{ - int retval, exit_code; - ssize_t pos; - process_handle_t *process_handle=NULL; - char stdout_buf[100], stderr_buf[100]; - int status; - - /* Start the program */ -#ifdef _WIN32 - status = tor_spawn_background(NULL, argv, NULL, &process_handle); -#else - status = tor_spawn_background(argv[0], argv, NULL, &process_handle); -#endif - - notify_pending_waitpid_callbacks(); - - /* the race condition doesn't affect status, - * because status isn't updated by the SIGCHLD handler, - * but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */ - tt_assert(MATCH_PROCESS_STATUS(expected_status, status)); - if (status == PROCESS_STATUS_ERROR) { - tt_ptr_op(process_handle, OP_EQ, NULL); - return; - } - - tt_assert(process_handle != NULL); - - /* When a spawned process forks, fails, then exits very quickly, - * (this typically occurs when exec fails) - * there is a race condition between the SIGCHLD handler - * updating the process_handle's fields, and this test - * checking the process status in those fields. - * The SIGCHLD update can occur before or after the code below executes. - * This causes intermittent failures in spawn_background_fail(), - * typically when the machine is under load. - * We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */ - - /* the race condition affects the change in - * process_handle->status from RUNNING to NOTRUNNING */ - tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status)); - -#ifndef _WIN32 - notify_pending_waitpid_callbacks(); - /* the race condition affects the change in - * process_handle->waitpid_cb to NULL, - * so we skip the check if expected_status is ambiguous, - * that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */ - tt_assert(process_handle->waitpid_cb != NULL - || expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING); -#endif - -#ifdef _WIN32 - tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE); - tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE); -#else - tt_assert(process_handle->stdout_pipe >= 0); - tt_assert(process_handle->stderr_pipe >= 0); -#endif - - /* Check stdout */ - pos = tor_read_all_from_process_stdout(process_handle, stdout_buf, - sizeof(stdout_buf) - 1); - tt_assert(pos >= 0); - stdout_buf[pos] = '\0'; - tt_int_op(strlen(expected_out),OP_EQ, pos); - tt_str_op(expected_out,OP_EQ, stdout_buf); - - notify_pending_waitpid_callbacks(); - - /* Check it terminated correctly */ - retval = tor_get_exit_code(process_handle, 1, &exit_code); - tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval); - tt_int_op(expected_exit,OP_EQ, exit_code); - // TODO: Make test-child exit with something other than 0 - -#ifndef _WIN32 - notify_pending_waitpid_callbacks(); - tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL); -#endif - - /* Check stderr */ - pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, - sizeof(stderr_buf) - 1); - tt_assert(pos >= 0); - stderr_buf[pos] = '\0'; - tt_str_op(expected_err,OP_EQ, stderr_buf); - tt_int_op(strlen(expected_err),OP_EQ, pos); - - notify_pending_waitpid_callbacks(); - - done: - if (process_handle) - tor_process_handle_destroy(process_handle, 1); -} - -/** Check that we can launch a process and read the output */ -static void -test_util_spawn_background_ok(void *ptr) -{ - const char *argv[] = {TEST_CHILD, "--test", NULL}; - const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL; - const char *expected_err = "ERR"EOL; - - (void)ptr; - - run_util_spawn_background(argv, expected_out, expected_err, 0, - PROCESS_STATUS_RUNNING); -} - -/** Check that failing to find the executable works as expected */ -static void -test_util_spawn_background_fail(void *ptr) -{ - const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL}; - const char *expected_err = ""; - char expected_out[1024]; - char code[32]; -#ifdef _WIN32 - const int expected_status = PROCESS_STATUS_ERROR; -#else - /* TODO: Once we can signal failure to exec, set this to be - * PROCESS_STATUS_RUNNING_OR_ERROR */ - const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING; -#endif - - memset(expected_out, 0xf0, sizeof(expected_out)); - memset(code, 0xf0, sizeof(code)); - - (void)ptr; - - tor_snprintf(code, sizeof(code), "%x/%x", - 9 /* CHILD_STATE_FAILEXEC */ , ENOENT); - tor_snprintf(expected_out, sizeof(expected_out), - "ERR: Failed to spawn background process - code %s\n", code); - - run_util_spawn_background(argv, expected_out, expected_err, 255, - expected_status); -} - -/** Test that reading from a handle returns a partial read rather than - * blocking */ -static void -test_util_spawn_background_partial_read_impl(int exit_early) -{ - const int expected_exit = 0; - const int expected_status = PROCESS_STATUS_RUNNING; - - int retval, exit_code; - ssize_t pos = -1; - process_handle_t *process_handle=NULL; - int status; - char stdout_buf[100], stderr_buf[100]; - - const char *argv[] = {TEST_CHILD, "--test", NULL}; - const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL, - "DONE" EOL, - NULL }; - const char *expected_err = "ERR" EOL; - -#ifndef _WIN32 - int eof = 0; -#endif - int expected_out_ctr; - - if (exit_early) { - argv[1] = "--hang"; - expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL; - } - - /* Start the program */ -#ifdef _WIN32 - status = tor_spawn_background(NULL, argv, NULL, &process_handle); -#else - status = tor_spawn_background(argv[0], argv, NULL, &process_handle); -#endif - tt_int_op(expected_status,OP_EQ, status); - tt_assert(process_handle); - tt_int_op(expected_status,OP_EQ, process_handle->status); - - /* Check stdout */ - for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) { -#ifdef _WIN32 - pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, - sizeof(stdout_buf) - 1, NULL); -#else - /* Check that we didn't read the end of file last time */ - tt_assert(!eof); - pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, - sizeof(stdout_buf) - 1, NULL, &eof); -#endif - log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos); - - /* We would have blocked, keep on trying */ - if (0 == pos) - continue; - - tt_assert(pos > 0); - stdout_buf[pos] = '\0'; - tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf); - tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos); - expected_out_ctr++; - } - - if (exit_early) { - tor_process_handle_destroy(process_handle, 1); - process_handle = NULL; - goto done; - } - - /* The process should have exited without writing more */ -#ifdef _WIN32 - pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, - sizeof(stdout_buf) - 1, - process_handle); - tt_int_op(0,OP_EQ, pos); -#else - if (!eof) { - /* We should have got all the data, but maybe not the EOF flag */ - pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, - sizeof(stdout_buf) - 1, - process_handle, &eof); - tt_int_op(0,OP_EQ, pos); - tt_assert(eof); - } - /* Otherwise, we got the EOF on the last read */ -#endif - - /* Check it terminated correctly */ - retval = tor_get_exit_code(process_handle, 1, &exit_code); - tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval); - tt_int_op(expected_exit,OP_EQ, exit_code); - - // TODO: Make test-child exit with something other than 0 - - /* Check stderr */ - pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, - sizeof(stderr_buf) - 1); - tt_assert(pos >= 0); - stderr_buf[pos] = '\0'; - tt_str_op(expected_err,OP_EQ, stderr_buf); - tt_int_op(strlen(expected_err),OP_EQ, pos); - - done: - tor_process_handle_destroy(process_handle, 1); -} - -static void -test_util_spawn_background_partial_read(void *arg) -{ - (void)arg; - test_util_spawn_background_partial_read_impl(0); -} - -static void -test_util_spawn_background_exit_early(void *arg) -{ - (void)arg; - test_util_spawn_background_partial_read_impl(1); -} - -static void -test_util_spawn_background_waitpid_notify(void *arg) -{ - int retval, exit_code; - process_handle_t *process_handle=NULL; - int status; - int ms_timer; - - const char *argv[] = {TEST_CHILD, "--fast", NULL}; - - (void) arg; - -#ifdef _WIN32 - status = tor_spawn_background(NULL, argv, NULL, &process_handle); -#else - status = tor_spawn_background(argv[0], argv, NULL, &process_handle); -#endif - - tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING); - tt_ptr_op(process_handle, OP_NE, NULL); - - /* We're not going to look at the stdout/stderr output this time. Instead, - * we're testing whether notify_pending_waitpid_calbacks() can report the - * process exit (on unix) and/or whether tor_get_exit_code() can notice it - * (on windows) */ - -#ifndef _WIN32 - ms_timer = 30*1000; - tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL); - while (process_handle->waitpid_cb && ms_timer > 0) { - tor_sleep_msec(100); - ms_timer -= 100; - notify_pending_waitpid_callbacks(); - } - tt_int_op(ms_timer, OP_GT, 0); - tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL); -#endif - - ms_timer = 30*1000; - while (((retval = tor_get_exit_code(process_handle, 0, &exit_code)) - == PROCESS_EXIT_RUNNING) && ms_timer > 0) { - tor_sleep_msec(100); - ms_timer -= 100; - } - tt_int_op(ms_timer, OP_GT, 0); - - tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED); - - done: - tor_process_handle_destroy(process_handle, 1); -} - -#undef TEST_CHILD -#undef EOL - -#undef MATCH_PROCESS_STATUS - -#ifndef _WIN32 -#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING -#undef IS_RUNNING_OR_NOTRUNNING -#endif - /** * Test for format_hex_number_sigsafe() */ @@ -4796,11 +4432,6 @@ struct testcase_t util_tests[] = { UTIL_TEST(exit_status, 0), UTIL_TEST(fgets_eagain, 0), #endif - UTIL_TEST(spawn_background_ok, 0), - UTIL_TEST(spawn_background_fail, 0), - UTIL_TEST(spawn_background_partial_read, 0), - UTIL_TEST(spawn_background_exit_early, 0), - UTIL_TEST(spawn_background_waitpid_notify, 0), UTIL_TEST(format_hex_number, 0), UTIL_TEST(format_dec_number, 0), UTIL_TEST(join_win_cmdline, 0), diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c new file mode 100644 index 000000000..4adcbe1d1 --- /dev/null +++ b/src/test/test_util_slow.c @@ -0,0 +1,388 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#define UTIL_PRIVATE +#include "util.h" +#include "util_process.h" +#include "crypto.h" +#include "torlog.h" +#include "test.h" + +#ifndef BUILDDIR +#define BUILDDIR "." +#endif + +#ifdef _WIN32 +#define notify_pending_waitpid_callbacks() STMT_NIL +#define TEST_CHILD "test-child.exe" +#define EOL "\r\n" +#else +#define TEST_CHILD (BUILDDIR "/src/test/test-child") +#define EOL "\n" +#endif + +#ifdef _WIN32 +/* I've assumed Windows doesn't have the gap between fork and exec + * that causes the race condition on unix-like platforms */ +#define MATCH_PROCESS_STATUS(s1,s2) ((s1) == (s2)) + +#else +/* work around a race condition of the timing of SIGCHLD handler updates + * to the process_handle's fields, and checks of those fields + * + * TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to + * PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */ +#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING (PROCESS_STATUS_RUNNING+1) +#define IS_RUNNING_OR_NOTRUNNING(s) \ + ((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING) +/* well, this is ugly */ +#define MATCH_PROCESS_STATUS(s1,s2) \ + ( (s1) == (s2) \ + ||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \ + && IS_RUNNING_OR_NOTRUNNING(s2)) \ + ||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \ + && IS_RUNNING_OR_NOTRUNNING(s1))) + +#endif // _WIN32 + +/** Helper function for testing tor_spawn_background */ +static void +run_util_spawn_background(const char *argv[], const char *expected_out, + const char *expected_err, int expected_exit, + int expected_status) +{ + int retval, exit_code; + ssize_t pos; + process_handle_t *process_handle=NULL; + char stdout_buf[100], stderr_buf[100]; + int status; + + /* Start the program */ +#ifdef _WIN32 + status = tor_spawn_background(NULL, argv, NULL, &process_handle); +#else + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); +#endif + + notify_pending_waitpid_callbacks(); + + /* the race condition doesn't affect status, + * because status isn't updated by the SIGCHLD handler, + * but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */ + tt_assert(MATCH_PROCESS_STATUS(expected_status, status)); + if (status == PROCESS_STATUS_ERROR) { + tt_ptr_op(process_handle, OP_EQ, NULL); + return; + } + + tt_assert(process_handle != NULL); + + /* When a spawned process forks, fails, then exits very quickly, + * (this typically occurs when exec fails) + * there is a race condition between the SIGCHLD handler + * updating the process_handle's fields, and this test + * checking the process status in those fields. + * The SIGCHLD update can occur before or after the code below executes. + * This causes intermittent failures in spawn_background_fail(), + * typically when the machine is under load. + * We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */ + + /* the race condition affects the change in + * process_handle->status from RUNNING to NOTRUNNING */ + tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status)); + +#ifndef _WIN32 + notify_pending_waitpid_callbacks(); + /* the race condition affects the change in + * process_handle->waitpid_cb to NULL, + * so we skip the check if expected_status is ambiguous, + * that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */ + tt_assert(process_handle->waitpid_cb != NULL + || expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING); +#endif + +#ifdef _WIN32 + tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE); + tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE); +#else + tt_assert(process_handle->stdout_pipe >= 0); + tt_assert(process_handle->stderr_pipe >= 0); +#endif + + /* Check stdout */ + pos = tor_read_all_from_process_stdout(process_handle, stdout_buf, + sizeof(stdout_buf) - 1); + tt_assert(pos >= 0); + stdout_buf[pos] = '\0'; + tt_int_op(strlen(expected_out),OP_EQ, pos); + tt_str_op(expected_out,OP_EQ, stdout_buf); + + notify_pending_waitpid_callbacks(); + + /* Check it terminated correctly */ + retval = tor_get_exit_code(process_handle, 1, &exit_code); + tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval); + tt_int_op(expected_exit,OP_EQ, exit_code); + // TODO: Make test-child exit with something other than 0 + +#ifndef _WIN32 + notify_pending_waitpid_callbacks(); + tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL); +#endif + + /* Check stderr */ + pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, + sizeof(stderr_buf) - 1); + tt_assert(pos >= 0); + stderr_buf[pos] = '\0'; + tt_str_op(expected_err,OP_EQ, stderr_buf); + tt_int_op(strlen(expected_err),OP_EQ, pos); + + notify_pending_waitpid_callbacks(); + + done: + if (process_handle) + tor_process_handle_destroy(process_handle, 1); +} + +/** Check that we can launch a process and read the output */ +static void +test_util_spawn_background_ok(void *ptr) +{ + const char *argv[] = {TEST_CHILD, "--test", NULL}; + const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL; + const char *expected_err = "ERR"EOL; + + (void)ptr; + + run_util_spawn_background(argv, expected_out, expected_err, 0, + PROCESS_STATUS_RUNNING); +} + +/** Check that failing to find the executable works as expected */ +static void +test_util_spawn_background_fail(void *ptr) +{ + const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL}; + const char *expected_err = ""; + char expected_out[1024]; + char code[32]; +#ifdef _WIN32 + const int expected_status = PROCESS_STATUS_ERROR; +#else + /* TODO: Once we can signal failure to exec, set this to be + * PROCESS_STATUS_RUNNING_OR_ERROR */ + const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING; +#endif + + memset(expected_out, 0xf0, sizeof(expected_out)); + memset(code, 0xf0, sizeof(code)); + + (void)ptr; + + tor_snprintf(code, sizeof(code), "%x/%x", + 9 /* CHILD_STATE_FAILEXEC */ , ENOENT); + tor_snprintf(expected_out, sizeof(expected_out), + "ERR: Failed to spawn background process - code %s\n", code); + + run_util_spawn_background(argv, expected_out, expected_err, 255, + expected_status); +} + +/** Test that reading from a handle returns a partial read rather than + * blocking */ +static void +test_util_spawn_background_partial_read_impl(int exit_early) +{ + const int expected_exit = 0; + const int expected_status = PROCESS_STATUS_RUNNING; + + int retval, exit_code; + ssize_t pos = -1; + process_handle_t *process_handle=NULL; + int status; + char stdout_buf[100], stderr_buf[100]; + + const char *argv[] = {TEST_CHILD, "--test", NULL}; + const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL, + "DONE" EOL, + NULL }; + const char *expected_err = "ERR" EOL; + +#ifndef _WIN32 + int eof = 0; +#endif + int expected_out_ctr; + + if (exit_early) { + argv[1] = "--hang"; + expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL; + } + + /* Start the program */ +#ifdef _WIN32 + status = tor_spawn_background(NULL, argv, NULL, &process_handle); +#else + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); +#endif + tt_int_op(expected_status,OP_EQ, status); + tt_assert(process_handle); + tt_int_op(expected_status,OP_EQ, process_handle->status); + + /* Check stdout */ + for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) { +#ifdef _WIN32 + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, + sizeof(stdout_buf) - 1, NULL); +#else + /* Check that we didn't read the end of file last time */ + tt_assert(!eof); + pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, + sizeof(stdout_buf) - 1, NULL, &eof); +#endif + log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos); + + /* We would have blocked, keep on trying */ + if (0 == pos) + continue; + + tt_assert(pos > 0); + stdout_buf[pos] = '\0'; + tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf); + tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos); + expected_out_ctr++; + } + + if (exit_early) { + tor_process_handle_destroy(process_handle, 1); + process_handle = NULL; + goto done; + } + + /* The process should have exited without writing more */ +#ifdef _WIN32 + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, + sizeof(stdout_buf) - 1, + process_handle); + tt_int_op(0,OP_EQ, pos); +#else + if (!eof) { + /* We should have got all the data, but maybe not the EOF flag */ + pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, + sizeof(stdout_buf) - 1, + process_handle, &eof); + tt_int_op(0,OP_EQ, pos); + tt_assert(eof); + } + /* Otherwise, we got the EOF on the last read */ +#endif + + /* Check it terminated correctly */ + retval = tor_get_exit_code(process_handle, 1, &exit_code); + tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval); + tt_int_op(expected_exit,OP_EQ, exit_code); + + // TODO: Make test-child exit with something other than 0 + + /* Check stderr */ + pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, + sizeof(stderr_buf) - 1); + tt_assert(pos >= 0); + stderr_buf[pos] = '\0'; + tt_str_op(expected_err,OP_EQ, stderr_buf); + tt_int_op(strlen(expected_err),OP_EQ, pos); + + done: + tor_process_handle_destroy(process_handle, 1); +} + +static void +test_util_spawn_background_partial_read(void *arg) +{ + (void)arg; + test_util_spawn_background_partial_read_impl(0); +} + +static void +test_util_spawn_background_exit_early(void *arg) +{ + (void)arg; + test_util_spawn_background_partial_read_impl(1); +} + +static void +test_util_spawn_background_waitpid_notify(void *arg) +{ + int retval, exit_code; + process_handle_t *process_handle=NULL; + int status; + int ms_timer; + + const char *argv[] = {TEST_CHILD, "--fast", NULL}; + + (void) arg; + +#ifdef _WIN32 + status = tor_spawn_background(NULL, argv, NULL, &process_handle); +#else + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); +#endif + + tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING); + tt_ptr_op(process_handle, OP_NE, NULL); + + /* We're not going to look at the stdout/stderr output this time. Instead, + * we're testing whether notify_pending_waitpid_calbacks() can report the + * process exit (on unix) and/or whether tor_get_exit_code() can notice it + * (on windows) */ + +#ifndef _WIN32 + ms_timer = 30*1000; + tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL); + while (process_handle->waitpid_cb && ms_timer > 0) { + tor_sleep_msec(100); + ms_timer -= 100; + notify_pending_waitpid_callbacks(); + } + tt_int_op(ms_timer, OP_GT, 0); + tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL); +#endif + + ms_timer = 30*1000; + while (((retval = tor_get_exit_code(process_handle, 0, &exit_code)) + == PROCESS_EXIT_RUNNING) && ms_timer > 0) { + tor_sleep_msec(100); + ms_timer -= 100; + } + tt_int_op(ms_timer, OP_GT, 0); + + tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED); + + done: + tor_process_handle_destroy(process_handle, 1); +} + +#undef TEST_CHILD +#undef EOL + +#undef MATCH_PROCESS_STATUS + +#ifndef _WIN32 +#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING +#undef IS_RUNNING_OR_NOTRUNNING +#endif + +#define UTIL_TEST(name, flags) \ + { #name, test_util_ ## name, flags, NULL, NULL } + +struct testcase_t slow_util_tests[] = { + UTIL_TEST(spawn_background_ok, 0), + UTIL_TEST(spawn_background_fail, 0), + UTIL_TEST(spawn_background_partial_read, 0), + UTIL_TEST(spawn_background_exit_early, 0), + UTIL_TEST(spawn_background_waitpid_notify, 0), + END_OF_TESTCASES +}; diff --git a/src/test/testing_common.c b/src/test/testing_common.c new file mode 100644 index 000000000..d7d6dacee --- /dev/null +++ b/src/test/testing_common.c @@ -0,0 +1,298 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Ordinarily defined in tor_main.c; this bit is just here to provide one + * since we're not linking to tor_main.c */ +const char tor_git_revision[] = ""; + +/** + * \file test_common.c + * \brief Common pieces to implement unit tests. + **/ + +#include "orconfig.h" +#include "or.h" +#include "config.h" +#include "rephist.h" +#include "backtrace.h" +#include "test.h" + +#include +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef _WIN32 +/* For mkdir() */ +#include +#else +#include +#endif + +#include "or.h" + +#ifdef USE_DMALLOC +#include +#include +#include "main.h" +#endif + +/** Temporary directory (set up by setup_directory) under which we store all + * our files during testing. */ +static char temp_dir[256]; +#ifdef _WIN32 +#define pid_t int +#endif +static pid_t temp_dir_setup_in_pid = 0; + +/** Select and create the temporary directory we'll use to run our unit tests. + * Store it in temp_dir. Exit immediately if we can't create it. + * idempotent. */ +static void +setup_directory(void) +{ + static int is_setup = 0; + int r; + char rnd[256], rnd32[256]; + if (is_setup) return; + +/* Due to base32 limitation needs to be a multiple of 5. */ +#define RAND_PATH_BYTES 5 + crypto_rand(rnd, RAND_PATH_BYTES); + base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES); + +#ifdef _WIN32 + { + char buf[MAX_PATH]; + const char *tmp = buf; + const char *extra_backslash = ""; + /* If this fails, we're probably screwed anyway */ + if (!GetTempPathA(sizeof(buf),buf)) + tmp = "c:\\windows\\temp\\"; + if (strcmpend(tmp, "\\")) { + /* According to MSDN, it should be impossible for GetTempPath to give us + * an answer that doesn't end with \. But let's make sure. */ + extra_backslash = "\\"; + } + tor_snprintf(temp_dir, sizeof(temp_dir), + "%s%stor_test_%d_%s", tmp, extra_backslash, + (int)getpid(), rnd32); + r = mkdir(temp_dir); + } +#else + tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s", + (int) getpid(), rnd32); + r = mkdir(temp_dir, 0700); + if (!r) { + /* undo sticky bit so tests don't get confused. */ + r = chown(temp_dir, getuid(), getgid()); + } +#endif + if (r) { + fprintf(stderr, "Can't create directory %s:", temp_dir); + perror(""); + exit(1); + } + is_setup = 1; + temp_dir_setup_in_pid = getpid(); +} + +/** Return a filename relative to our testing temporary directory */ +const char * +get_fname(const char *name) +{ + static char buf[1024]; + setup_directory(); + if (!name) + return temp_dir; + tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name); + return buf; +} + +/* Remove a directory and all of its subdirectories */ +static void +rm_rf(const char *dir) +{ + struct stat st; + smartlist_t *elements; + + elements = tor_listdir(dir); + if (elements) { + SMARTLIST_FOREACH_BEGIN(elements, const char *, cp) { + char *tmp = NULL; + tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp); + if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) { + rm_rf(tmp); + } else { + if (unlink(tmp)) { + fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno)); + } + } + tor_free(tmp); + } SMARTLIST_FOREACH_END(cp); + SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); + smartlist_free(elements); + } + if (rmdir(dir)) + fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno)); +} + +/** Remove all files stored under the temporary directory, and the directory + * itself. Called by atexit(). */ +static void +remove_directory(void) +{ + if (getpid() != temp_dir_setup_in_pid) { + /* Only clean out the tempdir when the main process is exiting. */ + return; + } + + rm_rf(temp_dir); +} + +/** Define this if unit tests spend too much time generating public keys*/ +#undef CACHE_GENERATED_KEYS + +static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL}; +#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys) + +/** Generate and return a new keypair for use in unit tests. If we're using + * the key cache optimization, we might reuse keys: we only guarantee that + * keys made with distinct values for idx are different. The value of + * idx must be at least 0, and less than N_PREGEN_KEYS. */ +crypto_pk_t * +pk_generate(int idx) +{ +#ifdef CACHE_GENERATED_KEYS + tor_assert(idx < N_PREGEN_KEYS); + if (! pregen_keys[idx]) { + pregen_keys[idx] = crypto_pk_new(); + tor_assert(!crypto_pk_generate_key(pregen_keys[idx])); + } + return crypto_pk_dup_key(pregen_keys[idx]); +#else + crypto_pk_t *result; + (void) idx; + result = crypto_pk_new(); + tor_assert(!crypto_pk_generate_key(result)); + return result; +#endif +} + +/** Free all storage used for the cached key optimization. */ +static void +free_pregenerated_keys(void) +{ + unsigned idx; + for (idx = 0; idx < N_PREGEN_KEYS; ++idx) { + if (pregen_keys[idx]) { + crypto_pk_free(pregen_keys[idx]); + pregen_keys[idx] = NULL; + } + } +} + +static void * +passthrough_test_setup(const struct testcase_t *testcase) +{ + return testcase->setup_data; +} +static int +passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + (void)testcase; + (void)ptr; + return 1; +} + +const struct testcase_setup_t passthrough_setup = { + passthrough_test_setup, passthrough_test_cleanup +}; + +extern struct testgroup_t testgroups[]; + +/** Main entry point for unit test code: parse the command line, and run + * some unit tests. */ +int +main(int c, const char **v) +{ + or_options_t *options; + char *errmsg = NULL; + int i, i_out; + int loglevel = LOG_ERR; + int accel_crypto = 0; + +#ifdef USE_DMALLOC + { + int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_); + tor_assert(r); + } +#endif + + update_approx_time(time(NULL)); + options = options_new(); + tor_threads_init(); + init_logging(1); + configure_backtrace_handler(get_version()); + + for (i_out = i = 1; i < c; ++i) { + if (!strcmp(v[i], "--warn")) { + loglevel = LOG_WARN; + } else if (!strcmp(v[i], "--notice")) { + loglevel = LOG_NOTICE; + } else if (!strcmp(v[i], "--info")) { + loglevel = LOG_INFO; + } else if (!strcmp(v[i], "--debug")) { + loglevel = LOG_DEBUG; + } else if (!strcmp(v[i], "--accel")) { + accel_crypto = 1; + } else { + v[i_out++] = v[i]; + } + } + c = i_out; + + { + log_severity_list_t s; + memset(&s, 0, sizeof(s)); + set_log_severity_config(loglevel, LOG_ERR, &s); + add_stream_log(&s, "", fileno(stdout)); + } + + options->command = CMD_RUN_UNITTESTS; + if (crypto_global_init(accel_crypto, NULL, NULL)) { + printf("Can't initialize crypto subsystem; exiting.\n"); + return 1; + } + crypto_set_tls_dh_prime(NULL); + crypto_seed_rng(1); + rep_hist_init(); + network_init(); + setup_directory(); + options_init(options); + options->DataDirectory = tor_strdup(temp_dir); + options->EntryStatistics = 1; + if (set_options(options, &errmsg) < 0) { + printf("Failed to set initial options: %s\n", errmsg); + tor_free(errmsg); + return 1; + } + + atexit(remove_directory); + + int have_failed = (tinytest_main(c, v, testgroups) != 0); + + free_pregenerated_keys(); +#ifdef USE_DMALLOC + tor_free_all(0); + dmalloc_log_unfreed(); +#endif + + if (have_failed) + return 1; + else + return 0; +} +