[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [tor] 04/07: hashx: API changes to allow recovery from late compile failures



This is an automated email from the git hooks/post-receive script.

dgoulet pushed a commit to branch main
in repository tor.

commit 5a4f92ea7b9f743548e4dba8843b13fb258a7321
Author: Micah Elizabeth Scott <beth@xxxxxxxxxxxxxx>
AuthorDate: Thu May 25 17:37:52 2023 -0700

    hashx: API changes to allow recovery from late compile failures
    
    This is an API breaking change to hashx, which modifies the error
    handling strategy. The main goal here is to allow unproblematic
    recovery from hashx_compile failures.
    
    hashx_alloc can no longer fail for reasons other than memory
    allocation. All platform-specific compile failures are now reported via
    hashx_make(), in order to both allow later failure and avoid requiring
    users of the API to maintain and test multiple failure paths.
    
    Note that late failures may be more common in actual use than early
    failures. Early failures represent architectures other than x86_64 and
    aarch64. Late failures could represent a number of system configurations
    where syscalls are restricted.
    
    The definition of a hashx context no longer tries to overlay storage for
    the different types of program, and instead allows one context to always
    contain an interpretable description of the program as well as an optional
    buffer for compiled code.
    
    The hashx_type enum is now used to mean either a specific type of hash
    function or a type of hashx context. You can allocate a context for use
    only with interpreted or compiled functions, or you can use
    HASHX_TRY_COMPILE to prefer the compiler with an automatic fallback on
    the interpreter. After calling hashx_make(), the new hashx_query_type()
    can be used if needed to determine which implementation was actually
    chosen.
    
    The error return types have been overhauled so that everyone uses the
    hashx_result enum, and seed failures vs compile failures are always
    clearly distinguishable.
    
    Signed-off-by: Micah Elizabeth Scott <beth@xxxxxxxxxxxxxx>
---
 src/ext/equix/hashx/README.md            |  37 ++++++++--
 src/ext/equix/hashx/include/hashx.h      |  72 ++++++++++++++-----
 src/ext/equix/hashx/src/bench.c          |  34 ++++++---
 src/ext/equix/hashx/src/compiler.c       |   9 +--
 src/ext/equix/hashx/src/compiler.h       |   5 +-
 src/ext/equix/hashx/src/context.c        |  45 +++---------
 src/ext/equix/hashx/src/context.h        |  16 ++---
 src/ext/equix/hashx/src/hashx.c          |  78 ++++++++++++++-------
 src/ext/equix/hashx/src/tests.c          | 116 ++++++++++++++++++++++---------
 src/ext/equix/hashx/src/virtual_memory.c |   3 +
 10 files changed, 269 insertions(+), 146 deletions(-)

diff --git a/src/ext/equix/hashx/README.md b/src/ext/equix/hashx/README.md
index 1d8ea47652..f6352475b0 100644
--- a/src/ext/equix/hashx/README.md
+++ b/src/ext/equix/hashx/README.md
@@ -13,7 +13,7 @@ and to ensure that each function takes exactly the same number of CPU cycles
 
 ## API
 
-The API consists of 4 functions and is documented in the public header file
+The API consists of 5 functions and is documented in the public header file
 [hashx.h](include/hashx.h).
 
 Example of usage:
@@ -25,13 +25,15 @@ Example of usage:
 int main() {
     char seed[] = "this is a seed that will generate a hash function";
     char hash[HASHX_SIZE];
-    hashx_ctx* ctx = hashx_alloc(HASHX_COMPILED);
-    if (ctx == HASHX_NOTSUPP)
-        ctx = hashx_alloc(HASHX_INTERPRETED);
+    hashx_type func_type;
+    hashx_ctx* ctx = hashx_alloc(HASHX_TRY_COMPILE);
     if (ctx == NULL)
         return 1;
-    if (!hashx_make(ctx, seed, sizeof(seed))) /* generate a hash function */
+    /* generate a hash function */
+    if (hashx_make(ctx, seed, sizeof(seed)) != HASHX_OK)
         return 1;
+    if (hashx_query_type(ctx, &func_type) == HASHX_OK && func_type == HASHX_TYPE_COMPILED)
+        printf("Using the compiled implementation of HashX\n");
     hashx_exec(ctx, 123456789, hash); /* calculate the hash of a nonce value */
     hashx_free(ctx);
     for (unsigned i = 0; i < HASHX_SIZE; ++i)
@@ -84,6 +86,31 @@ A benchmark executable is included:
 ./hashx-bench --seeds 500
 ```
 
+## Error fallback
+
+The compiled implementation of HashX is much faster (very roughly 20x) so it
+should be used whenever possible. It may be necessary to use the interpreter
+for multiple reasons: either the platform is not supported at compile time,
+or various runtime policies disallow the memory protection changes that are
+necessary to do just-in-time compilation. Failures may be detected late, so
+the library provides a built-in mechanism to fall back from the compiled
+implementation to interpreted quickly without duplicating the whole context.
+
+The `hashx_query_type()` function is optional, provided for users of the
+`HASHX_TRY_COMPILE` context who need to know which implementation was
+ultimately used.
+
+The actual hash function, `hashx_exec()`, returns an error code for
+completeness in reporting programming errors, but if a caller has invoked
+`hashx_make()` successfully it can be considered infallible.
+
+It is always possible for `hashx_make()` to fail. In addition to the
+OS-specific failures you may see when forcing HashX to use the compiled
+implementation with `hashx_alloc(HASHX_TYPE_COMPILED)`, it's always possible
+for `hashx_make` to fail unpredictably for a particular seed value. These
+seeds should be discarded and a new one attempted by the caller when
+`hashx_make` returns `HASHX_FAIL_SEED`.
+
 ## Security
 
 HashX should provide strong preimage resistance. No other security guarantees are made. About
diff --git a/src/ext/equix/hashx/include/hashx.h b/src/ext/equix/hashx/include/hashx.h
index 0d5521177a..2910515d9a 100644
--- a/src/ext/equix/hashx/include/hashx.h
+++ b/src/ext/equix/hashx/include/hashx.h
@@ -15,14 +15,13 @@
     int main() {
         char seed[] = "this is a seed that will generate a hash function";
         char hash[HASHX_SIZE];
-        hashx_ctx* ctx = hashx_alloc(HASHX_COMPILED);
-        if (ctx == HASHX_NOTSUPP)
-            ctx = hashx_alloc(HASHX_INTERPRETED);
+        hashx_ctx* ctx = hashx_alloc(HASHX_TRY_COMPILE);
         if (ctx == NULL)
             return 1;
-        if (!hashx_make(ctx, seed, sizeof(seed)))
+        if (hashx_make(ctx, seed, sizeof(seed)) != EQUIX_OK)
+            return 1;
+        if (hashx_exec(ctx, 123456789, hash) != EQUIX_OK)
             return 1;
-        hashx_exec(ctx, 123456789, hash);
         hashx_free(ctx);
         for (unsigned i = 0; i < HASHX_SIZE; ++i)
             printf("%02x", hash[i] & 0xff);
@@ -58,14 +57,21 @@
 /* Opaque struct representing a HashX instance */
 typedef struct hashx_ctx hashx_ctx;
 
-/* Type of hash function */
+/* Type of hash context / type of compiled function */
 typedef enum hashx_type {
-    HASHX_INTERPRETED,
-    HASHX_COMPILED
+    HASHX_TYPE_INTERPRETED = 1, /* Only the interpreted implementation */
+    HASHX_TYPE_COMPILED,        /* Require the compiler, fail if unavailable */
+    HASHX_TRY_COMPILE,          /* (hashx_alloc) Try compiler, don't require */
 } hashx_type;
 
-/* Sentinel value used to indicate unsupported type */
-#define HASHX_NOTSUPP ((hashx_ctx*)-1)
+/* Result code for hashx_make and hashx_exec */
+typedef enum hashx_result {
+    HASHX_OK = 0,
+    HASHX_FAIL_UNPREPARED, /* Trying to run an unmade hash funciton */
+    HASHX_FAIL_UNDEFINED,  /* Unrecognized hashx_type enum value */
+    HASHX_FAIL_SEED,       /* Can't construct a hash function from this seed */
+    HASHX_FAIL_COMPILE,    /* Can't compile, and no fallback is enabled. */
+} hashx_result;
 
 #if defined(_WIN32) || defined(__CYGWIN__)
 #define HASHX_WIN
@@ -100,35 +106,65 @@ extern "C" {
  * @param type is the type of instance to be created.
  *
  * @return pointer to a new HashX instance. Returns NULL on memory allocation
- *         failure and HASHX_NOTSUPP if the requested type is not supported.
-*/
+ *         failures only. Other failures are reported in hashx_make.
+ */
 HASHX_API hashx_ctx* hashx_alloc(hashx_type type);
 
 /*
- * Create a new HashX function from seed.
+ * Create a new HashX function from a variable-length seed value.
+ *
+ * The seed value will be hashed internally in order to initialize the state
+ * of the HashX program generator and create a new unique hash function.
  *
  * @param ctx is pointer to a HashX instance.
  * @param seed is a pointer to the seed value.
  * @param size is the size of the seed.
  *
- * @return 1 on success, 0 on failure.
+ * @return HASHX_OK on success, HASHX_FAIL_SEED if the specific seed is
+ *         not associated with a valid hash program, and HASHX_FAIL_COMPILE
+ *         if the compiler failed for OS-specific reasons and the interpreter
+ *         fallback was disabled by allocating the context with
+ *         HASHX_TYPE_COMPILED rather than HASHX_TRY_COMPILE.
+ */
+HASHX_API hashx_result hashx_make(hashx_ctx* ctx,
+                                  const void* seed, size_t size);
+
+/*
+ * Asks the specific implementation of a function created with hashx_make.
+ *
+ * This will equal the parameter to hashx_alloc() if a specific type was
+ * chosen there, but a context allocated with HASHX_TRY_COMPILE will allow
+ * the implementation to vary dynamically during hashx_make.
+ *
+ * @param ctx is pointer to a HashX instance.
+ * @param type_out is a pointer to which, on success, we write
+ *                 a HASHX_TYPE_* value.
+ *
+ * @return HASHX_OK on success, or HASHX_FAIL_UNPREPARED if hashx_make has not
+ *         been invoked successfully on this context.
 */
-HASHX_API int hashx_make(hashx_ctx* ctx, const void* seed, size_t size);
+HASHX_API hashx_result hashx_query_type(hashx_ctx* ctx, hashx_type *type_out);
 
 /*
  * Execute the HashX function.
  *
  * @param ctx is pointer to a HashX instance. A HashX function must have
- *        been previously created by calling hashx_make.
+ *        been previously created by invoking hashx_make successfully.
  * @param HASHX_INPUT is the input to be hashed (see definition above).
  * @param output is a pointer to the result buffer. HASHX_SIZE bytes will be
  *        written.
- s*/
-HASHX_API void hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output);
+ *
+ * @return HASHX_OK on success, or HASHX_FAIL_UNPREPARED if hashx_make has not
+ *         been invoked successfully on this context.
+ */
+HASHX_API hashx_result hashx_exec(const hashx_ctx* ctx,
+                                  HASHX_INPUT, void* output);
 
 /*
  * Free a HashX instance.
  *
+ * Has no effect if ctx is NULL.
+ *
  * @param ctx is pointer to a HashX instance.
 */
 HASHX_API void hashx_free(hashx_ctx* ctx);
diff --git a/src/ext/equix/hashx/src/bench.c b/src/ext/equix/hashx/src/bench.c
index fbcd41a064..a440825436 100644
--- a/src/ext/equix/hashx/src/bench.c
+++ b/src/ext/equix/hashx/src/bench.c
@@ -5,6 +5,7 @@
 #include "hashx_thread.h"
 #include "hashx_endian.h"
 #include "hashx_time.h"
+#include <assert.h>
 #include <limits.h>
 #include <inttypes.h>
 
@@ -26,16 +27,31 @@ static hashx_thread_retval worker(void* args) {
 	job->total_hashes = 0;
 	job->best_hash = UINT64_MAX;
 	for (int seed = job->start; seed < job->end; seed += job->step) {
-		if (!hashx_make(job->ctx, &seed, sizeof(seed))) {
-			continue;
+		{
+			hashx_result result = hashx_make(job->ctx, &seed, sizeof(seed));
+			if (result == HASHX_FAIL_SEED) {
+				continue;
+			}
+			if (result == HASHX_FAIL_COMPILE) {
+				printf("Error: not supported. Try with --interpret\n");
+			}
+			assert(result == HASHX_OK);
+			if (result != HASHX_OK)
+				break;
 		}
 		for (int nonce = 0; nonce < job->nonces; ++nonce) {
 			uint8_t hash[HASHX_SIZE] = { 0 };
+			{
 #ifndef HASHX_BLOCK_MODE
-			hashx_exec(job->ctx, nonce, hash);
+				hashx_result result = hashx_exec(job->ctx, nonce, hash);
 #else
-			hashx_exec(job->ctx, &nonce, sizeof(nonce), hash);
+				hashx_result result = hashx_exec(job->ctx,
+					&nonce, sizeof(nonce), hash);
 #endif
+				assert(result == HASHX_OK);
+				if (result != HASHX_OK)
+					break;
+			}
 			uint64_t hashval = load64(hash);
 			if (hashval < job->best_hash) {
 				job->best_hash = hashval;
@@ -70,9 +86,9 @@ int main(int argc, char** argv) {
 	read_int_option("--nonces", argc, argv, &nonces, 65536);
 	read_int_option("--threads", argc, argv, &threads, 1);
 	read_option("--interpret", argc, argv, &interpret);
-	hashx_type flags = HASHX_INTERPRETED;
+	hashx_type ctx_type = HASHX_TYPE_INTERPRETED;
 	if (!interpret) {
-		flags = HASHX_COMPILED;
+		ctx_type = HASHX_TYPE_COMPILED;
 	}
 	uint64_t best_hash = UINT64_MAX;
 	uint64_t diff_ex = (uint64_t)diff * 1000ULL;
@@ -88,15 +104,11 @@ int main(int argc, char** argv) {
 		return 1;
 	}
 	for (int thd = 0; thd < threads; ++thd) {
-		jobs[thd].ctx = hashx_alloc(flags);
+		jobs[thd].ctx = hashx_alloc(ctx_type);
 		if (jobs[thd].ctx == NULL) {
 			printf("Error: memory allocation failure\n");
 			return 1;
 		}
-		if (jobs[thd].ctx == HASHX_NOTSUPP) {
-			printf("Error: not supported. Try with --interpret\n");
-			return 1;
-		}
 		jobs[thd].id = thd;
 		jobs[thd].start = start + thd;
 		jobs[thd].step = threads;
diff --git a/src/ext/equix/hashx/src/compiler.c b/src/ext/equix/hashx/src/compiler.c
index f180bf2d25..870f3654b6 100644
--- a/src/ext/equix/hashx/src/compiler.c
+++ b/src/ext/equix/hashx/src/compiler.c
@@ -8,11 +8,12 @@
 #include "program.h"
 #include "context.h"
 
-bool hashx_compiler_init(hashx_ctx* ctx) {
-	ctx->code = hashx_vm_alloc(COMP_CODE_SIZE);
-	return ctx->code != NULL;
+void hashx_compiler_init(hashx_ctx* ctx) {
+	/* This can fail, but it's uncommon. We report this up the call chain
+	 * later, at the same time as an mprotect or similar failure. */
+	ctx->compiler_mem = hashx_vm_alloc(COMP_CODE_SIZE);
 }
 
 void hashx_compiler_destroy(hashx_ctx* ctx) {
-	hashx_vm_free(ctx->code, COMP_CODE_SIZE);
+	hashx_vm_free(ctx->compiler_mem, COMP_CODE_SIZE);
 }
diff --git a/src/ext/equix/hashx/src/compiler.h b/src/ext/equix/hashx/src/compiler.h
index 140f797a58..f248e8c1ea 100644
--- a/src/ext/equix/hashx/src/compiler.h
+++ b/src/ext/equix/hashx/src/compiler.h
@@ -15,19 +15,16 @@ HASHX_PRIVATE bool hashx_compile_x86(const hashx_program* program, uint8_t* code
 HASHX_PRIVATE bool hashx_compile_a64(const hashx_program* program, uint8_t* code);
 
 #if defined(_M_X64) || defined(__x86_64__)
-#define HASHX_COMPILER 1
 #define HASHX_COMPILER_X86
 #define hashx_compile(p,c) hashx_compile_x86(p,c)
 #elif defined(__aarch64__)
-#define HASHX_COMPILER 1
 #define HASHX_COMPILER_A64
 #define hashx_compile(p,c) hashx_compile_a64(p,c)
 #else
-#define HASHX_COMPILER 0
 #define hashx_compile(p,c) (false)
 #endif
 
-HASHX_PRIVATE bool hashx_compiler_init(hashx_ctx* compiler);
+HASHX_PRIVATE void hashx_compiler_init(hashx_ctx* compiler);
 HASHX_PRIVATE void hashx_compiler_destroy(hashx_ctx* compiler);
 
 #define COMP_PAGE_SIZE 4096
diff --git a/src/ext/equix/hashx/src/context.c b/src/ext/equix/hashx/src/context.c
index 8548fb7ffa..03a9de57fd 100644
--- a/src/ext/equix/hashx/src/context.c
+++ b/src/ext/equix/hashx/src/context.c
@@ -33,50 +33,25 @@ const blake2b_param hashx_blake2_params = {
 };
 
 hashx_ctx* hashx_alloc(hashx_type type) {
-	if (!HASHX_COMPILER && (type & HASHX_COMPILED)) {
-		return HASHX_NOTSUPP;
-	}
 	hashx_ctx* ctx = malloc(sizeof(hashx_ctx));
-	if (ctx == NULL) {
-		goto failure;
-	}
-	ctx->code = NULL;
-	ctx->type = 0;
-	if (type & HASHX_COMPILED) {
-		if (!hashx_compiler_init(ctx)) {
-			goto failure;
-		}
-		ctx->type = HASHX_COMPILED;
-	}
-	else {
-		ctx->program = malloc(sizeof(hashx_program));
-		if (ctx->program == NULL) {
-			goto failure;
-		}
-		ctx->type = HASHX_INTERPRETED;
+	if (ctx == NULL)
+		return NULL;
+
+	memset(ctx, 0, sizeof *ctx);
+	ctx->ctx_type = type;
+	if (type == HASHX_TYPE_COMPILED || type == HASHX_TRY_COMPILE) {
+		hashx_compiler_init(ctx);
 	}
+
 #ifdef HASHX_BLOCK_MODE
 	memcpy(&ctx->params, &hashx_blake2_params, 32);
-#endif
-#ifndef NDEBUG
-	ctx->has_program = false;
 #endif
 	return ctx;
-failure:
-	hashx_free(ctx);
-	return NULL;
 }
 
 void hashx_free(hashx_ctx* ctx) {
-	if (ctx != NULL && ctx != HASHX_NOTSUPP) {
-		if (ctx->code != NULL) {
-			if (ctx->type & HASHX_COMPILED) {
-				hashx_compiler_destroy(ctx);
-			}
-			else {
-				free(ctx->program);
-			}
-		}
+	if (ctx != NULL) {
+		hashx_compiler_destroy(ctx);
 		free(ctx);
 	}
 }
diff --git a/src/ext/equix/hashx/src/context.h b/src/ext/equix/hashx/src/context.h
index 40736397f8..ad434eb66c 100644
--- a/src/ext/equix/hashx/src/context.h
+++ b/src/ext/equix/hashx/src/context.h
@@ -9,8 +9,7 @@
 #include "hashx.h"
 #include "blake2.h"
 #include "siphash.h"
-
-typedef void program_func(uint64_t r[8]);
+#include "program.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -26,20 +25,15 @@ typedef struct hashx_program hashx_program;
 
 /* HashX context. */
 typedef struct hashx_ctx {
-	union {
-		uint8_t* code;
-		program_func* func;
-		hashx_program* program;
-	};
-	hashx_type type;
+	uint8_t* compiler_mem;
+	hashx_type ctx_type;
+	hashx_type func_type;
+	hashx_program program;
 #ifndef HASHX_BLOCK_MODE
 	siphash_state keys;
 #else
 	blake2b_param params;
 #endif
-#ifndef NDEBUG
-	bool has_program;
-#endif
 } hashx_ctx;
 
 #endif
diff --git a/src/ext/equix/hashx/src/hashx.c b/src/ext/equix/hashx/src/hashx.c
index 372df09581..36a32fc298 100644
--- a/src/ext/equix/hashx/src/hashx.c
+++ b/src/ext/equix/hashx/src/hashx.c
@@ -22,25 +22,20 @@
 #define HASHX_INPUT_ARGS input, size
 #endif
 
-static int initialize_program(hashx_ctx* ctx, hashx_program* program,
-	siphash_state keys[2]) {
-
-	if (!hashx_program_generate(&keys[0], program)) {
-		return 0;
+static bool initialize_program(hashx_ctx* ctx, siphash_state keys[2]) {
+	if (!hashx_program_generate(&keys[0], &ctx->program)) {
+		return false;
 	}
 #ifndef HASHX_BLOCK_MODE
 	memcpy(&ctx->keys, &keys[1], 32);
 #else
 	memcpy(&ctx->params.salt, &keys[1], 32);
 #endif
-#ifndef NDEBUG
-	ctx->has_program = true;
-#endif
-	return 1;
+	return true;
 }
 
-int hashx_make(hashx_ctx* ctx, const void* seed, size_t size) {
-	assert(ctx != NULL && ctx != HASHX_NOTSUPP);
+hashx_result hashx_make(hashx_ctx* ctx, const void* seed, size_t size) {
+	assert(ctx != NULL);
 	assert(seed != NULL || size == 0);
 
 	uint8_t keys_bytes[2 * sizeof(siphash_state)];
@@ -59,23 +54,48 @@ int hashx_make(hashx_ctx* ctx, const void* seed, size_t size) {
 	keys[1].v2 = load64(keys_bytes + 6 * sizeof(uint64_t));
 	keys[1].v3 = load64(keys_bytes + 7 * sizeof(uint64_t));
 
-	if (ctx->type & HASHX_COMPILED) {
-		hashx_program program;
-		if (!initialize_program(ctx, &program, keys)) {
-			return 0;
+	ctx->func_type = (hashx_type)0;
+	if (!initialize_program(ctx, keys)) {
+		return HASHX_FAIL_SEED;
+	}
+
+	switch (ctx->ctx_type) {
+	case HASHX_TYPE_INTERPRETED:
+		ctx->func_type = HASHX_TYPE_INTERPRETED;
+		return HASHX_OK;
+	case HASHX_TYPE_COMPILED:
+	case HASHX_TRY_COMPILE:
+		if (ctx->compiler_mem != NULL &&
+			hashx_compile(&ctx->program, ctx->compiler_mem)) {
+			ctx->func_type = HASHX_TYPE_COMPILED;
+			return HASHX_OK;
 		}
-		if (!hashx_compile(&program, ctx->code)) {
-			return 0;
+		if (ctx->ctx_type == HASHX_TRY_COMPILE) {
+			ctx->func_type = HASHX_TYPE_INTERPRETED;
+			return HASHX_OK;
+		} else {
+			return HASHX_FAIL_COMPILE;
 		}
-		return 1;
+	default:
+		return HASHX_FAIL_UNDEFINED;
+	}
+}
+
+hashx_result hashx_query_type(hashx_ctx* ctx, hashx_type *type_out) {
+	assert(ctx != NULL);
+	assert(type_out != NULL);
+
+	if (ctx->func_type == (hashx_type)0) {
+		return HASHX_FAIL_UNPREPARED;
 	}
-	return initialize_program(ctx, ctx->program, keys);
+	*type_out = ctx->func_type;
+	return HASHX_OK;
 }
 
-void hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output) {
-	assert(ctx != NULL && ctx != HASHX_NOTSUPP);
+hashx_result hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output) {
+	assert(ctx != NULL);
 	assert(output != NULL);
-	assert(ctx->has_program);
+
 	uint64_t r[8];
 #ifndef HASHX_BLOCK_MODE
 	hashx_siphash24_ctr_state512(&ctx->keys, input, r);
@@ -83,11 +103,14 @@ void hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output) {
 	hashx_blake2b_4r(&ctx->params, input, size, r);
 #endif
 
-	if (ctx->type & HASHX_COMPILED) {
-		ctx->func(r);
-	}
-	else {
-		hashx_program_execute(ctx->program, r);
+	if (ctx->func_type == HASHX_TYPE_COMPILED) {
+		typedef void program_func(uint64_t r[8]);
+		assert(ctx->compiler_mem != NULL);
+		((program_func*)ctx->compiler_mem)(r);
+	} else if (ctx->func_type == HASHX_TYPE_INTERPRETED) {
+		hashx_program_execute(&ctx->program, r);
+	} else {
+		return HASHX_FAIL_UNPREPARED;
 	}
 
 	/* Hash finalization to remove bias toward 0 caused by multiplications */
@@ -145,4 +168,5 @@ void hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output) {
 	memcpy(output, temp_out, HASHX_SIZE);
 #endif
 #endif
+	return HASHX_OK;
 }
diff --git a/src/ext/equix/hashx/src/tests.c b/src/ext/equix/hashx/src/tests.c
index e1569844ac..f0a4ebe713 100644
--- a/src/ext/equix/hashx/src/tests.c
+++ b/src/ext/equix/hashx/src/tests.c
@@ -14,6 +14,7 @@ static int test_no = 0;
 
 static hashx_ctx* ctx_int = NULL;
 static hashx_ctx* ctx_cmp = NULL;
+static hashx_ctx* ctx_auto = NULL;
 
 static const char seed1[] = "This is a test";
 static const char seed2[] = "Lorem ipsum dolor sit amet";
@@ -42,20 +43,21 @@ static void run_test(const char* name, test_func* func) {
 }
 
 static bool test_alloc() {
-	ctx_int = hashx_alloc(HASHX_INTERPRETED);
-	assert(ctx_int != NULL && ctx_int != HASHX_NOTSUPP);
+	ctx_int = hashx_alloc(HASHX_TYPE_INTERPRETED);
+	assert(ctx_int != NULL);
 	return true;
 }
 
 static bool test_free() {
 	hashx_free(ctx_int);
 	hashx_free(ctx_cmp);
+	hashx_free(ctx_auto);
 	return true;
 }
 
 static bool test_make1() {
-	int result = hashx_make(ctx_int, seed1, sizeof(seed1));
-	assert(result == 1);
+	hashx_result result = hashx_make(ctx_int, seed1, sizeof(seed1));
+	assert(result == HASHX_OK);
 	return true;
 }
 
@@ -65,7 +67,8 @@ static bool test_hash_ctr1() {
 #endif
 #ifndef HASHX_BLOCK_MODE
 	char hash[HASHX_SIZE];
-	hashx_exec(ctx_int, counter2, hash);
+	hashx_result result = hashx_exec(ctx_int, counter2, hash);
+	assert(result == HASHX_OK);
 	/* printf("\n");
 	output_hex(hash, HASHX_SIZE);
 	printf("\n"); */
@@ -82,7 +85,8 @@ static bool test_hash_ctr2() {
 #endif
 #ifndef HASHX_BLOCK_MODE
 	char hash[HASHX_SIZE];
-	hashx_exec(ctx_int, counter1, hash);
+	hashx_result result = hashx_exec(ctx_int, counter1, hash);
+	assert(result == HASHX_OK);
 	assert(equals_hex(hash, "2b2f54567dcbea98fdb5d5e5ce9a65983c4a4e35ab1464b1efb61e83b7074bb2"));
 	return true;
 #else
@@ -91,8 +95,8 @@ static bool test_hash_ctr2() {
 }
 
 static bool test_make2() {
-	int result = hashx_make(ctx_int, seed2, sizeof(seed2));
-	assert(result == 1);
+	hashx_result result = hashx_make(ctx_int, seed2, sizeof(seed2));
+	assert(result == HASHX_OK);
 	return true;
 }
 
@@ -102,7 +106,8 @@ static bool test_hash_ctr3() {
 #endif
 #ifndef HASHX_BLOCK_MODE
 	char hash[HASHX_SIZE];
-	hashx_exec(ctx_int, counter2, hash);
+	hashx_result result = hashx_exec(ctx_int, counter2, hash);
+	assert(result == HASHX_OK);
 	assert(equals_hex(hash, "ab3d155bf4bbb0aa3a71b7801089826186e44300e6932e6ffd287cf302bbb0ba"));
 	return true;
 #else
@@ -116,7 +121,8 @@ static bool test_hash_ctr4() {
 #endif
 #ifndef HASHX_BLOCK_MODE
 	char hash[HASHX_SIZE];
-	hashx_exec(ctx_int, counter3, hash);
+	hashx_result result = hashx_exec(ctx_int, counter3, hash);
+	assert(result == HASHX_OK);
 	assert(equals_hex(hash, "8dfef0497c323274a60d1d93292b68d9a0496379ba407b4341cf868a14d30113"));
 	return true;
 #else
@@ -132,36 +138,40 @@ static bool test_hash_block1() {
 	return false;
 #else
 	char hash[HASHX_SIZE];
-	hashx_exec(ctx_int, long_input, sizeof(long_input), hash);
+	hashx_result result = hashx_exec(ctx_int, long_input, sizeof(long_input), hash);
+	assert(result == HASHX_OK);
 	assert(equals_hex(hash, "d0b232b832459501ca1ac9dc0429fd931414ead7624a457e375a43ea3e5e737a"));
 	return true;
 #endif
 }
 
 static bool test_alloc_compiler() {
-	ctx_cmp = hashx_alloc(HASHX_COMPILED);
+	ctx_cmp = hashx_alloc(HASHX_TYPE_COMPILED);
 	assert(ctx_cmp != NULL);
-	return ctx_cmp != HASHX_NOTSUPP;
+	return true;
 }
 
 static bool test_make3() {
-	if (ctx_cmp == HASHX_NOTSUPP)
+	hashx_result result = hashx_make(ctx_cmp, seed2, sizeof(seed2));
+	if (result == HASHX_FAIL_COMPILE) {
 		return false;
-
-	int result = hashx_make(ctx_cmp, seed2, sizeof(seed2));
-	assert(result == 1);
+	}
+	assert(result == HASHX_OK);
 	return true;
 }
 
 static bool test_compiler_ctr1() {
-	if (ctx_cmp == HASHX_NOTSUPP)
-		return false;
-
 #ifndef HASHX_BLOCK_MODE
+	hashx_result result;
 	char hash1[HASHX_SIZE];
 	char hash2[HASHX_SIZE];
-	hashx_exec(ctx_int, counter2, hash1);
-	hashx_exec(ctx_cmp, counter2, hash2);
+	result = hashx_exec(ctx_int, counter2, hash1);
+	assert(result == HASHX_OK);
+	result = hashx_exec(ctx_cmp, counter2, hash2);
+	if (result == HASHX_FAIL_UNPREPARED) {
+		return false;
+	}
+	assert(result == HASHX_OK);
 	assert(hashes_equal(hash1, hash2));
 	return true;
 #else
@@ -170,14 +180,17 @@ static bool test_compiler_ctr1() {
 }
 
 static bool test_compiler_ctr2() {
-	if (ctx_cmp == HASHX_NOTSUPP)
-		return false;
-
 #ifndef HASHX_BLOCK_MODE
+	hashx_result result;
 	char hash1[HASHX_SIZE];
 	char hash2[HASHX_SIZE];
-	hashx_exec(ctx_int, counter1, hash1);
-	hashx_exec(ctx_cmp, counter1, hash2);
+	result = hashx_exec(ctx_int, counter1, hash1);
+	assert(result == HASHX_OK);
+	result = hashx_exec(ctx_cmp, counter1, hash2);
+	if (result == HASHX_FAIL_UNPREPARED) {
+		return false;
+	}
+	assert(result == HASHX_OK);
 	assert(hashes_equal(hash1, hash2));
 	return true;
 #else
@@ -186,20 +199,58 @@ static bool test_compiler_ctr2() {
 }
 
 static bool test_compiler_block1() {
-	if (ctx_cmp == HASHX_NOTSUPP)
-		return false;
 #ifndef HASHX_BLOCK_MODE
 	return false;
 #else
+	hashx_result result;
 	char hash1[HASHX_SIZE];
 	char hash2[HASHX_SIZE];
-	hashx_exec(ctx_int, long_input, sizeof(long_input), hash1);
-	hashx_exec(ctx_cmp, long_input, sizeof(long_input), hash2);
+	result = hashx_exec(ctx_int, long_input, sizeof(long_input), hash1);
+	assert(result == HASHX_OK);
+	result = hashx_exec(ctx_cmp, long_input, sizeof(long_input), hash2);
+	if (result == HASHX_FAIL_UNPREPARED) {
+		return false;
+	}
+	assert(result == HASHX_OK);
 	assert(hashes_equal(hash1, hash2));
 	return true;
 #endif
 }
 
+static bool test_alloc_automatic() {
+	ctx_auto = hashx_alloc(HASHX_TRY_COMPILE);
+	assert(ctx_auto != NULL);
+	return true;
+}
+
+static bool test_auto_fallback() {
+	hashx_result result = hashx_make(ctx_auto, seed2, sizeof(seed2));
+	assert(result == HASHX_OK);
+	hashx_type actual_type = (hashx_type)-1;
+	result = hashx_query_type(ctx_auto, &actual_type);
+	assert(result == HASHX_OK);
+	assert(actual_type == HASHX_TYPE_INTERPRETED ||
+	       actual_type == HASHX_TYPE_COMPILED);
+	return actual_type == HASHX_TYPE_INTERPRETED;
+}
+
+static bool test_bad_seeds() {
+#ifdef HASHX_SALT
+	return false;
+#else
+	hashx_result result;
+	result = hashx_make(ctx_auto, "\xf8\x05\x00\x00", 4);
+	assert(result == HASHX_OK);
+	result = hashx_make(ctx_auto, "\xf9\x05\x00\x00", 4);
+	assert(result == HASHX_FAIL_SEED);
+	result = hashx_make(ctx_auto, "\x5d\x93\x02\x00", 4);
+	assert(result == HASHX_FAIL_SEED);
+	result = hashx_make(ctx_auto, "\x5e\x93\x02\x00", 4);
+	assert(result == HASHX_OK);
+	return true;
+#endif
+}
+
 int main() {
 	RUN_TEST(test_alloc);
 	RUN_TEST(test_make1);
@@ -214,6 +265,9 @@ int main() {
 	RUN_TEST(test_compiler_ctr2);
 	RUN_TEST(test_hash_block1);
 	RUN_TEST(test_compiler_block1);
+	RUN_TEST(test_alloc_automatic);
+	RUN_TEST(test_auto_fallback);
+	RUN_TEST(test_bad_seeds);
 	RUN_TEST(test_free);
 
 	printf("\nAll tests were successful\n");
diff --git a/src/ext/equix/hashx/src/virtual_memory.c b/src/ext/equix/hashx/src/virtual_memory.c
index 8d574740c5..e9df825c9f 100644
--- a/src/ext/equix/hashx/src/virtual_memory.c
+++ b/src/ext/equix/hashx/src/virtual_memory.c
@@ -120,6 +120,9 @@ void* hashx_vm_alloc_huge(size_t bytes) {
 }
 
 void hashx_vm_free(void* ptr, size_t bytes) {
+	if (!ptr) {
+		return;
+	}
 #ifdef HASHX_WIN
 	(void)bytes;
 	VirtualFree(ptr, 0, MEM_RELEASE);

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits