diff --git a/GNUmakefile b/GNUmakefile
index 6d98e636..5867dba7 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -650,6 +650,7 @@ SODIUM_SOURCES := \
 	deps/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c \
 	deps/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c \
 	deps/libsodium/src/libsodium/crypto_core/softaes/softaes.c \
+	deps/libsodium/src/libsodium/crypto_generichash/crypto_generichash.c \
 	deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
 	deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c \
 	deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c \
diff --git a/src/mem.c b/src/mem.c
index 1c31bd07..5284ce83 100644
--- a/src/mem.c
+++ b/src/mem.c
@@ -3,6 +3,7 @@
 #include "util.js.h"
 
 #include "quickjs.h"
+#include "sodium/crypto_generichash.h"
 #include "sqlite3.h"
 #include "uv.h"
 
@@ -154,11 +155,11 @@ static void _tf_mem_summarize(void* ptr, size_t size, int frames_count, void* co
 {
 	summary_t* summary = user_data;
 	tf_mem_allocation_t allocation = {
-		.stack_hash = tf_util_fnv32a(frames, sizeof(void*) * frames_count, 0),
 		.count = 1,
 		.size = size,
 		.frames_count = frames_count,
 	};
+	crypto_generichash((void*)&allocation.stack_hash, sizeof(allocation.stack_hash), (const void*)frames, sizeof(void*) * frames_count, NULL, 0);
 	memcpy(allocation.frames, frames, sizeof(void*) * frames_count);
 
 	int index = tf_util_insert_index(&allocation, summary->allocations, summary->count, sizeof(tf_mem_allocation_t), _tf_mem_hash_stack_compare);
diff --git a/src/task.c b/src/task.c
index 64bb539b..f89e3edb 100644
--- a/src/task.c
+++ b/src/task.c
@@ -22,6 +22,7 @@
 #include "ares.h"
 #include "backtrace.h"
 #include "quickjs.h"
+#include "sodium/crypto_generichash.h"
 #include "sqlite3.h"
 #include "unzip.h"
 #include "uv.h"
@@ -1255,6 +1256,8 @@ static void _tf_task_free_promise(tf_task_t* task, promiseid_t id)
 JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
 {
 	uint32_t stack_hash = 0;
+	crypto_generichash_state state;
+	crypto_generichash_init(&state, NULL, 0, sizeof(stack_hash));
 	if (task->_promise_stack_debug)
 	{
 		JSValue error = JS_ThrowInternalError(task->_context, "promise callstack");
@@ -1262,16 +1265,17 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
 		JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack");
 		size_t length = 0;
 		const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
-		stack_hash = tf_util_fnv32a((const void*)stack, (int)length, 0);
+		crypto_generichash_update(&state, (const void*)stack, (int)length);
 		void* buffer[31];
 		int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
-		stack_hash = tf_util_fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
+		crypto_generichash_update(&state, (const void*)buffer, sizeof(void*) * count);
 		_add_promise_stack(task, stack_hash, stack, buffer, count);
 		JS_FreeCString(task->_context, stack);
 		JS_FreeValue(task->_context, stack_value);
 		JS_FreeValue(task->_context, exception);
 		JS_FreeValue(task->_context, error);
 	}
+	crypto_generichash_final(&state, (void*)&stack_hash, sizeof(stack_hash));
 
 	promiseid_t promise_id;
 	do
diff --git a/src/util.js.c b/src/util.js.c
index d685b995..2853ef09 100644
--- a/src/util.js.c
+++ b/src/util.js.c
@@ -691,17 +691,6 @@ bool tf_util_is_mobile()
 	return TF_IS_MOBILE != 0;
 }
 
-uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start)
-{
-	uint32_t result = 0x811c9dc5;
-	for (int i = 0; i < length; i++)
-	{
-		result ^= ((const uint8_t*)buffer)[i];
-		result += (result << 1) + (result << 4) + (result << 7) + (result << 8) + (result << 24);
-	}
-	return result;
-}
-
 size_t tf_string_set(char* buffer, size_t size, const char* string)
 {
 	size_t length = string ? strlen(string) : 0;
diff --git a/src/util.js.h b/src/util.js.h
index 7a35bab5..b31f6e9f 100644
--- a/src/util.js.h
+++ b/src/util.js.h
@@ -224,15 +224,6 @@ void tf_util_document_settings(const char* line_prefix);
 */
 bool tf_util_is_mobile();
 
-/**
-** Compute a 32-bit hash of a buffer.
-** @param buffer The data.
-** @param length The size of the buffer in bytes.
-** @param start The hash seed.
-** @return The computed hash.
-*/
-uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start);
-
 /**
 ** Populate a string buffer, truncating if necessary.
 ** @param buffer The buffer.