diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/address.c | 4 | ||||
-rw-r--r-- | src/common/address.h | 2 | ||||
-rw-r--r-- | src/common/compat.c | 3 | ||||
-rw-r--r-- | src/common/compat.h | 2 | ||||
-rw-r--r-- | src/common/crypto.c | 179 | ||||
-rw-r--r-- | src/common/crypto.h | 2 | ||||
-rw-r--r-- | src/common/crypto_curve25519.c | 9 | ||||
-rw-r--r-- | src/common/crypto_ed25519.c | 19 | ||||
-rw-r--r-- | src/common/crypto_pwbox.c | 28 | ||||
-rw-r--r-- | src/common/crypto_s2k.c | 8 | ||||
-rw-r--r-- | src/common/di_ops.c | 45 | ||||
-rw-r--r-- | src/common/di_ops.h | 2 | ||||
-rw-r--r-- | src/common/handles.h | 153 | ||||
-rw-r--r-- | src/common/include.am | 54 | ||||
-rw-r--r-- | src/common/memarea.c | 6 | ||||
-rw-r--r-- | src/common/procmon.c | 9 | ||||
-rw-r--r-- | src/common/pubsub.c | 129 | ||||
-rw-r--r-- | src/common/pubsub.h | 177 | ||||
-rw-r--r-- | src/common/timers.c | 297 | ||||
-rw-r--r-- | src/common/timers.h | 22 | ||||
-rw-r--r-- | src/common/util.c | 55 | ||||
-rw-r--r-- | src/common/util.h | 42 | ||||
-rw-r--r-- | src/common/util_bug.c | 53 | ||||
-rw-r--r-- | src/common/util_bug.h | 150 |
24 files changed, 1252 insertions, 198 deletions
diff --git a/src/common/address.c b/src/common/address.c index 793a40effc..01607a4b3a 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1172,7 +1172,7 @@ tor_addr_hash(const tor_addr_t *addr) /** Return a newly allocated string with a representation of <b>addr</b>. */ char * -tor_dup_addr(const tor_addr_t *addr) +tor_addr_to_str_dup(const tor_addr_t *addr) { char buf[TOR_ADDR_BUF_LEN]; if (tor_addr_to_str(buf, addr, sizeof(buf), 0)) { @@ -1809,7 +1809,7 @@ MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity, /* ====== * IPv4 helpers - * XXXX024 IPv6 deprecate some of these. + * XXXX IPv6 deprecate some of these. */ /** Given an address of the form "ip:port", try to divide it into its diff --git a/src/common/address.h b/src/common/address.h index 53712bde02..3de67e1c74 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -179,7 +179,7 @@ tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u) #define TOR_ADDR_BUF_LEN 48 int tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr_out); -char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC; +char *tor_addr_to_str_dup(const tor_addr_t *addr) ATTR_MALLOC; /** Wrapper function of fmt_addr_impl(). It does not decorate IPv6 * addresses. */ diff --git a/src/common/compat.c b/src/common/compat.c index 23eaa134cf..9d49b8ff44 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -15,7 +15,8 @@ /* This is required on rh7 to make strptime not complain. * We also need it to make memmem get defined (where available) */ -/* XXXX024 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf, + +/* XXXX We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf, * and get this (and other important stuff!) automatically. Once we do that, * make sure to also change the extern char **environ detection in * configure.ac, because whether that is declared or not depends on whether diff --git a/src/common/compat.h b/src/common/compat.h index 8cf84580c6..b6ee4106db 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -430,7 +430,7 @@ typedef int socklen_t; #ifdef _WIN32 /* XXX Actually, this should arguably be SOCKET; we use intptr_t here so that - * any inadvertant checks for the socket being <= 0 or > 0 will probably + * any inadvertent checks for the socket being <= 0 or > 0 will probably * still work. */ #define tor_socket_t intptr_t #define TOR_SOCKET_T_FORMAT INTPTR_T_FORMAT diff --git a/src/common/crypto.c b/src/common/crypto.c index 933f1033f7..76e262e257 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -134,7 +134,7 @@ crypto_get_rsa_padding_overhead(int padding) switch (padding) { case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD; - default: tor_assert(0); return -1; + default: tor_assert(0); return -1; // LCOV_EXCL_LINE } } @@ -146,7 +146,7 @@ crypto_get_rsa_padding(int padding) switch (padding) { case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING; - default: tor_assert(0); return -1; + default: tor_assert(0); return -1; // LCOV_EXCL_LINE } } @@ -171,13 +171,9 @@ crypto_log_errors(int severity, const char *doing) if (!msg) msg = "(null)"; if (!lib) lib = "(null)"; if (!func) func = "(null)"; - if (doing) { - tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)", + if (BUG(!doing)) doing = "(null)"; + tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)", doing, msg, lib, func); - } else { - tor_log(severity, LD_CRYPTO, "crypto error: %s (in %s:%s)", - msg, lib, func); - } } } @@ -944,6 +940,10 @@ crypto_pk_copy_full(crypto_pk_t *env) new_key = RSAPublicKey_dup(env->key); } if (!new_key) { + /* LCOV_EXCL_START + * + * We can't cause RSA*Key_dup() to fail, so we can't really test this. + */ log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.", privatekey?"private":"public"); crypto_log_errors(LOG_ERR, @@ -951,6 +951,7 @@ crypto_pk_copy_full(crypto_pk_t *env) "Duplicating a public key"); tor_fragile_assert(); return NULL; + /* LCOV_EXCL_STOP */ } return crypto_new_pk_from_rsa_(new_key); @@ -1701,8 +1702,10 @@ crypto_digest_algorithm_get_name(digest_algorithm_t alg) case DIGEST_SHA3_512: return "sha3-512"; default: + // LCOV_EXCL_START tor_fragile_assert(); return "??unknown_digest??"; + // LCOV_EXCL_STOP } } @@ -1726,7 +1729,7 @@ crypto_digest_algorithm_parse_name(const char *name) } /** Given an algorithm, return the digest length in bytes. */ -static inline size_t +size_t crypto_digest_algorithm_get_length(digest_algorithm_t alg) { switch (alg) { @@ -1741,8 +1744,8 @@ crypto_digest_algorithm_get_length(digest_algorithm_t alg) case DIGEST_SHA3_512: return DIGEST512_LEN; default: - tor_assert(0); - return 0; /* Unreachable */ + tor_assert(0); // LCOV_EXCL_LINE + return 0; /* Unreachable */ // LCOV_EXCL_LINE } } @@ -1785,23 +1788,53 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg) case DIGEST_SHA3_512: return END_OF_FIELD(d.sha3); default: - tor_assert(0); - return 0; + tor_assert(0); // LCOV_EXCL_LINE + return 0; // LCOV_EXCL_LINE } #undef END_OF_FIELD #undef STRUCT_FIELD_SIZE } +/** + * Internal function: create and return a new digest object for 'algorithm'. + * Does not typecheck the algorithm. + */ +static crypto_digest_t * +crypto_digest_new_internal(digest_algorithm_t algorithm) +{ + crypto_digest_t *r = tor_malloc(crypto_digest_alloc_bytes(algorithm)); + r->algorithm = algorithm; + + switch (algorithm) + { + case DIGEST_SHA1: + SHA1_Init(&r->d.sha1); + break; + case DIGEST_SHA256: + SHA256_Init(&r->d.sha2); + break; + case DIGEST_SHA512: + SHA512_Init(&r->d.sha512); + break; + case DIGEST_SHA3_256: + keccak_digest_init(&r->d.sha3, 256); + break; + case DIGEST_SHA3_512: + keccak_digest_init(&r->d.sha3, 512); + break; + default: + tor_assert_unreached(); + } + + return r; +} + /** Allocate and return a new digest object to compute SHA1 digests. */ crypto_digest_t * crypto_digest_new(void) { - crypto_digest_t *r; - r = tor_malloc(crypto_digest_alloc_bytes(DIGEST_SHA1)); - SHA1_Init(&r->d.sha1); - r->algorithm = DIGEST_SHA1; - return r; + return crypto_digest_new_internal(DIGEST_SHA1); } /** Allocate and return a new digest object to compute 256-bit digests @@ -1809,15 +1842,8 @@ crypto_digest_new(void) crypto_digest_t * crypto_digest256_new(digest_algorithm_t algorithm) { - crypto_digest_t *r; tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256); - r = tor_malloc(crypto_digest_alloc_bytes(algorithm)); - if (algorithm == DIGEST_SHA256) - SHA256_Init(&r->d.sha2); - else - keccak_digest_init(&r->d.sha3, 256); - r->algorithm = algorithm; - return r; + return crypto_digest_new_internal(algorithm); } /** Allocate and return a new digest object to compute 512-bit digests @@ -1825,15 +1851,8 @@ crypto_digest256_new(digest_algorithm_t algorithm) crypto_digest_t * crypto_digest512_new(digest_algorithm_t algorithm) { - crypto_digest_t *r; tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512); - r = tor_malloc(crypto_digest_alloc_bytes(algorithm)); - if (algorithm == DIGEST_SHA512) - SHA512_Init(&r->d.sha512); - else - keccak_digest_init(&r->d.sha3, 512); - r->algorithm = algorithm; - return r; + return crypto_digest_new_internal(algorithm); } /** Deallocate a digest object. @@ -1876,8 +1895,10 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len); break; default: + /* LCOV_EXCL_START */ tor_fragile_assert(); break; + /* LCOV_EXCL_STOP */ } } @@ -1916,13 +1937,15 @@ crypto_digest_get_digest(crypto_digest_t *digest, case DIGEST_SHA512: SHA512_Final(r, &tmpenv.d.sha512); break; +//LCOV_EXCL_START case DIGEST_SHA3_256: /* FALLSTHROUGH */ case DIGEST_SHA3_512: - log_warn(LD_BUG, "Handling unexpected algorithm %d", digest->algorithm); - tor_assert(0); /* This is fatal, because it should never happen. */ default: - tor_assert(0); /* Unreachable. */ + log_warn(LD_BUG, "Handling unexpected algorithm %d", digest->algorithm); + /* This is fatal, because it should never happen. */ + tor_assert_unreached(); break; +//LCOV_EXCL_STOP } memcpy(out, r, out_len); memwipe(r, 0, sizeof(r)); @@ -1981,27 +2004,7 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out, const char *append, digest_algorithm_t alg) { - crypto_digest_t *d = NULL; - switch (alg) { - case DIGEST_SHA1: - d = crypto_digest_new(); - break; - case DIGEST_SHA256: /* FALLSTHROUGH */ - case DIGEST_SHA3_256: - d = crypto_digest256_new(alg); - break; - case DIGEST_SHA512: /* FALLSTHROUGH */ - case DIGEST_SHA3_512: - d = crypto_digest512_new(alg); - break; - default: - log_warn(LD_BUG, "Called with unknown algorithm %d", alg); - /* If fragile_assert is not enabled, wipe output and return - * without running any calculations */ - memwipe(digest_out, 0xff, len_out); - tor_fragile_assert(); - goto free; - } + crypto_digest_t *d = crypto_digest_new_internal(alg); if (prepend) crypto_digest_add_bytes(d, prepend, strlen(prepend)); SMARTLIST_FOREACH(lst, const char *, cp, @@ -2009,8 +2012,6 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out, if (append) crypto_digest_add_bytes(d, append, strlen(append)); crypto_digest_get_digest(d, digest_out, len_out); - - free: crypto_digest_free(d); } @@ -2169,9 +2170,14 @@ crypto_set_tls_dh_prime(void) int r; /* If the space is occupied, free the previous TLS DH prime */ - if (dh_param_p_tls) { + if (BUG(dh_param_p_tls)) { + /* LCOV_EXCL_START + * + * We shouldn't be calling this twice. + */ BN_clear_free(dh_param_p_tls); dh_param_p_tls = NULL; + /* LCOV_EXCL_STOP */ } tls_prime = BN_new(); @@ -2203,8 +2209,8 @@ init_dh_param(void) { BIGNUM *circuit_dh_prime; int r; - if (dh_param_p && dh_param_g) - return; + if (BUG(dh_param_p && dh_param_g)) + return; // LCOV_EXCL_LINE This function isn't supposed to be called twice. circuit_dh_prime = BN_new(); tor_assert(circuit_dh_prime); @@ -2269,10 +2275,13 @@ crypto_dh_new(int dh_type) return res; err: + /* LCOV_EXCL_START + * This error condition is only reached when an allocation fails */ crypto_log_errors(LOG_WARN, "creating DH object"); if (res->dh) DH_free(res->dh); /* frees p and g too */ tor_free(res); return NULL; + /* LCOV_EXCL_STOP */ } /** Return a copy of <b>dh</b>, sharing its internal state. */ @@ -2304,10 +2313,15 @@ crypto_dh_generate_public(crypto_dh_t *dh) { again: if (!DH_generate_key(dh->dh)) { + /* LCOV_EXCL_START + * To test this we would need some way to tell openssl to break DH. */ crypto_log_errors(LOG_WARN, "generating DH key"); return -1; + /* LCOV_EXCL_STOP */ } if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) { + /* LCOV_EXCL_START + * If this happens, then openssl's DH implementation is busted. */ log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-" "the-universe chances really do happen. Trying again."); /* Free and clear the keys, so OpenSSL will actually try again. */ @@ -2315,6 +2329,7 @@ crypto_dh_generate_public(crypto_dh_t *dh) BN_clear_free(dh->dh->priv_key); dh->dh->pub_key = dh->dh->priv_key = NULL; goto again; + /* LCOV_EXCL_STOP */ } return 0; } @@ -2361,8 +2376,8 @@ tor_check_dh_key(int severity, BIGNUM *bn) tor_assert(bn); x = BN_new(); tor_assert(x); - if (!dh_param_p) - init_dh_param(); + if (BUG(!dh_param_p)) + init_dh_param(); //LCOV_EXCL_LINE we already checked whether we did this. BN_set_word(x, 1); if (BN_cmp(bn,x)<=0) { log_fn(severity, LD_CRYPTO, "DH key must be at least 2."); @@ -2384,8 +2399,6 @@ tor_check_dh_key(int severity, BIGNUM *bn) return -1; } -#undef MIN -#define MIN(a,b) ((a)<(b)?(a):(b)) /** Given a DH key exchange object, and our peer's value of g^y (as a * <b>pubkey_len</b>-byte value in <b>pubkey</b>) generate * <b>secret_bytes_out</b> bytes of shared key material and write them @@ -2573,6 +2586,11 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng) tor_init_weak_random(rng, seed); } +#ifdef TOR_UNIT_TESTS +int break_strongest_rng_syscall = 0; +int break_strongest_rng_fallback = 0; +#endif + /** Try to get <b>out_len</b> bytes of the strongest entropy we can generate, * via system calls, storing it into <b>out</b>. Return 0 on success, -1 on * failure. A maximum request size of 256 bytes is imposed. @@ -2582,6 +2600,11 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len) { tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE); +#ifdef TOR_UNIT_TESTS + if (break_strongest_rng_syscall) + return -1; +#endif + #if defined(_WIN32) static int provider_set = 0; static HCRYPTPROV provider; @@ -2631,6 +2654,7 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len) } while (ret == -1 && ((errno == EINTR) ||(errno == EAGAIN))); if (PREDICT_UNLIKELY(ret == -1)) { + /* LCOV_EXCL_START we can't actually make the syscall fail in testing. */ tor_assert(errno != EAGAIN); tor_assert(errno != EINTR); @@ -2638,6 +2662,7 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len) log_warn(LD_CRYPTO, "Can't get entropy from getrandom()."); getrandom_works = 0; /* Don't bother trying again. */ return -1; + /* LCOV_EXCL_STOP */ } tor_assert(ret == (long)out_len); @@ -2666,6 +2691,11 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len) static int crypto_strongest_rand_fallback(uint8_t *out, size_t out_len) { +#ifdef TOR_UNIT_TESTS + if (break_strongest_rng_fallback) + return -1; +#endif + #ifdef _WIN32 /* Windows exclusively uses crypto_strongest_rand_syscall(). */ (void)out; @@ -2686,10 +2716,13 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len) n = read_all(fd, (char*)out, out_len, 0); close(fd); if (n != out_len) { + /* LCOV_EXCL_START + * We can't make /dev/foorandom actually fail. */ log_warn(LD_CRYPTO, "Error reading from entropy source (read only %lu bytes).", (unsigned long)n); return -1; + /* LCOV_EXCL_STOP */ } return 0; @@ -2703,7 +2736,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len) * storing it into <b>out</b>. Return 0 on success, -1 on failure. A maximum * request size of 256 bytes is imposed. */ -static int +STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len) { static const size_t sanity_min_size = 16; @@ -2737,13 +2770,17 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len) return 0; } - /* We tried max_attempts times to fill a buffer >= 128 bits long, + /* LCOV_EXCL_START + * + * We tried max_attempts times to fill a buffer >= 128 bits long, * and each time it returned all '0's. Either the system entropy * source is busted, or the user should go out and buy a ticket to * every lottery on the planet. */ log_warn(LD_CRYPTO, "Strong OS entropy returned all zero buffer."); + return -1; + /* LCOV_EXCL_STOP */ } /** Try to get <b>out_len</b> bytes of the strongest entropy we can generate, @@ -2762,10 +2799,12 @@ crypto_strongest_rand(uint8_t *out, size_t out_len) while (out_len) { crypto_rand((char*) inp, DLEN); if (crypto_strongest_rand_raw(inp+DLEN, DLEN) < 0) { + // LCOV_EXCL_START log_err(LD_CRYPTO, "Failed to load strong entropy when generating an " "important key. Exiting."); /* Die with an assertion so we get a stack trace. */ tor_assert(0); + // LCOV_EXCL_STOP } if (out_len >= DLEN) { SHA512(inp, sizeof(inp), out); @@ -2796,7 +2835,7 @@ crypto_seed_rng(void) * functions. If one succeeds, we'll accept the RNG as seeded. */ rand_poll_ok = RAND_poll(); if (rand_poll_ok == 0) - log_warn(LD_CRYPTO, "RAND_poll() failed."); + log_warn(LD_CRYPTO, "RAND_poll() failed."); // LCOV_EXCL_LINE load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf)); if (load_entropy_ok) { diff --git a/src/common/crypto.h b/src/common/crypto.h index 682c4e3253..ff38cca0da 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -233,6 +233,7 @@ void crypto_digest_smartlist(char *digest_out, size_t len_out, const struct smartlist_t *lst, const char *append, digest_algorithm_t alg); const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg); +size_t crypto_digest_algorithm_get_length(digest_algorithm_t alg); int crypto_digest_algorithm_parse_name(const char *name); crypto_digest_t *crypto_digest_new(void); crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm); @@ -317,6 +318,7 @@ void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); #ifdef CRYPTO_PRIVATE STATIC int crypto_force_rand_ssleay(void); +STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len); #endif #endif diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c index 57c878b79a..58ec923638 100644 --- a/src/common/crypto_curve25519.c +++ b/src/common/crypto_curve25519.c @@ -65,8 +65,10 @@ STATIC int curve25519_basepoint_impl(uint8_t *output, const uint8_t *secret) { int r = 0; - if (PREDICT_UNLIKELY(curve25519_use_ed == -1)) { + if (BUG(curve25519_use_ed == -1)) { + /* LCOV_EXCL_START - Only reached if we forgot to call curve25519_init() */ pick_curve25519_basepoint_impl(); + /* LCOV_EXCL_STOP */ } /* TODO: Someone should benchmark curved25519_scalarmult_basepoint versus @@ -290,10 +292,13 @@ pick_curve25519_basepoint_impl(void) if (curve25519_basepoint_spot_check() == 0) return; - log_warn(LD_CRYPTO, "The ed25519-based curve25519 basepoint " + /* LCOV_EXCL_START + * only reachable if our basepoint implementation broken */ + log_warn(LD_BUG|LD_CRYPTO, "The ed25519-based curve25519 basepoint " "multiplication seems broken; using the curve25519 " "implementation."); curve25519_use_ed = 0; + /* LCOV_EXCL_STOP */ } /** Initialize the curve25519 implementations. This is necessary if you're diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c index ea2d8e3892..84c3eece6d 100644 --- a/src/common/crypto_ed25519.c +++ b/src/common/crypto_ed25519.c @@ -94,8 +94,8 @@ static const ed25519_impl_t *ed25519_impl = NULL; static inline const ed25519_impl_t * get_ed_impl(void) { - if (PREDICT_UNLIKELY(ed25519_impl == NULL)) { - pick_ed25519_impl(); + if (BUG(ed25519_impl == NULL)) { + pick_ed25519_impl(); // LCOV_EXCL_LINE - We always call ed25519_init(). } return ed25519_impl; } @@ -259,11 +259,11 @@ ed25519_checksig_batch(int *okay_out, int *oks; int all_ok; - ms = tor_malloc(sizeof(uint8_t*)*n_checkable); - lens = tor_malloc(sizeof(size_t)*n_checkable); - pks = tor_malloc(sizeof(uint8_t*)*n_checkable); - sigs = tor_malloc(sizeof(uint8_t*)*n_checkable); - oks = okay_out ? okay_out : tor_malloc(sizeof(int)*n_checkable); + ms = tor_calloc(n_checkable, sizeof(uint8_t*)); + lens = tor_calloc(n_checkable, sizeof(size_t)); + pks = tor_calloc(n_checkable, sizeof(uint8_t*)); + sigs = tor_calloc(n_checkable, sizeof(uint8_t*)); + oks = okay_out ? okay_out : tor_calloc(n_checkable, sizeof(int)); for (i = 0; i < n_checkable; ++i) { ms[i] = checkable[i].msg; @@ -433,6 +433,7 @@ ed25519_seckey_read_from_file(ed25519_secret_key_t *seckey_out, errno = EINVAL; } + tor_free(*tag_out); return -1; } @@ -472,6 +473,7 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, errno = EINVAL; } + tor_free(*tag_out); return -1; } @@ -594,9 +596,12 @@ pick_ed25519_impl(void) if (ed25519_impl_spot_check() == 0) return; + /* LCOV_EXCL_START + * unreachable unless ed25519_donna is broken */ log_warn(LD_CRYPTO, "The Ed25519-donna implementation seems broken; using " "the ref10 implementation."); ed25519_impl = &impl_ref10; + /* LCOV_EXCL_STOP */ } /* Initialize the Ed25519 implementation. This is neccessary if you're diff --git a/src/common/crypto_pwbox.c b/src/common/crypto_pwbox.c index 819dc0c39d..31e37c007d 100644 --- a/src/common/crypto_pwbox.c +++ b/src/common/crypto_pwbox.c @@ -61,7 +61,7 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out, pwbox_encoded_getarray_skey_header(enc), S2K_MAXLEN, s2k_flags); - if (spec_len < 0 || spec_len > S2K_MAXLEN) + if (BUG(spec_len < 0 || spec_len > S2K_MAXLEN)) goto err; pwbox_encoded_setlen_skey_header(enc, spec_len); enc->header_len = spec_len; @@ -76,10 +76,11 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out, /* Now that all the data is in position, derive some keys, encrypt, and * digest */ - if (secret_to_key_derivekey(keys, sizeof(keys), + const int s2k_rv = secret_to_key_derivekey(keys, sizeof(keys), pwbox_encoded_getarray_skey_header(enc), spec_len, - secret, secret_len) < 0) + secret, secret_len); + if (BUG(s2k_rv < 0)) goto err; cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv); @@ -87,11 +88,11 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out, crypto_cipher_free(cipher); result_len = pwbox_encoded_encoded_len(enc); - if (result_len < 0) + if (BUG(result_len < 0)) goto err; result = tor_malloc(result_len); enc_len = pwbox_encoded_encode(result, result_len, enc); - if (enc_len < 0) + if (BUG(enc_len < 0)) goto err; tor_assert(enc_len == result_len); @@ -107,9 +108,24 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out, goto out; err: + /* LCOV_EXCL_START + + This error case is often unreachable if we're correctly coded, unless + somebody adds a new error case somewhere, or unless you're building + without scrypto support. + + - make_specifier can't fail, unless S2K_MAX_LEN is too short. + - secret_to_key_derivekey can't really fail unless we're missing + scrypt, or the underlying function fails, or we pass it a bogus + algorithm or parameters. + - pwbox_encoded_encoded_len can't fail unless we're using trunnel + incorrectly. + - pwbox_encoded_encode can't fail unless we're using trunnel wrong, + or it's buggy. + */ tor_free(result); rv = -1; - + /* LCOV_EXCL_STOP */ out: pwbox_encoded_free(enc); memwipe(keys, 0, sizeof(keys)); diff --git a/src/common/crypto_s2k.c b/src/common/crypto_s2k.c index 3bc05f1cf9..5dbd2ad91f 100644 --- a/src/common/crypto_s2k.c +++ b/src/common/crypto_s2k.c @@ -57,7 +57,8 @@ #define SCRYPT_KEY_LEN 32 /** Given an algorithm ID (one of S2K_TYPE_*), return the length of the - * specifier part of it, without the prefix type byte. */ + * specifier part of it, without the prefix type byte. Return -1 if it is not + * a valid algorithm ID. */ static int secret_to_key_spec_len(uint8_t type) { @@ -86,7 +87,8 @@ secret_to_key_key_len(uint8_t type) case S2K_TYPE_SCRYPT: return DIGEST256_LEN; default: - return -1; + tor_fragile_assert(); // LCOV_EXCL_LINE + return -1; // LCOV_EXCL_LINE } } @@ -168,7 +170,7 @@ make_specifier(uint8_t *spec_out, uint8_t type, unsigned flags) spec_out[SCRYPT_SPEC_LEN-1] = (3u << 4) | (1u << 0); break; default: - tor_fragile_assert(); + tor_fragile_assert(); // LCOV_EXCL_LINE - we should have returned above. return S2K_BAD_ALGORITHM; } diff --git a/src/common/di_ops.c b/src/common/di_ops.c index 5dfe828066..e671af6fac 100644 --- a/src/common/di_ops.c +++ b/src/common/di_ops.c @@ -226,3 +226,48 @@ safe_mem_is_zero(const void *mem, size_t sz) return 1 & ((total - 1) >> 8); } +/** Time-invariant 64-bit greater-than; works on two integers in the range + * (0,INT64_MAX). */ +#if SIZEOF_VOID_P == 8 +#define gt_i64_timei(a,b) ((a) > (b)) +#else +static inline int +gt_i64_timei(uint64_t a, uint64_t b) +{ + int64_t diff = (int64_t) (b - a); + int res = diff >> 63; + return res & 1; +} +#endif + +/** + * Given an array of list of <b>n_entries</b> uint64_t values, whose sum is + * <b>total</b>, find the first i such that the total of all elements 0...i is + * greater than rand_val. + * + * Try to perform this operation in a constant-time way. + */ +int +select_array_member_cumulative_timei(const uint64_t *entries, int n_entries, + uint64_t total, uint64_t rand_val) +{ + int i, i_chosen=-1, n_chosen=0; + uint64_t total_so_far = 0; + + for (i = 0; i < n_entries; ++i) { + total_so_far += entries[i]; + if (gt_i64_timei(total_so_far, rand_val)) { + i_chosen = i; + n_chosen++; + /* Set rand_val to INT64_MAX rather than stopping the loop. This way, + * the time we spend in the loop does not leak which element we chose. */ + rand_val = INT64_MAX; + } + } + tor_assert(total_so_far == total); + tor_assert(n_chosen == 1); + tor_assert(i_chosen >= 0); + tor_assert(i_chosen < n_entries); + + return i_chosen; +} diff --git a/src/common/di_ops.h b/src/common/di_ops.h index 6e77b5cfd7..f1050a00db 100644 --- a/src/common/di_ops.h +++ b/src/common/di_ops.h @@ -42,6 +42,8 @@ void dimap_add_entry(di_digest256_map_t **map, const uint8_t *key, void *val); void *dimap_search(const di_digest256_map_t *map, const uint8_t *key, void *dflt_val); +int select_array_member_cumulative_timei(const uint64_t *entries, int n_entries, + uint64_t total, uint64_t rand_val); #endif diff --git a/src/common/handles.h b/src/common/handles.h new file mode 100644 index 0000000000..1ee2322579 --- /dev/null +++ b/src/common/handles.h @@ -0,0 +1,153 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file handles.h + * \brief Macros for C weak-handle implementation. + * + * A 'handle' is a pointer to an object that is allowed to go away while + * the handle stays alive. When you dereference the handle, you might get + * the object, or you might get "NULL". + * + * Use this pattern when an object has a single obvious lifespan, so you don't + * want to use reference counting, but when other objects might need to refer + * to the first object without caring about its lifetime. + * + * To enable a type to have handles, add a HANDLE_ENTRY() field in its + * definition, as in: + * + * struct walrus { + * HANDLE_ENTRY(wlr, walrus); + * // ... + * }; + * + * And invoke HANDLE_DECL(wlr, walrus, [static]) to declare the handle + * manipulation functions (typically in a header): + * + * // opaque handle to walrus. + * typedef struct wlr_handle_t wlr_handle_t; + * + * // make a new handle + * struct wlr_handle_t *wlr_handle_new(struct walrus *); + * + * // release a handle + * void wlr_handle_free(wlr_handle_t *); + * + * // return the pointed-to walrus, or NULL. + * struct walrus *wlr_handle_get(wlr_handle_t *). + * + * // call this function when you're about to free the walrus; + * // it invalidates all handles. (IF YOU DON'T, YOU WILL HAVE + * // DANGLING REFERENCES) + * void wlr_handles_clear(struct walrus *); + * + * Finally, use HANDLE_IMPL() to define the above functions in some + * appropriate C file: HANDLE_IMPL(wlr, walrus, [static]) + * + **/ + +#ifndef TOR_HANDLE_H +#define TOR_HANDLE_H + +#include "orconfig.h" +#include "tor_queue.h" +#include "util.h" + +#define HANDLE_ENTRY(name, structname) \ + struct name ## _handle_head_t *handle_head + +#define HANDLE_DECL(name, structname, linkage) \ + typedef struct name ## _handle_t name ## _handle_t; \ + linkage name ## _handle_t *name ## _handle_new(struct structname *object); \ + linkage void name ## _handle_free(name ## _handle_t *); \ + linkage struct structname *name ## _handle_get(name ## _handle_t *); \ + linkage void name ## _handles_clear(struct structname *object); + +/* + * Implementation notes: there are lots of possible implementations here. We + * could keep a linked list of handles, each with a backpointer to the object, + * and set all of their backpointers to NULL when the object is freed. Or we + * could have the clear function invalidate the object, but not actually let + * the object get freed until the all the handles went away. We could even + * have a hash-table mapping unique identifiers to objects, and have each + * handle be a copy of the unique identifier. (We'll want to build that last + * one eventually if we want cross-process handles.) + * + * But instead we're opting for a single independent 'head' that knows how + * many handles there are, and where the object is (or isn't). This makes + * all of our functions O(1), and most as fast as a single pointer access. + * + * The handles themselves are opaque structures holding a pointer to the head. + * We could instead have each foo_handle_t* be identical to foo_handle_head_t + * *, and save some allocations ... but doing so would make handle leaks + * harder to debug. As it stands, every handle leak is a memory leak, and + * existing memory debugging tools should help with those. We can revisit + * this decision if handles are too slow. + */ + +#define HANDLE_IMPL(name, structname, linkage) \ + /* The 'head' object for a handle-accessible type. This object */ \ + /* persists for as long as the object, or any handles, exist. */ \ + typedef struct name ## _handle_head_t { \ + struct structname *object; /* pointed-to object, or NULL */ \ + unsigned int references; /* number of existing handles */ \ + } name ## _handle_head_t; \ + \ + struct name ## _handle_t { \ + struct name ## _handle_head_t *head; /* reference to the 'head'. */ \ + }; \ + \ + linkage struct name ## _handle_t * \ + name ## _handle_new(struct structname *object) \ + { \ + tor_assert(object); \ + name ## _handle_head_t *head = object->handle_head; \ + if (PREDICT_UNLIKELY(head == NULL)) { \ + head = object->handle_head = tor_malloc_zero(sizeof(*head)); \ + head->object = object; \ + } \ + name ## _handle_t *new_ref = tor_malloc_zero(sizeof(*new_ref)); \ + new_ref->head = head; \ + ++head->references; \ + return new_ref; \ + } \ + \ + linkage void \ + name ## _handle_free(struct name ## _handle_t *ref) \ + { \ + if (! ref) return; \ + name ## _handle_head_t *head = ref->head; \ + tor_assert(head); \ + --head->references; \ + tor_free(ref); \ + if (head->object == NULL && head->references == 0) { \ + tor_free(head); \ + return; \ + } \ + } \ + \ + linkage struct structname * \ + name ## _handle_get(struct name ## _handle_t *ref) \ + { \ + tor_assert(ref); \ + name ## _handle_head_t *head = ref->head; \ + tor_assert(head); \ + return head->object; \ + } \ + \ + linkage void \ + name ## _handles_clear(struct structname *object) \ + { \ + tor_assert(object); \ + name ## _handle_head_t *head = object->handle_head; \ + if (! head) \ + return; \ + object->handle_head = NULL; \ + head->object = NULL; \ + if (head->references == 0) { \ + tor_free(head); \ + } \ + } + +#endif /* TOR_HANDLE_H */ + diff --git a/src/common/include.am b/src/common/include.am index 5afb30da6a..222afe0291 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -1,12 +1,14 @@ noinst_LIBRARIES += \ src/common/libor.a \ + src/common/libor-ctime.a \ src/common/libor-crypto.a \ src/common/libor-event.a if UNITTESTS_ENABLED noinst_LIBRARIES += \ src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ src/common/libor-crypto-testing.a \ src/common/libor-event-testing.a endif @@ -27,12 +29,14 @@ src_common_libcurve25519_donna_a_CFLAGS= if BUILD_CURVE25519_DONNA src_common_libcurve25519_donna_a_SOURCES=\ src/ext/curve25519_donna/curve25519-donna.c +# See bug 13538 -- this code is known to have signed overflow issues. src_common_libcurve25519_donna_a_CFLAGS+=\ - @F_OMIT_FRAME_POINTER@ + @F_OMIT_FRAME_POINTER@ @CFLAGS_CONSTTIME@ noinst_LIBRARIES+=src/common/libcurve25519_donna.a LIBDONNA=src/common/libcurve25519_donna.a else if BUILD_CURVE25519_DONNA_C64 +src_common_libcurve25519_donna_a_CFLAGS+=@CFLAGS_CONSTTIME@ src_common_libcurve25519_donna_a_SOURCES=\ src/ext/curve25519_donna/curve25519-donna-c64.c noinst_LIBRARIES+=src/common/libcurve25519_donna.a @@ -58,22 +62,37 @@ else readpassphrase_source= endif -LIBOR_A_SOURCES = \ +if ADD_MULODI4 +mulodi4_source=src/ext/mulodi/mulodi4.c +else +mulodi4_source= +endif + +LIBOR_CTIME_A_SRC = \ + $(mulodi4_source) \ + src/ext/csiphash.c \ + src/common/di_ops.c + +src_common_libor_ctime_a_SOURCES = $(LIBOR_CTIME_A_SRC) +src_common_libor_ctime_testing_a_SOURCES = $(LIBOR_CTIME_A_SRC) +src_common_libor_ctime_a_CFLAGS = @CFLAGS_CONSTTIME@ +src_common_libor_ctime_testing_a_CFLAGS = @CFLAGS_CONSTTIME@ $(TEST_CFLAGS) + +LIBOR_A_SRC = \ src/common/address.c \ src/common/backtrace.c \ src/common/compat.c \ src/common/compat_threads.c \ src/common/container.c \ - src/common/di_ops.c \ src/common/log.c \ src/common/memarea.c \ + src/common/pubsub.c \ src/common/util.c \ + src/common/util_bug.c \ src/common/util_format.c \ src/common/util_process.c \ src/common/sandbox.c \ src/common/workqueue.c \ - src/ext/csiphash.c \ - src/ext/trunnel/trunnel.c \ $(libor_extra_source) \ $(threads_impl_source) \ $(readpassphrase_source) @@ -81,7 +100,7 @@ LIBOR_A_SOURCES = \ src/common/src_common_libor_testing_a-log.$(OBJEXT) \ src/common/log.$(OBJEXT): micro-revision.i -LIBOR_CRYPTO_A_SOURCES = \ +LIBOR_CRYPTO_A_SRC = \ src/common/aes.c \ src/common/crypto.c \ src/common/crypto_pwbox.c \ @@ -89,21 +108,22 @@ LIBOR_CRYPTO_A_SOURCES = \ src/common/crypto_format.c \ src/common/torgzip.c \ src/common/tortls.c \ - src/trunnel/pwbox.c \ src/common/crypto_curve25519.c \ src/common/crypto_ed25519.c -LIBOR_EVENT_A_SOURCES = \ +LIBOR_EVENT_A_SRC = \ src/common/compat_libevent.c \ - src/common/procmon.c + src/common/procmon.c \ + src/common/timers.c \ + src/ext/timeouts/timeout.c -src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES) -src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES) -src_common_libor_event_a_SOURCES = $(LIBOR_EVENT_A_SOURCES) +src_common_libor_a_SOURCES = $(LIBOR_A_SRC) +src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SRC) +src_common_libor_event_a_SOURCES = $(LIBOR_EVENT_A_SRC) -src_common_libor_testing_a_SOURCES = $(LIBOR_A_SOURCES) -src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES) -src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SOURCES) +src_common_libor_testing_a_SOURCES = $(LIBOR_A_SRC) +src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SRC) +src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SRC) src_common_libor_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_common_libor_crypto_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) @@ -129,16 +149,20 @@ COMMONHEADERS = \ src/common/crypto_pwbox.h \ src/common/crypto_s2k.h \ src/common/di_ops.h \ + src/common/handles.h \ src/common/memarea.h \ src/common/linux_syscalls.inc \ src/common/procmon.h \ + src/common/pubsub.h \ src/common/sandbox.h \ src/common/testsupport.h \ + src/common/timers.h \ src/common/torgzip.h \ src/common/torint.h \ src/common/torlog.h \ src/common/tortls.h \ src/common/util.h \ + src/common/util_bug.h \ src/common/util_format.h \ src/common/util_process.h \ src/common/workqueue.h diff --git a/src/common/memarea.c b/src/common/memarea.c index 173ed4e1cb..7d16b702e3 100644 --- a/src/common/memarea.c +++ b/src/common/memarea.c @@ -131,7 +131,7 @@ alloc_chunk(size_t sz) /** Release <b>chunk</b> from a memarea. */ static void -chunk_free_unchecked(memarea_chunk_t *chunk) +memarea_chunk_free_unchecked(memarea_chunk_t *chunk) { CHECK_SENTINEL(chunk); tor_free(chunk); @@ -154,7 +154,7 @@ memarea_drop_all(memarea_t *area) memarea_chunk_t *chunk, *next; for (chunk = area->first; chunk; chunk = next) { next = chunk->next_chunk; - chunk_free_unchecked(chunk); + memarea_chunk_free_unchecked(chunk); } area->first = NULL; /*fail fast on */ tor_free(area); @@ -170,7 +170,7 @@ memarea_clear(memarea_t *area) if (area->first->next_chunk) { for (chunk = area->first->next_chunk; chunk; chunk = next) { next = chunk->next_chunk; - chunk_free_unchecked(chunk); + memarea_chunk_free_unchecked(chunk); } area->first->next_chunk = NULL; } diff --git a/src/common/procmon.c b/src/common/procmon.c index 12d53fcd41..4ecee26e8d 100644 --- a/src/common/procmon.c +++ b/src/common/procmon.c @@ -116,11 +116,11 @@ struct tor_process_monitor_t { * periodically check whether the process we have a handle to has * ended. */ HANDLE hproc; - /* XXX023 We can and should have Libevent watch hproc for us, - * if/when some version of Libevent 2.x can be told to do so. */ + /* XXXX We should have Libevent watch hproc for us, + * if/when some version of Libevent can be told to do so. */ #endif - /* XXX023 On Linux, we can and should receive the 22nd + /* XXXX On Linux, we can and should receive the 22nd * (space-delimited) field (‘starttime’) of /proc/$PID/stat from the * owning controller and store it, and poll once in a while to see * whether it has changed -- if so, the kernel has *definitely* @@ -130,7 +130,8 @@ struct tor_process_monitor_t { * systems whose admins have mounted procfs, or the start-time field * of the process-information structure returned by kvmgetprocs() on * any system. The latter is ickier. */ - /* XXX023 On FreeBSD (and possibly other kqueue systems), we can and + + /* XXXX On FreeBSD (and possibly other kqueue systems), we can and * should arrange to receive EVFILT_PROC NOTE_EXIT notifications for * pid, so we don't have to do such a heavyweight poll operation in * order to avoid the PID-reassignment race condition. (We would diff --git a/src/common/pubsub.c b/src/common/pubsub.c new file mode 100644 index 0000000000..b3faf40e00 --- /dev/null +++ b/src/common/pubsub.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file pubsub.c + * + * \brief DOCDOC + */ + +#include "orconfig.h" +#include "pubsub.h" +#include "container.h" + +/** Helper: insert <b>s</b> into <b>topic's</b> list of subscribers, keeping + * them sorted in priority order. */ +static void +subscriber_insert(pubsub_topic_t *topic, pubsub_subscriber_t *s) +{ + int i; + smartlist_t *sl = topic->subscribers; + for (i = 0; i < smartlist_len(sl); ++i) { + pubsub_subscriber_t *other = smartlist_get(sl, i); + if (s->priority < other->priority) { + break; + } + } + smartlist_insert(sl, i, s); +} + +/** + * Add a new subscriber to <b>topic</b>, where (when an event is triggered), + * we'll notify the function <b>fn</b> by passing it <b>subscriber_data</b>. + * Return a handle to the subscribe which can later be passed to + * pubsub_unsubscribe_(). + * + * Functions are called in priority order, from lowest to highest. + * + * See pubsub.h for <b>subscribe_flags</b>. + */ +const pubsub_subscriber_t * +pubsub_subscribe_(pubsub_topic_t *topic, + pubsub_subscriber_fn_t fn, + void *subscriber_data, + unsigned subscribe_flags, + unsigned priority) +{ + tor_assert(! topic->locked); + if (subscribe_flags & SUBSCRIBE_ATSTART) { + tor_assert(topic->n_events_fired == 0); + } + pubsub_subscriber_t *r = tor_malloc_zero(sizeof(*r)); + r->priority = priority; + r->subscriber_flags = subscribe_flags; + r->fn = fn; + r->subscriber_data = subscriber_data; + if (topic->subscribers == NULL) { + topic->subscribers = smartlist_new(); + } + subscriber_insert(topic, r); + return r; +} + +/** + * Remove the subscriber <b>s</b> from <b>topic</b>. After calling this + * function, <b>s</b> may no longer be used. + */ +int +pubsub_unsubscribe_(pubsub_topic_t *topic, + const pubsub_subscriber_t *s) +{ + tor_assert(! topic->locked); + smartlist_t *sl = topic->subscribers; + if (sl == NULL) + return -1; + int i = smartlist_pos(sl, s); + if (i == -1) + return -1; + pubsub_subscriber_t *tmp = smartlist_get(sl, i); + tor_assert(tmp == s); + smartlist_del_keeporder(sl, i); + tor_free(tmp); + return 0; +} + +/** + * For every subscriber s in <b>topic</b>, invoke notify_fn on s and + * event_data. Return 0 if there were no nonzero return values, and -1 if + * there were any. + */ +int +pubsub_notify_(pubsub_topic_t *topic, pubsub_notify_fn_t notify_fn, + void *event_data, unsigned notify_flags) +{ + tor_assert(! topic->locked); + (void) notify_flags; + smartlist_t *sl = topic->subscribers; + int n_bad = 0; + ++topic->n_events_fired; + if (sl == NULL) + return -1; + topic->locked = 1; + SMARTLIST_FOREACH_BEGIN(sl, pubsub_subscriber_t *, s) { + int r = notify_fn(s, event_data); + if (r != 0) + ++n_bad; + } SMARTLIST_FOREACH_END(s); + topic->locked = 0; + return (n_bad == 0) ? 0 : -1; +} + +/** + * Release all storage held by <b>topic</b>. + */ +void +pubsub_clear_(pubsub_topic_t *topic) +{ + tor_assert(! topic->locked); + + smartlist_t *sl = topic->subscribers; + if (sl == NULL) + return; + SMARTLIST_FOREACH_BEGIN(sl, pubsub_subscriber_t *, s) { + tor_free(s); + } SMARTLIST_FOREACH_END(s); + smartlist_free(sl); + topic->subscribers = NULL; + topic->n_events_fired = 0; +} + diff --git a/src/common/pubsub.h b/src/common/pubsub.h new file mode 100644 index 0000000000..09e492ec4f --- /dev/null +++ b/src/common/pubsub.h @@ -0,0 +1,177 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file pubsub.h + * \brief Macros to implement publish/subscribe abstractions. + * + * To use these macros, call DECLARE_PUBSUB_TOPIC() with an identifier to use + * as your topic. Below, I'm going to assume you say DECLARE_PUBSUB_TOPIC(T). + * + * Doing this will declare the following types: + * typedef struct T_event_data_t T_event_data_t; // you define this struct + * typedef struct T_subscriber_data_t T_subscriber_data_t; // this one too. + * typedef struct T_subscriber_t T_subscriber_t; // opaque + * typedef int (*T_subscriber_fn_t)(T_event_data_t*, T_subscriber_data_t*); + * + * and it will declare the following functions: + * const T_subscriber_t *T_subscribe(T_subscriber_fn_t, + * T_subscriber_data_t *, + * unsigned flags, + * unsigned priority); + * int T_unsubscribe(const T_subscriber_t *) + * + * Elsewhere you can say DECLARE_NOTIFY_PUBSUB_TOPIC(static, T), which declares: + * static int T_notify(T_event_data_t *, unsigned notify_flags); + * static void T_clear(void); + * + * And in some C file, you would define these functions with: + * IMPLEMENT_PUBSUB_TOPIC(static, T). + * + * The implementations will be small typesafe wrappers over generic versions + * of the above functions. + * + * To use the typesafe functions, you add any number of subscribers with + * T_subscribe(). Each has an associated function pointer, data pointer, + * and priority. Later, you can invoke T_notify() to declare that the + * event has occurred. Each of the subscribers will be invoked once. + **/ + +#ifndef TOR_PUBSUB_H +#define TOR_PUBSUB_H + +#include "torint.h" + +/** + * Flag for T_subscribe: die with an assertion failure if the event + * have ever been published before. Used when a subscriber must absolutely + * never have missed an event. + */ +#define SUBSCRIBE_ATSTART (1u<<0) + +#define DECLARE_PUBSUB_STRUCT_TYPES(name) \ + /* You define this type. */ \ + typedef struct name ## _event_data_t name ## _event_data_t; \ + /* You define this type. */ \ + typedef struct name ## _subscriber_data_t name ## _subscriber_data_t; + +#define DECLARE_PUBSUB_TOPIC(name) \ + /* This type is opaque. */ \ + typedef struct name ## _subscriber_t name ## _subscriber_t; \ + /* You declare functions matching this type. */ \ + typedef int (*name ## _subscriber_fn_t)( \ + name ## _event_data_t *data, \ + name ## _subscriber_data_t *extra); \ + /* Call this function to subscribe to a topic. */ \ + const name ## _subscriber_t *name ## _subscribe( \ + name##_subscriber_fn_t subscriber, \ + name##_subscriber_data_t *extra_data, \ + unsigned flags, \ + unsigned priority); \ + /* Call this function to unsubscribe from a topic. */ \ + int name ## _unsubscribe(const name##_subscriber_t *s); + +#define DECLARE_NOTIFY_PUBSUB_TOPIC(linkage, name) \ + /* Call this function to notify all subscribers. Flags not yet used. */ \ + linkage int name ## _notify(name ## _event_data_t *data, unsigned flags); \ + /* Call this function to release storage held by the topic. */ \ + linkage void name ## _clear(void); + +/** + * Type used to hold a generic function for a subscriber. + * + * [Yes, it is safe to cast to this, so long as we cast back to the original + * type before calling. From C99: "A pointer to a function of one type may be + * converted to a pointer to a function of another type and back again; the + * result shall compare equal to the original pointer."] +*/ +typedef int (*pubsub_subscriber_fn_t)(void *, void *); + +/** + * Helper type to implement pubsub abstraction. Don't use this directly. + * It represents a subscriber. + */ +typedef struct pubsub_subscriber_t { + /** Function to invoke when the event triggers. */ + pubsub_subscriber_fn_t fn; + /** Data associated with this subscriber. */ + void *subscriber_data; + /** Priority for this subscriber. Low priorities happen first. */ + unsigned priority; + /** Flags set on this subscriber. Not yet used.*/ + unsigned subscriber_flags; +} pubsub_subscriber_t; + +/** + * Helper type to implement pubsub abstraction. Don't use this directly. + * It represents a topic, and keeps a record of subscribers. + */ +typedef struct pubsub_topic_t { + /** List of subscribers to this topic. May be NULL. */ + struct smartlist_t *subscribers; + /** Total number of times that pubsub_notify_() has ever been called on this + * topic. */ + uint64_t n_events_fired; + /** True iff we're running 'notify' on this topic, and shouldn't allow + * any concurrent modifications or events. */ + unsigned locked; +} pubsub_topic_t; + +const pubsub_subscriber_t *pubsub_subscribe_(pubsub_topic_t *topic, + pubsub_subscriber_fn_t fn, + void *subscriber_data, + unsigned subscribe_flags, + unsigned priority); +int pubsub_unsubscribe_(pubsub_topic_t *topic, const pubsub_subscriber_t *sub); +void pubsub_clear_(pubsub_topic_t *topic); +typedef int (*pubsub_notify_fn_t)(pubsub_subscriber_t *subscriber, + void *notify_data); +int pubsub_notify_(pubsub_topic_t *topic, pubsub_notify_fn_t notify_fn, + void *notify_data, unsigned notify_flags); + +#define IMPLEMENT_PUBSUB_TOPIC(notify_linkage, name) \ + static pubsub_topic_t name ## _topic_ = { NULL, 0, 0 }; \ + const name ## _subscriber_t * \ + name ## _subscribe(name##_subscriber_fn_t subscriber, \ + name##_subscriber_data_t *extra_data, \ + unsigned flags, \ + unsigned priority) \ + { \ + const pubsub_subscriber_t *s; \ + s = pubsub_subscribe_(&name##_topic_, \ + (pubsub_subscriber_fn_t)subscriber, \ + extra_data, \ + flags, \ + priority); \ + return (const name##_subscriber_t *)s; \ + } \ + int \ + name ## _unsubscribe(const name##_subscriber_t *subscriber) \ + { \ + return pubsub_unsubscribe_(&name##_topic_, \ + (const pubsub_subscriber_t *)subscriber); \ + } \ + static int \ + name##_call_the_notify_fn_(pubsub_subscriber_t *subscriber, \ + void *notify_data) \ + { \ + name ## _subscriber_fn_t fn; \ + fn = (name ## _subscriber_fn_t) subscriber->fn; \ + return fn(notify_data, subscriber->subscriber_data); \ + } \ + notify_linkage int \ + name ## _notify(name ## _event_data_t *event_data, unsigned flags) \ + { \ + return pubsub_notify_(&name##_topic_, \ + name##_call_the_notify_fn_, \ + event_data, \ + flags); \ + } \ + notify_linkage void \ + name ## _clear(void) \ + { \ + pubsub_clear_(&name##_topic_); \ + } + +#endif /* TOR_PUBSUB_H */ + diff --git a/src/common/timers.c b/src/common/timers.c new file mode 100644 index 0000000000..5d8d1feafd --- /dev/null +++ b/src/common/timers.c @@ -0,0 +1,297 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file timers.c + * \brief Wrapper around William Ahern's fast hierarchical timer wheel + * implementation, to tie it in with a libevent backend. + * + * Only use these functions from the main thread. + * + * The main advantage of tor_timer_t over using libevent's timers is that + * they're way more efficient if we need to have thousands or millions of + * them. For more information, see + * http://www.25thandclement.com/~william/projects/timeout.c.html + * + * Periodic timers are available in the backend, but I've turned them off. + * We can turn them back on if needed. + */ + +/* Notes: + * + * The use of tor_gettimeofday_cached_monotonic() is kind of ugly. It would + * be neat to fix it. + * + * Having a way to free all timers on shutdown would free people from the + * need to track them. Not sure if that's clever though. + * + * In an ideal world, Libevent would just switch to use this backend, and we + * could throw this file away. But even if Libevent does switch, we'll be + * stuck with legacy libevents for some time. + */ + +#include "orconfig.h" + +#include "compat.h" +#include "compat_libevent.h" +#include "timers.h" +#include "torlog.h" +#include "util.h" + +#ifdef HAVE_EVENT2_EVENT_H +#include <event2/event.h> +#else +#include <event.h> +#endif + +struct timeout_cb { + timer_cb_fn_t cb; + void *arg; +}; + +/* + * These definitions are for timeouts.c and timeouts.h. + */ +#ifdef __GNUC__ +/* We're not exposing any of the functions outside this file. */ +#define TIMEOUT_PUBLIC __attribute__((__unused__)) static +#else +/* We're not exposing any of the functions outside this file. */ +#define TIMEOUT_PUBLIC static +#endif +/* We're not using periodic events. */ +#define TIMEOUT_DISABLE_INTERVALS +/* We always know the global_timeouts object, so we don't need each timeout + * to keep a pointer to it. */ +#define TIMEOUT_DISABLE_RELATIVE_ACCESS +/* We're providing our own struct timeout_cb. */ +#define TIMEOUT_CB_OVERRIDE +/* We're going to support timers that are pretty far out in advance. Making + * this big can be inefficient, but having a significant number of timers + * above TIMEOUT_MAX can also be super-inefficent. Choosing 5 here sets + * timeout_max to 2^30 ticks, or 29 hours with our value for USEC_PER_TICK */ +#define WHEEL_NUM 5 +#include "src/ext/timeouts/timeout.c" + +static struct timeouts *global_timeouts = NULL; +static struct event *global_timer_event = NULL; + +/** We need to choose this value carefully. Because we're using timer wheels, + * it actually costs us to have extra resolution we don't use. So for now, + * I'm going to define our resolution as .1 msec, and hope that's good enough. + * + * Note that two of the most popular libevent backends (epoll without timerfd, + * and windows select), simply can't support sub-millisecond resolution, + * do this is optimistic for a lot of users. + */ +#define USEC_PER_TICK 100 + +/** One million microseconds in a second */ +#define USEC_PER_SEC 1000000 + +/** Check at least once every N seconds. */ +#define MIN_CHECK_SECONDS 3600 + +/** Check at least once every N ticks. */ +#define MIN_CHECK_TICKS \ + (((timeout_t)MIN_CHECK_SECONDS) * (1000000 / USEC_PER_TICK)) + +/** + * Convert the timeval in <b>tv</b> to a timeout_t, and return it. + * + * The output resolution is set by USEC_PER_TICK, and the time corresponding + * to 0 is the same as the time corresponding to 0 from + * tor_gettimeofday_cached_monotonic(). + */ +static timeout_t +tv_to_timeout(const struct timeval *tv) +{ + uint64_t usec = tv->tv_usec; + usec += ((uint64_t)USEC_PER_SEC) * tv->tv_sec; + return usec / USEC_PER_TICK; +} + +/** + * Convert the timeout in <b>t</b> to a timeval in <b>tv_out</b> + */ +static void +timeout_to_tv(timeout_t t, struct timeval *tv_out) +{ + t *= USEC_PER_TICK; + tv_out->tv_usec = (int)(t % USEC_PER_SEC); + tv_out->tv_sec = (time_t)(t / USEC_PER_SEC); +} + +/** + * Update the timer <b>tv</b> to the current time in <b>tv</b>. + */ +static void +timer_advance_to_cur_time(const struct timeval *tv) +{ + timeout_t cur_tick = tv_to_timeout(tv); + if (BUG(cur_tick < timeouts_get_curtime(global_timeouts))) { + cur_tick = timeouts_get_curtime(global_timeouts); // LCOV_EXCL_LINE + } + timeouts_update(global_timeouts, cur_tick); +} + +/** + * Adjust the time at which the libevent timer should fire based on + * the next-expiring time in <b>global_timeouts</b> + */ +static void +libevent_timer_reschedule(void) +{ + struct timeval now; + tor_gettimeofday_cached_monotonic(&now); + timer_advance_to_cur_time(&now); + + timeout_t delay = timeouts_timeout(global_timeouts); + struct timeval d; + if (delay > MIN_CHECK_TICKS) + delay = MIN_CHECK_TICKS; + timeout_to_tv(delay, &d); + event_add(global_timer_event, &d); +} + +/** + * Invoked when the libevent timer has expired: see which tor_timer_t events + * have fired, activate their callbacks, and reschedule the libevent timer. + */ +static void +libevent_timer_callback(evutil_socket_t fd, short what, void *arg) +{ + (void)fd; + (void)what; + (void)arg; + + struct timeval now; + tor_gettimeofday_cache_clear(); + tor_gettimeofday_cached_monotonic(&now); + timer_advance_to_cur_time(&now); + + tor_timer_t *t; + while ((t = timeouts_get(global_timeouts))) { + t->callback.cb(t, t->callback.arg, &now); + } + + tor_gettimeofday_cache_clear(); + libevent_timer_reschedule(); +} + +/** + * Initialize the timers subsystem. Requires that libevent has already been + * initialized. + */ +void +timers_initialize(void) +{ + if (BUG(global_timeouts)) + return; // LCOV_EXCL_LINE + + timeout_error_t err; + global_timeouts = timeouts_open(0, &err); + if (!global_timeouts) { + // LCOV_EXCL_START -- this can only fail on malloc failure. + log_err(LD_BUG, "Unable to open timer backend: %s", strerror(err)); + tor_assert(0); + // LCOV_EXCL_STOP + } + + struct event *timer_event; + timer_event = tor_event_new(tor_libevent_get_base(), + -1, 0, libevent_timer_callback, NULL); + tor_assert(timer_event); + global_timer_event = timer_event; + + libevent_timer_reschedule(); +} + +/** + * Release all storage held in the timers subsystem. Does not fire timers. + */ +void +timers_shutdown(void) +{ + if (global_timer_event) { + tor_event_free(global_timer_event); + global_timer_event = NULL; + } + if (global_timeouts) { + timeouts_close(global_timeouts); + global_timeouts = NULL; + } +} + +/** + * Allocate and return a new timer, with given callback and argument. + */ +tor_timer_t * +timer_new(timer_cb_fn_t cb, void *arg) +{ + tor_timer_t *t = tor_malloc(sizeof(tor_timer_t)); + timeout_init(t, 0); + timer_set_cb(t, cb, arg); + return t; +} + +/** + * Release all storage held by <b>t</b>, and unschedule it if was already + * scheduled. + */ +void +timer_free(tor_timer_t *t) +{ + if (! t) + return; + + timeouts_del(global_timeouts, t); + tor_free(t); +} + +/** + * Change the callback and argument associated with a timer <b>t</b>. + */ +void +timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg) +{ + t->callback.cb = cb; + t->callback.arg = arg; +} + +/** + * Schedule the timer t to fire at the current time plus a delay of <b>tv</b>. + * All times are relative to tor_gettimeofday_cached_monotonic. + */ +void +timer_schedule(tor_timer_t *t, const struct timeval *tv) +{ + const timeout_t when = tv_to_timeout(tv); + struct timeval now; + tor_gettimeofday_cached_monotonic(&now); + timer_advance_to_cur_time(&now); + + /* Take the old timeout value. */ + timeout_t to = timeouts_timeout(global_timeouts); + + timeouts_add(global_timeouts, t, when); + + /* Should we update the libevent timer? */ + if (to <= when) { + return; /* we're already going to fire before this timer would trigger. */ + } + libevent_timer_reschedule(); +} + +/** + * Cancel the timer <b>t</b> if it is currently scheduled. (It's okay to call + * this on an unscheduled timer. + */ +void +timer_disable(tor_timer_t *t) +{ + timeouts_del(global_timeouts, t); + /* We don't reschedule the libevent timer here, since it's okay if it fires + * early. */ +} + diff --git a/src/common/timers.h b/src/common/timers.h new file mode 100644 index 0000000000..594cf38a64 --- /dev/null +++ b/src/common/timers.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_TIMERS_H +#define TOR_TIMERS_H + +#include "orconfig.h" +#include "testsupport.h" + +typedef struct timeout tor_timer_t; +typedef void (*timer_cb_fn_t)(tor_timer_t *, void *, const struct timeval *); +tor_timer_t *timer_new(timer_cb_fn_t cb, void *arg); +void timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg); +void timer_schedule(tor_timer_t *t, const struct timeval *delay); +void timer_disable(tor_timer_t *t); +void timer_free(tor_timer_t *t); + +void timers_initialize(void); +void timers_shutdown(void); + +#endif + diff --git a/src/common/util.c b/src/common/util.c index f3effe0957..78afe5954f 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -105,23 +105,6 @@ #endif /* ===== - * Assertion helper. - * ===== */ -/** Helper for tor_assert: report the assertion failure. */ -void -tor_assertion_failed_(const char *fname, unsigned int line, - const char *func, const char *expr) -{ - char buf[256]; - log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.", - fname, line, func, expr); - tor_snprintf(buf, sizeof(buf), - "Assertion %s failed in %s at %s:%u", - expr, func, fname, line); - log_backtrace(LOG_ERR, LD_BUG, buf); -} - -/* ===== * Memory management * ===== */ #ifdef USE_DMALLOC @@ -530,21 +513,6 @@ round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor) return number; } -/** Return the lowest x in [INT64_MIN, INT64_MAX] such that x is at least - * <b>number</b>, and x modulo <b>divisor</b> == 0. If no such x can be - * expressed as an int64_t, return INT64_MAX */ -int64_t -round_int64_to_next_multiple_of(int64_t number, int64_t divisor) -{ - tor_assert(divisor > 0); - if (INT64_MAX - divisor + 1 < number) - return INT64_MAX; - if (number >= 0) - number += divisor - 1; - number -= number % divisor; - return number; -} - /** Transform a random value <b>p</b> from the uniform distribution in * [0.0, 1.0[ into a Laplace distributed value with location parameter * <b>mu</b> and scale parameter <b>b</b>. Truncate the final result @@ -3079,7 +3047,7 @@ digit_to_num(char d) * success, store the result in <b>out</b>, advance bufp to the next * character, and return 0. On failure, return -1. */ static int -scan_unsigned(const char **bufp, unsigned long *out, int width, int base) +scan_unsigned(const char **bufp, unsigned long *out, int width, unsigned base) { unsigned long result = 0; int scanned_so_far = 0; @@ -3092,7 +3060,7 @@ scan_unsigned(const char **bufp, unsigned long *out, int width, int base) while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp)) && scanned_so_far < width) { - int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++); + unsigned digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++); // Check for overflow beforehand, without actually causing any overflow // This preserves functionality on compilers that don't wrap overflow // (i.e. that trap or optimise away overflow) @@ -3138,14 +3106,15 @@ scan_signed(const char **bufp, long *out, int width) if (neg && result > 0) { if (result > ((unsigned long)LONG_MAX) + 1) return -1; /* Underflow */ - // Avoid overflow on the cast to signed long when result is LONG_MIN - // by subtracting 1 from the unsigned long positive value, - // then, after it has been cast to signed and negated, - // subtracting the original 1 (the double-subtraction is intentional). - // Otherwise, the cast to signed could cause a temporary long - // to equal LONG_MAX + 1, which is undefined. - // We avoid underflow on the subtraction by treating -0 as positive. - *out = (-(long)(result - 1)) - 1; + else if (result == ((unsigned long)LONG_MAX) + 1) + *out = LONG_MIN; + else { + /* We once had a far more clever no-overflow conversion here, but + * some versions of GCC apparently ran it into the ground. Now + * we just check for LONG_MIN explicitly. + */ + *out = -(long)result; + } } else { if (result > LONG_MAX) return -1; /* Overflow */ @@ -3291,8 +3260,10 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap) *out = lng; } else { int *out = va_arg(ap, int *); +#if LONG_MAX > INT_MAX if (lng < INT_MIN || lng > INT_MAX) return n_matched; +#endif *out = (int)lng; } ++pattern; diff --git a/src/common/util.h b/src/common/util.h index ebcf88b32d..4c5070e65b 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -22,6 +22,7 @@ /* for the correct alias to struct stat */ #include <sys/stat.h> #endif +#include "util_bug.h" #ifndef O_BINARY #define O_BINARY 0 @@ -33,41 +34,6 @@ #define O_NOFOLLOW 0 #endif -/* Replace assert() with a variant that sends failures to the log before - * calling assert() normally. - */ -#ifdef NDEBUG -/* Nobody should ever want to build with NDEBUG set. 99% of our asserts will - * be outside the critical path anyway, so it's silly to disable bug-checking - * throughout the entire program just because a few asserts are slowing you - * down. Profile, optimize the critical path, and keep debugging on. - * - * And I'm not just saying that because some of our asserts check - * security-critical properties. - */ -#error "Sorry; we don't support building with NDEBUG." -#endif - -/* Sometimes we don't want to use assertions during branch coverage tests; it - * leads to tons of unreached branches which in reality are only assertions we - * didn't hit. */ -#if defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS) -#define tor_assert(a) STMT_BEGIN \ - (void)(a); \ - STMT_END -#else -/** Like assert(3), but send assertion failures to the log as well as to - * stderr. */ -#define tor_assert(expr) STMT_BEGIN \ - if (PREDICT_UNLIKELY(!(expr))) { \ - tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \ - abort(); \ - } STMT_END -#endif - -void tor_assertion_failed_(const char *fname, unsigned int line, - const char *func, const char *expr); - /* If we're building with dmalloc, we want all of our memory allocation * functions to take an extra file/line pair of arguments. If not, not. * We define DMALLOC_PARAMS to the extra parameters to insert in the @@ -81,11 +47,6 @@ void tor_assertion_failed_(const char *fname, unsigned int line, #define DMALLOC_ARGS #endif -/** Define this if you want Tor to crash when any problem comes up, - * so you can get a coredump and track things down. */ -// #define tor_fragile_assert() tor_assert(0) -#define tor_fragile_assert() - /* Memory management */ void *tor_malloc_(size_t size DMALLOC_PARAMS) ATTR_MALLOC; void *tor_malloc_zero_(size_t size DMALLOC_PARAMS) ATTR_MALLOC; @@ -184,7 +145,6 @@ uint64_t round_to_power_of_2(uint64_t u64); unsigned round_to_next_multiple_of(unsigned number, unsigned divisor); uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor); uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor); -int64_t round_int64_to_next_multiple_of(int64_t number, int64_t divisor); int64_t sample_laplace_distribution(double mu, double b, double p); int64_t add_laplace_noise(int64_t signal, double random, double delta_f, double epsilon); diff --git a/src/common/util_bug.c b/src/common/util_bug.c new file mode 100644 index 0000000000..e3e1d6df90 --- /dev/null +++ b/src/common/util_bug.c @@ -0,0 +1,53 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file util_bug.c + **/ + +#include "orconfig.h" +#include "util_bug.h" +#include "torlog.h" +#include "backtrace.h" + +/** Helper for tor_assert: report the assertion failure. */ +void +tor_assertion_failed_(const char *fname, unsigned int line, + const char *func, const char *expr) +{ + char buf[256]; + log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.", + fname, line, func, expr); + tor_snprintf(buf, sizeof(buf), + "Assertion %s failed in %s at %s:%u", + expr, func, fname, line); + log_backtrace(LOG_ERR, LD_BUG, buf); +} + +/** Helper for tor_assert_nonfatal: report the assertion failure. */ +void +tor_bug_occurred_(const char *fname, unsigned int line, + const char *func, const char *expr, + int once) +{ + char buf[256]; + const char *once_str = once ? + " (Future instances of this warning will be silenced.)": ""; + if (! expr) { + log_warn(LD_BUG, "%s:%u: %s: This line should not have been reached.%s", + fname, line, func, once_str); + tor_snprintf(buf, sizeof(buf), + "Line unexpectedly reached at %s at %s:%u", + func, fname, line); + } else { + log_warn(LD_BUG, "%s:%u: %s: Non-fatal assertion %s failed.%s", + fname, line, func, expr, once_str); + tor_snprintf(buf, sizeof(buf), + "Non-fatal assertion %s failed in %s at %s:%u", + expr, func, fname, line); + } + log_backtrace(LOG_WARN, LD_BUG, buf); +} + diff --git a/src/common/util_bug.h b/src/common/util_bug.h new file mode 100644 index 0000000000..36056aa4bd --- /dev/null +++ b/src/common/util_bug.h @@ -0,0 +1,150 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file util_bug.h + **/ + +#ifndef TOR_UTIL_BUG_H +#define TOR_UTIL_BUG_H + +#include "orconfig.h" +#include "compat.h" +#include "testsupport.h" + +/* Replace assert() with a variant that sends failures to the log before + * calling assert() normally. + */ +#ifdef NDEBUG +/* Nobody should ever want to build with NDEBUG set. 99% of our asserts will + * be outside the critical path anyway, so it's silly to disable bug-checking + * throughout the entire program just because a few asserts are slowing you + * down. Profile, optimize the critical path, and keep debugging on. + * + * And I'm not just saying that because some of our asserts check + * security-critical properties. + */ +#error "Sorry; we don't support building with NDEBUG." +#endif + +/* Sometimes we don't want to use assertions during branch coverage tests; it + * leads to tons of unreached branches which in reality are only assertions we + * didn't hit. */ +#if defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS) +#define tor_assert(a) STMT_BEGIN \ + (void)(a); \ + STMT_END +#else +/** Like assert(3), but send assertion failures to the log as well as to + * stderr. */ +#define tor_assert(expr) STMT_BEGIN \ + if (PREDICT_UNLIKELY(!(expr))) { \ + tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \ + abort(); \ + } STMT_END +#endif + +#define tor_assert_unreached() tor_assert(0) + +/* Non-fatal bug assertions. The "unreached" variants mean "this line should + * never be reached." The "once" variants mean "Don't log a warning more than + * once". + * + * The 'BUG' macro checks a boolean condition and logs an error message if it + * is true. Example usage: + * if (BUG(x == NULL)) + * return -1; + */ + +#ifdef ALL_BUGS_ARE_FATAL +#define tor_assert_nonfatal_unreached() tor_assert(0) +#define tor_assert_nonfatal(cond) tor_assert((cond)) +#define tor_assert_nonfatal_unreached_once() tor_assert(0) +#define tor_assert_nonfatal_once(cond) tor_assert((cond)) +#define BUG(cond) \ + (PREDICT_UNLIKELY(cond) ? \ + (tor_assertion_failed_(SHORT_FILE__,__LINE__,__func__,#cond), abort(), 1) \ + : 0) +#elif defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS) +#define tor_assert_nonfatal_unreached() STMT_NIL +#define tor_assert_nonfatal(cond) ((void)(cond)) +#define tor_assert_nonfatal_unreached_once() STMT_NIL +#define tor_assert_nonfatal_once(cond) ((void)(cond)) +#define BUG(cond) (PREDICT_UNLIKELY(cond) ? 1 : 0) +#else /* Normal case, !ALL_BUGS_ARE_FATAL, !DISABLE_ASSERTS_IN_UNIT_TESTS */ +#define tor_assert_nonfatal_unreached() STMT_BEGIN \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 0); \ + STMT_END +#define tor_assert_nonfatal(cond) STMT_BEGIN \ + if (PREDICT_UNLIKELY(!(cond))) { \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 0); \ + } \ + STMT_END +#define tor_assert_nonfatal_unreached_once() STMT_BEGIN \ + static int warning_logged__ = 0; \ + if (!warning_logged__) { \ + warning_logged__ = 1; \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 1); \ + } \ + STMT_END +#define tor_assert_nonfatal_once(cond) STMT_BEGIN \ + static int warning_logged__ = 0; \ + if (!warning_logged__ && PREDICT_UNLIKELY(!(cond))) { \ + warning_logged__ = 1; \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1); \ + } \ + STMT_END +#define BUG(cond) \ + (PREDICT_UNLIKELY(cond) ? \ + (tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,#cond,0), 1) \ + : 0) +#endif + +#ifdef __GNUC__ +#define IF_BUG_ONCE__(cond,var) \ + if (({ \ + static int var = 0; \ + int bool_result = (cond); \ + if (PREDICT_UNLIKELY(bool_result) && !var) { \ + var = 1; \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1); \ + } \ + PREDICT_UNLIKELY(bool_result); })) +#else +#define IF_BUG_ONCE__(cond,var) \ + static int var = 0; \ + if (PREDICT_UNLIKELY(cond)) ? \ + (var ? 1 : \ + (var=1, \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1), \ + 1)) \ + : 0) +#endif +#define IF_BUG_ONCE_VARNAME_(a) \ + warning_logged_on_ ## a ## __ +#define IF_BUG_ONCE_VARNAME__(a) \ + IF_BUG_ONCE_VARNAME_(a) + +/** This macro behaves as 'if (bug(x))', except that it only logs its + * warning once, no matter how many times it triggers. + */ + +#define IF_BUG_ONCE(cond) \ + IF_BUG_ONCE__((cond), \ + IF_BUG_ONCE_VARNAME__(__LINE__)) + +/** Define this if you want Tor to crash when any problem comes up, + * so you can get a coredump and track things down. */ +// #define tor_fragile_assert() tor_assert_unreached(0) +#define tor_fragile_assert() tor_assert_nonfatal_unreached_once() + +void tor_assertion_failed_(const char *fname, unsigned int line, + const char *func, const char *expr); +void tor_bug_occurred_(const char *fname, unsigned int line, + const char *func, const char *expr, + int once); + +#endif + |