MINOR: tools: provide a function to generate a hashed random pair

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.
This commit is contained in:
Willy Tarreau 2026-05-25 17:30:58 +02:00
parent de266d9a99
commit 7b7b33df53
3 changed files with 38 additions and 0 deletions

View 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 */

View 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;

View 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.
*/