diff --git a/include/common/standard.h b/include/common/standard.h index f9eed961f..d8bccbafb 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -1550,6 +1550,21 @@ static inline void *my_realloc2(void *ptr, size_t size) int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz); +/* PRNG */ +void ha_random_seed(const unsigned char *seed, size_t len); +void ha_random_jump96(uint32_t dist); +uint64_t ha_random64(); + +static inline uint32_t ha_random32() +{ + return ha_random64() >> 32; +} + +static inline int32_t ha_random() +{ + return ha_random32() >> 1; +} + /* HAP_STRING() makes a string from a literal while HAP_XSTRING() first * evaluates the argument and is suited to pass macros. * diff --git a/src/51d.c b/src/51d.c index b00f01844..42d192967 100644 --- a/src/51d.c +++ b/src/51d.c @@ -700,7 +700,7 @@ static int init_51degrees(void) free(_51d_property_list); #ifdef FIFTYONEDEGREES_H_PATTERN_INCLUDED - _51d_lru_seed = random(); + _51d_lru_seed = ha_random(); if (global_51degrees.cache_size) { _51d_lru_tree = lru64_new(global_51degrees.cache_size); } diff --git a/src/backend.c b/src/backend.c index 251a7a5a4..87e3b9a1c 100644 --- a/src/backend.c +++ b/src/backend.c @@ -541,7 +541,7 @@ static struct server *get_server_rnd(struct stream *s, const struct server *avoi do { prev = curr; /* ensure all 32 bits are covered as long as RAND_MAX >= 65535 */ - hash = ((uint64_t)random() * ((uint64_t)RAND_MAX + 1)) ^ random(); + hash = ((uint64_t)ha_random() * ((uint64_t)RAND_MAX + 1)) ^ ha_random(); curr = chash_get_server_hash(px, hash, avoid); if (!curr) break; diff --git a/src/flt_spoe.c b/src/flt_spoe.c index d54fcd437..bcdec08e7 100644 --- a/src/flt_spoe.c +++ b/src/flt_spoe.c @@ -269,7 +269,7 @@ generate_pseudo_uuid() while (byte < 4) { while (bits < 32) { - last |= (uint64_t)random() << bits; + last |= (uint64_t)ha_random() << bits; bits += rand_max_bits; } rnd[byte++] = last; @@ -3109,10 +3109,6 @@ spoe_init_per_thread(struct proxy *p, struct flt_conf *fconf) struct spoe_config *conf = fconf->conf; struct spoe_agent *agent = conf->agent; - /* Use a != seed per process */ - if (relative_pid > 1 && tid == 0) - srandom(now_ms * pid); - agent->rt[tid].engine_id = generate_pseudo_uuid(); if (agent->rt[tid].engine_id == NULL) return -1; diff --git a/src/flt_trace.c b/src/flt_trace.c index 5a26fabea..b06ba150b 100644 --- a/src/flt_trace.c +++ b/src/flt_trace.c @@ -468,7 +468,7 @@ trace_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg unsigned int data = trace_get_htx_datalen(htxbuf(&msg->chn->buf), offset, len); if (data) { - ret = random() % (ret+1); + ret = ha_random() % (ret+1); if (!ret || ret >= data) ret = len; } @@ -536,7 +536,7 @@ trace_tcp_payload(struct stream *s, struct filter *filter, struct channel *chn, unsigned int data = trace_get_htx_datalen(htxbuf(&chn->buf), offset, len); if (data) { - ret = random() % (ret+1); + ret = ha_random() % (ret+1); if (!ret || ret >= data) ret = len; } @@ -554,7 +554,7 @@ trace_tcp_payload(struct stream *s, struct filter *filter, struct channel *chn, else { if (ret && conf->rand_forwarding) - ret = random() % (ret+1); + ret = ha_random() % (ret+1); FLT_STRM_TRACE(conf, s, "%-25s: channel=%-10s - mode=%-5s (%s) - " "offset=%u - len=%u - forward=%d", diff --git a/src/haproxy.c b/src/haproxy.c index ef9010f95..180dbdb43 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -1374,6 +1374,10 @@ static char **copy_argv(int argc, char **argv) * We initialize the current process with the first 32 bits before starting the * polling loop, where all this will be changed to have process specific and * thread specific sequences. + * + * Before starting threads, it's still possible to call random() as srandom() + * is initialized from this, but after threads and/or processes are started, + * only ha_random() is expected to be used to guarantee distinct sequences. */ static void ha_random_boot(char *const *argv) { @@ -1444,6 +1448,7 @@ static void ha_random_boot(char *const *argv) blk_SHA1_Final(boot_seed, &ctx); srandom(read_u32(boot_seed)); + ha_random_seed(boot_seed, sizeof(boot_seed)); } /* considers splicing proxies' maxconn, computes the ideal global.maxpipes @@ -3248,8 +3253,10 @@ int main(int argc, char **argv) protocol_unbind_all(); exit(1); /* there has been an error */ } - else if (ret == 0) /* child breaks here */ + else if (ret == 0) { /* child breaks here */ + ha_random_jump96(relative_pid); break; + } if (pidfd >= 0 && !(global.mode & MODE_MWORKER)) { char pidstr[100]; snprintf(pidstr, sizeof(pidstr), "%d\n", ret); diff --git a/src/memory.c b/src/memory.c index d1aec5925..0ff3ea8b8 100644 --- a/src/memory.c +++ b/src/memory.c @@ -628,7 +628,7 @@ int mem_should_fail(const struct pool_head *pool) int n; if (mem_fail_rate > 0 && !(global.mode & MODE_STARTING)) { - int randnb = random() % 100; + int randnb = ha_random() % 100; if (mem_fail_rate > randnb) ret = 1; diff --git a/src/pattern.c b/src/pattern.c index 8dfe3cf29..3ea1f33d4 100644 --- a/src/pattern.c +++ b/src/pattern.c @@ -2667,7 +2667,7 @@ int pattern_finalize_config(void) struct pat_ref *ref, **arr; struct list pr = LIST_HEAD_INIT(pr); - pat_lru_seed = random(); + pat_lru_seed = ha_random(); /* Count pat_refs with user defined unique_id and totalt count */ list_for_each_entry(ref, &pattern_reference, list) { diff --git a/src/peers.c b/src/peers.c index f5a4f1865..640a99f5e 100644 --- a/src/peers.c +++ b/src/peers.c @@ -2232,7 +2232,7 @@ switchstate: * retrying otherwise the other end will do the same and we can loop * for a while. */ - curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000)); + curpeer->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000)); peer_session_forceshutdown(curpeer); } if (maj_ver != (unsigned int)-1 && min_ver != (unsigned int)-1) { @@ -2685,7 +2685,7 @@ static struct task *process_peer_sync(struct task * task, void *context, unsigne ps->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT)); } else { - ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000)); + ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000)); peer_session_forceshutdown(ps); ps->no_hbt++; } @@ -2741,7 +2741,7 @@ static struct task *process_peer_sync(struct task * task, void *context, unsigne * retrying otherwise the other end will do the same and we can loop * for a while. */ - ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000)); + ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + ha_random() % 2000)); if (ps->appctx) { peer_session_forceshutdown(ps); } diff --git a/src/sample.c b/src/sample.c index 3c6111224..fd63902a9 100644 --- a/src/sample.c +++ b/src/sample.c @@ -3124,7 +3124,7 @@ smp_fetch_thread(const struct arg *args, struct sample *smp, const char *kw, voi static int smp_fetch_rand(const struct arg *args, struct sample *smp, const char *kw, void *private) { - smp->data.u.sint = random(); + smp->data.u.sint = ha_random(); /* reduce if needed. Don't do a modulo, use all bits! */ if (args && args[0].type == ARGT_SINT) @@ -3336,7 +3336,7 @@ static int smp_fetch_uuid(const struct arg *args, struct sample *smp, const char while (byte < 4) { while (bits < 32) { - last |= (uint64_t)random() << bits; + last |= (uint64_t)ha_random() << bits; bits += rand_max_bits; } rnd[byte++] = last; diff --git a/src/standard.c b/src/standard.c index 38997d5f4..5ccf44776 100644 --- a/src/standard.c +++ b/src/standard.c @@ -4528,6 +4528,118 @@ int varint_bytes(uint64_t v) return len; } + +/* Random number generator state, see below */ +static uint64_t ha_random_state[2]; + +/* This is a thread-safe implementation of xoroshiro128** described below: + * http://prng.di.unimi.it/ + * It features a 2^128 long sequence, returns 64 high-quality bits on each call, + * supports fast jumps and passes all common quality tests. It is thread-safe, + * uses a double-cas on 64-bit architectures supporting it, and falls back to a + * local lock on other ones. + */ +uint64_t ha_random64() +{ + uint64_t result; + uint64_t old[2]; + uint64_t new[2]; + +#if defined(USE_THREAD) && (!defined(HA_CAS_IS_8B) || !defined(HA_HAVE_CAS_DW)) + static HA_SPINLOCK_T rand_lock; + + HA_SPIN_LOCK(OTHER_LOCK, &rand_lock); +#endif + + old[0] = ha_random_state[0]; + old[1] = ha_random_state[1]; + +#if defined(USE_THREAD) && defined(HA_CAS_IS_8B) && defined(HA_HAVE_CAS_DW) + do { +#endif + result = rotl64(old[0] * 5, 7) * 9; + new[1] = old[0] ^ old[1]; + new[0] = rotl64(old[0], 24) ^ new[1] ^ (new[1] << 16); // a, b + new[1] = rotl64(new[1], 37); // c + +#if defined(USE_THREAD) && defined(HA_CAS_IS_8B) && defined(HA_HAVE_CAS_DW) + } while (unlikely(!_HA_ATOMIC_DWCAS(ha_random_state, old, new))); +#else + ha_random_state[0] = new[0]; + ha_random_state[1] = new[1]; +#if defined(USE_THREAD) + HA_SPIN_UNLOCK(OTHER_LOCK, &rand_lock); +#endif +#endif + return result; +} + +/* seeds the random state using up to bytes from , starting with + * the first non-zero byte. + */ +void ha_random_seed(const unsigned char *seed, size_t len) +{ + size_t pos; + + /* the seed must not be all zeroes, so we pre-fill it with alternating + * bits and overwrite part of them with the block starting at the first + * non-zero byte from the seed. + */ + memset(ha_random_state, 0x55, sizeof(ha_random_state)); + + for (pos = 0; pos < len; pos++) + if (seed[pos] != 0) + break; + + if (pos == len) + return; + + seed += pos; + len -= pos; + + if (len > sizeof(ha_random_state)) + len = sizeof(ha_random_state); + + memcpy(ha_random_state, seed, len); +} + +/* This causes a jump to (dist * 2^96) places in the pseudo-random sequence, + * and is equivalent to calling ha_random64() as many times. It is used to + * provide non-overlapping sequences of 2^96 numbers (~7*10^28) to up to 2^32 + * different generators (i.e. different processes after a fork). The + * argument is the distance to jump to and is used in a loop so it rather not + * be too large if the processing time is a concern. + * + * BEWARE: this function is NOT thread-safe and must not be called during + * concurrent accesses to ha_random64(). + */ +void ha_random_jump96(uint32_t dist) +{ + while (dist--) { + uint64_t s0 = 0; + uint64_t s1 = 0; + int b; + + for (b = 0; b < 64; b++) { + if ((0xd2a98b26625eee7bULL >> b) & 1) { + s0 ^= ha_random_state[0]; + s1 ^= ha_random_state[1]; + } + ha_random64(); + } + + for (b = 0; b < 64; b++) { + if ((0xdddf9b1090aa7ac1ULL >> b) & 1) { + s0 ^= ha_random_state[0]; + s1 ^= ha_random_state[1]; + } + ha_random64(); + } + ha_random_state[0] = s0; + ha_random_state[1] = s1; + } +} + /* * Local variables: * c-indent-level: 8