diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 9f134583b3..59da6b0c34 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -168,6 +168,9 @@ _test_hash_str (const char *str) static void test_nm_hash (void) { + g_assert (nm_hash_static (0)); + g_assert (nm_hash_static (777)); + _test_hash_str (""); _test_hash_str ("a"); _test_hash_str ("aa"); diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c index c563140e3a..f1e4bd9f9e 100644 --- a/shared/nm-utils/nm-hash-utils.c +++ b/shared/nm-utils/nm-hash-utils.c @@ -39,29 +39,62 @@ static const guint8 * _get_hash_key (void) { static const guint8 *volatile global_seed = NULL; + /* the returned hash is aligned to guin64, hence, it is safe + * to use it as guint* or guint64* pointer. */ + static union { + guint8 v8[HASH_KEY_SIZE]; + } g_arr _nm_alignas (guint64); + static gsize g_lock; const guint8 *g; + struct siphash siph_state; + uint64_t h; + guint *p; g = global_seed; - if (G_UNLIKELY (g == NULL)) { - /* the returned hash is aligned to guin64, hence, it is save - * to use it as guint* or guint64* pointer. */ - static union { - guint8 v8[HASH_KEY_SIZE]; - } g_arr _nm_alignas (guint64); - static gsize g_lock; - - if (g_once_init_enter (&g_lock)) { - nm_utils_random_bytes (g_arr.v8, sizeof (g_arr.v8)); - g_atomic_pointer_compare_and_exchange (&global_seed, NULL, g_arr.v8); - g = g_arr.v8; - g_once_init_leave (&g_lock, 1); - } else { - g = global_seed; - nm_assert (g); - } + if (G_LIKELY (g != NULL)) { + nm_assert (g == g_arr.v8); + return g; } - return g; + if (g_once_init_enter (&g_lock)) { + + nm_utils_random_bytes (g_arr.v8, sizeof (g_arr.v8)); + + /* use siphash() of the key-size, to mangle the first guint. Otherwise, + * the first guint has only the entropy that nm_utils_random_bytes() + * generated for the first 4 bytes and relies on a good random generator. */ + siphash24_init (&siph_state, g_arr.v8); + siphash24_compress (g_arr.v8, sizeof (g_arr.v8), &siph_state); + h = siphash24_finalize (&siph_state); + p = (guint *) g_arr.v8; + if (sizeof (guint) < sizeof (h)) + *p = *p ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32)); + else + *p = *p ^ ((guint) (h & 0xFFFFFFFFu)); + + g_atomic_pointer_compare_and_exchange (&global_seed, NULL, g_arr.v8); + g_once_init_leave (&g_lock, 1); + } + + nm_assert (global_seed == g_arr.v8); + return g_arr.v8; +} + +guint +nm_hash_static (guint static_seed) +{ + /* note that we only xor the static_seed with the key. + * We don't use siphash24(), which would mix the bits better. + * Note that this doesn't matter, because static_seed is not + * supposed to be a value that you are hashing (for that, use + * full siphash24()). + * Instead, different callers may set a different static_seed + * so that nm_hash_str(NULL) != nm_hash_ptr(NULL). + * + * Also, ensure that we don't return zero. + */ + return ((*((const guint *) _get_hash_key ())) ^ static_seed) + ?: static_seed ?: 3679500967u; } void diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h index 276e1ebe02..21396f2814 100644 --- a/shared/nm-utils/nm-hash-utils.h +++ b/shared/nm-utils/nm-hash-utils.h @@ -31,6 +31,8 @@ struct _NMHashState { typedef struct _NMHashState NMHashState; +guint nm_hash_static (guint static_seed); + void nm_hash_init (NMHashState *state, guint static_seed); static inline guint