diff --git a/changes/bug24914 b/changes/bug24914 new file mode 100644 index 000000000..ea441fd38 --- /dev/null +++ b/changes/bug24914 @@ -0,0 +1,3 @@ + o Minor features (performance): + - Avoid a needless call to malloc() when processing an incoming + relay cell. Closes ticket 24914. diff --git a/configure.ac b/configure.ac index 4c751d1eb..1f43650b0 100644 --- a/configure.ac +++ b/configure.ac @@ -855,6 +855,10 @@ AC_CHECK_MEMBERS([SSL.state], , , [#include ]) +AC_CHECK_SIZEOF(SHA_CTX, , [AC_INCLUDES_DEFAULT() +#include +]) + dnl Define the set of checks for KIST scheduler support. AC_DEFUN([CHECK_KIST_SUPPORT],[ dnl KIST needs struct tcp_info and for certain members to exist. diff --git a/src/common/crypto.c b/src/common/crypto.c index d85aca400..69faa0f63 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -970,6 +970,7 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg) /* Gives the length of crypto_digest_t through the end of the field 'd' */ #define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \ STRUCT_FIELD_SIZE(crypto_digest_t, f)) + switch (alg) { case DIGEST_SHA1: return END_OF_FIELD(d.sha1); @@ -1155,6 +1156,33 @@ crypto_digest_dup(const crypto_digest_t *digest) return tor_memdup(digest, alloc_bytes); } +/** Temporarily save the state of digest in checkpoint. + * Asserts that digest is a SHA1 digest object. + */ +void +crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint, + const crypto_digest_t *digest) +{ + tor_assert(digest->algorithm == DIGEST_SHA1); + /* The optimizer should turn this into a constant... */ + const size_t bytes = crypto_digest_alloc_bytes(DIGEST_SHA1); + /* ... and remove this assertion entirely. */ + tor_assert(bytes <= sizeof(checkpoint->mem)); + memcpy(checkpoint->mem, digest, bytes); +} + +/** Restore the state of digest from checkpoint. + * Asserts that digest is a SHA1 digest object. Requires that the + * state was previously stored with crypto_digest_checkpoint() */ +void +crypto_digest_restore(crypto_digest_t *digest, + const crypto_digest_checkpoint_t *checkpoint) +{ + tor_assert(digest->algorithm == DIGEST_SHA1); + const size_t bytes = crypto_digest_alloc_bytes(DIGEST_SHA1); + memcpy(digest, checkpoint->mem, bytes); +} + /** Replace the state of the digest object into with the state * of the digest object from. Requires that 'into' and 'from' * have the same digest type. diff --git a/src/common/crypto.h b/src/common/crypto.h index a9c8837b9..f8a392dff 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -90,6 +90,13 @@ typedef struct crypto_digest_t crypto_digest_t; typedef struct crypto_xof_t crypto_xof_t; typedef struct crypto_dh_t crypto_dh_t; +#define DIGEST_CHECKPOINT_BYTES (SIZEOF_VOID_P + SIZEOF_SHA_CTX) +/** Structure used to temporarily save the a digest object. Only implemented + * for SHA1 digest for now. */ +typedef struct crypto_digest_checkpoint_t { + uint8_t mem[DIGEST_CHECKPOINT_BYTES]; +} crypto_digest_checkpoint_t; + /* global state */ int crypto_early_init(void) ATTR_WUR; int crypto_global_init(int hardwareAccel, @@ -178,6 +185,10 @@ void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, void crypto_digest_get_digest(crypto_digest_t *digest, char *out, size_t out_len); crypto_digest_t *crypto_digest_dup(const crypto_digest_t *digest); +void crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint, + const crypto_digest_t *digest); +void crypto_digest_restore(crypto_digest_t *digest, + const crypto_digest_checkpoint_t *checkpoint); void crypto_digest_assign(crypto_digest_t *into, const crypto_digest_t *from); void crypto_hmac_sha256(char *hmac_out, diff --git a/src/or/relay.c b/src/or/relay.c index a59b82ac6..7c21839a8 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -151,9 +151,9 @@ relay_digest_matches(crypto_digest_t *digest, cell_t *cell) { uint32_t received_integrity, calculated_integrity; relay_header_t rh; - crypto_digest_t *backup_digest=NULL; + crypto_digest_checkpoint_t backup_digest; - backup_digest = crypto_digest_dup(digest); + crypto_digest_checkpoint(&backup_digest, digest); relay_header_unpack(&rh, cell->payload); memcpy(&received_integrity, rh.integrity, 4); @@ -167,19 +167,21 @@ relay_digest_matches(crypto_digest_t *digest, cell_t *cell) crypto_digest_add_bytes(digest, (char*) cell->payload, CELL_PAYLOAD_SIZE); crypto_digest_get_digest(digest, (char*) &calculated_integrity, 4); + int rv = 1; + if (calculated_integrity != received_integrity) { // log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing."); // (%d vs %d).", received_integrity, calculated_integrity); /* restore digest to its old form */ - crypto_digest_assign(digest, backup_digest); + crypto_digest_restore(digest, &backup_digest); /* restore the relay header */ memcpy(rh.integrity, &received_integrity, 4); relay_header_pack(cell->payload, &rh); - crypto_digest_free(backup_digest); - return 0; + rv = 0; } - crypto_digest_free(backup_digest); - return 1; + + memwipe(&backup_digest, 0, sizeof(backup_digest)); + return rv; } /** Apply cipher to CELL_PAYLOAD_SIZE bytes of in