Split the slow unit tests into their own binary

This can run in parallel with the faster ones and the other tests.
This commit is contained in:
Nick Mathewson 2015-01-23 11:15:53 -05:00
parent 420037dcef
commit 7322de15dc
10 changed files with 1151 additions and 1034 deletions

2
.gitignore vendored
View File

@ -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

3
changes/ticket13243 Normal file
View File

@ -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.

View File

@ -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 \

View File

@ -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 <dmalloc.h>
#include <openssl/crypto.h>
#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 <b>temp_dir</b>. 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 <b>idx</b> are different. The value of
* <b>idx</b> 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;
}

View File

@ -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. "
"Its 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,

410
src/test/test_crypto_slow.c Normal file
View File

@ -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. "
"Its 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
};

29
src/test/test_slow.c Normal file
View File

@ -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 <stdio.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#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
};

View File

@ -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),

388
src/test/test_util_slow.c Normal file
View File

@ -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
};

298
src/test/testing_common.c Normal file
View File

@ -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 <stdio.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef _WIN32
/* For mkdir() */
#include <direct.h>
#else
#include <dirent.h>
#endif
#include "or.h"
#ifdef USE_DMALLOC
#include <dmalloc.h>
#include <openssl/crypto.h>
#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 <b>temp_dir</b>. 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 <b>idx</b> are different. The value of
* <b>idx</b> 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;
}