]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: tools: provide a function to generate a hashed random pair
authorWilly Tarreau <w@1wt.eu>
Mon, 25 May 2026 15:30:58 +0000 (17:30 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 26 May 2026 11:13:24 +0000 (13:13 +0200)
A lot of places call two ha_random64() in a row to generate a 128-bit
random. While it's now safe against linear analysis thanks to the XXH64
call, it's still particularly expensive due to the lock.

Here we introduce a new function ha_random64_pair_hashed(), that feeds
two uint64_t with a hash of the PRNG's internal state, and make it
advance. This will cut in half the number of calls to ha_random64()
and should recover a part of the performance lost in the lock. For
now it's not used.

include/haproxy/tools-t.h
include/haproxy/tools.h
src/tools.c

index 77911dd85d9590993f868d648081cefa0eca74f1..792a0f650430f052832da4b45327a2482daf58c7 100644 (file)
@@ -188,4 +188,12 @@ struct file_name_node {
        char name[VAR_ARRAY]; /* storage, used with cebus_*() */
 };
 
+/* a pair of uint64_t. It's purposely arranged in little endian to help
+ * being vectorized on modern processors.
+ */
+struct uint64_pair {
+       uint64_t l;
+       uint64_t h;
+};
+
 #endif /* _HAPROXY_TOOLS_T_H */
index 6e47585098fa53ebeaf2bf9b09d7c26631223b32..3b6e0a3387b90a83e3901204d5d663553980c631 100644 (file)
@@ -1288,6 +1288,8 @@ static inline void _ha_aligned_free(void *ptr)
 int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
 
 /* PRNG */
+struct uint64_pair _ha_random64_pair_hashed(void);
+
 void ha_generate_uuid_v4(struct buffer *output);
 void ha_generate_uuid_v7(struct buffer *output);
 void ha_random_seed(const unsigned char *seed, size_t len);
@@ -1295,6 +1297,17 @@ void ha_random_jump96(uint32_t dist);
 uint64_t ha_random64(void);
 uint64_t ha_random64_internal(void);
 
+/* Returns a pair of uint64_t randoms hashed so as not to disclose the internal
+ * PRNG state.
+ */
+static inline void ha_random64_pair_hashed(uint64_t *l, uint64_t *h)
+{
+       struct uint64_pair ret = _ha_random64_pair_hashed();
+
+       *l = ret.l;
+       *h = ret.h;
+}
+
 static inline uint32_t ha_random32()
 {
        return ha_random64() >> 32;
index 578d38d1f0da8a212be8e25c7da6cd646d066db4..f9825826d302dce5b5c64c7bb2e37689de4e0fbb 100644 (file)
@@ -6297,6 +6297,23 @@ uint64_t ha_random64(void)
                                             now_ns);
 }
 
+/* Returns a pair of uint64_t randoms hashed so as not to disclose the internal
+ * PRNG state. This function shouldn't be used directly, better use the public
+ * ha_random64_pair_hashed() which calls it. The function uses a local XXH
+ * secret that is created at boot, and now_ns as the seed to limit remote
+ * analysis.
+ */
+struct uint64_pair _ha_random64_pair_hashed(void)
+{
+       XXH128_hash_t ret;
+       ret = XXH3_128bits_withSecretandSeed(ha_random_state, 2*sizeof(uint64_t),
+                                            ha_random_xxh_secret, sizeof(ha_random_xxh_secret),
+                                            now_ns);
+       /* update the internal state */
+       ha_random64_internal();
+       return (struct uint64_pair){ .l = ret.low64, .h = ret.high64 };
+}
+
 /* seeds the random state using up to <len> bytes from <seed>, starting with
  * the first non-zero byte.
  */