summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2020-03-24 11:31:27 +0100
committerThomas Haller <thaller@redhat.com>2020-04-03 11:26:49 +0200
commitb6fdc30a887df55238e264e879e4f26e6a9634b3 (patch)
tree8069916f0697c67261f3b2bd73fb4c8d84c9afc5
parent573b02f7d7451e89009f5cb9c2eb28dac85ffe50 (diff)
downloadNetworkManager-b6fdc30a887df55238e264e879e4f26e6a9634b3.tar.gz
shared: cleanup _get_hash_key_init() and better explain the reasoning
- add more code comments - refactor the code flow in _get_hash_key_init() to follow a simpler code path. - use c_siphash_hash() instead of 3 separate steps. - Drop "?: static_seed" from nm_hash_static(). It's not useful, because the only _get_hash_key() for which _get_hash_key()^static_seed is zero is ~static_seed. That means, only one value of all the static seeds can result in zero here. At that point, we can just coerce that value to 3679500967u directly.
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.c90
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.h3
2 files changed, 49 insertions, 44 deletions
diff --git a/shared/nm-glib-aux/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c
index dcf662053a..0a701d06e1 100644
--- a/shared/nm-glib-aux/nm-hash-utils.c
+++ b/shared/nm-glib-aux/nm-hash-utils.c
@@ -24,7 +24,6 @@ static const guint8 *volatile global_seed = NULL;
static const guint8 *
_get_hash_key_init (void)
{
- static gsize g_lock;
/* the returned hash is aligned to guin64, hence, it is safe
* to use it as guint* or guint64* pointer. */
static union {
@@ -34,50 +33,49 @@ _get_hash_key_init (void)
guint64 _align_as_uint64;
} g_arr;
const guint8 *g;
- union {
- guint8 v8[HASH_KEY_SIZE];
- guint vuint;
- } t_arr;
again:
g = g_atomic_pointer_get (&global_seed);
- if (G_LIKELY (g != NULL)) {
- nm_assert (g == g_arr.v8);
- return g;
- }
-
- {
- CSipHash siph_state;
+ if (!G_UNLIKELY (g)) {
+ static gsize g_lock;
uint64_t h;
-
- /* initialize a random key in t_arr. */
+ union {
+ guint vuint;
+ guint8 v8[HASH_KEY_SIZE];
+ guint8 _extra_entropy[3 * HASH_KEY_SIZE];
+ } t_arr;
nm_utils_random_bytes (&t_arr, sizeof (t_arr));
- /* 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.
+ /* We only initialize one random hash key. So we can spend some effort
+ * of getting this right. For one, we collect more random bytes than
+ * necessary.
*
- * The first int is especially interesting for nm_hash_static() below, and we
- * want to have it all the entropy of t_arr. */
- c_siphash_init (&siph_state, t_arr.v8);
- c_siphash_append (&siph_state, (const guint8 *) &t_arr, sizeof (t_arr));
- h = c_siphash_finalize (&siph_state);
- if (sizeof (guint) < sizeof (h))
- t_arr.vuint = t_arr.vuint ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32));
+ * Then, the first guint of the seed should have all the entropy that we could
+ * obtain in sizeof(t_arr). For that, siphash(t_arr) and xor the first guint
+ * with hash.
+ * The first guint is especially interesting for nm_hash_static() below that
+ * doesn't use siphash itself. */
+ h = c_siphash_hash (t_arr.v8,
+ (const guint8 *) &t_arr,
+ sizeof (t_arr));
+ if (sizeof (h) > sizeof (guint))
+ t_arr.vuint = t_arr.vuint ^ ((guint) (h & G_MAXUINT)) ^ ((guint) (h >> 32));
else
- t_arr.vuint = t_arr.vuint ^ ((guint) (h & 0xFFFFFFFFu));
- }
+ t_arr.vuint = t_arr.vuint ^ ((guint) (h & G_MAXUINT));
+
+ if (!g_once_init_enter (&g_lock)) {
+ /* lost a race. The random key is already initialized. */
+ goto again;
+ }
- if (!g_once_init_enter (&g_lock)) {
- /* lost a race. The random key is already initialized. */
- goto again;
+ memcpy (g_arr.v8, t_arr.v8, HASH_KEY_SIZE);
+ g = g_arr.v8;
+ g_atomic_pointer_set (&global_seed, g);
+ g_once_init_leave (&g_lock, 1);
}
- memcpy (g_arr.v8, t_arr.v8, HASH_KEY_SIZE);
- g = g_arr.v8;
- g_atomic_pointer_set (&global_seed, g);
- g_once_init_leave (&g_lock, 1);
+ nm_assert (g == g_arr.v8);
return g;
}
@@ -94,18 +92,24 @@ again:
guint
nm_hash_static (guint static_seed)
{
- /* note that we only xor the static_seed with the key.
- * We don't use siphash, 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 siphash).
- * Instead, different callers may set a different static_seed
- * so that nm_hash_str(NULL) != nm_hash_ptr(NULL).
+ /* Note that we only xor the static_seed with the first guint of the key.
+ *
+ * We don't use siphash, which would mix the bits better with _get_hash_key().
+ * Note that nm_hash_static() isn't used to hash the static_seed. Instead, it
+ * is used to get a unique hash value in a static context. That means, every
+ * caller is responsible to choose a static_seed that is sufficiently
+ * distinct from all other callers. In other words, static_seed should be a
+ * unique constant with good entropy.
+ *
+ * Note that _get_hash_key_init() already xored the first guint of the
+ * key with the siphash of the entire static key. That means, even if
+ * we got bad randomness for the first guint, the first guint is also
+ * mixed with the randomness of the entire random key.
*
- * Also, ensure that we don't return zero.
+ * Also, ensure that we don't return zero (like for nm_hash_complete()).
*/
- return ((*((const guint *) _get_hash_key ())) ^ static_seed)
- ?: static_seed ?: 3679500967u;
+ return ((*((const guint *) _get_hash_key ())) ^ static_seed)
+ ?: 3679500967u;
}
void
diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h
index 9879dc477b..9f2e976678 100644
--- a/shared/nm-glib-aux/nm-hash-utils.h
+++ b/shared/nm-glib-aux/nm-hash-utils.h
@@ -88,7 +88,8 @@ nm_hash_complete (NMHashState *state)
/* we don't ever want to return a zero hash.
*
* NMPObject requires that in _idx_obj_part(), and it's just a good idea. */
- return (((guint) (h >> 32)) ^ ((guint) h)) ?: 1396707757u;
+ return (((guint) (h >> 32)) ^ ((guint) h))
+ ?: 1396707757u;
}
static inline void