diff --git a/bufferevent-internal.h b/bufferevent-internal.h index 400c6d96..461c46e7 100644 --- a/bufferevent-internal.h +++ b/bufferevent-internal.h @@ -104,6 +104,10 @@ struct bufferevent_rate_limit_group { /** Timeout event that goes off once a tick, when the bucket is ready * to refill. */ struct event master_refill_event; + + /** Seed for weak random number generator. Protected by 'lock' */ + struct evutil_weakrand_state weakrand_seed; + /** Lock to protect the members of this group. This lock should nest * within every bufferevent lock: if you are holding this lock, do * not assume you can lock another bufferevent. */ diff --git a/bufferevent_ratelim.c b/bufferevent_ratelim.c index 02c50228..f7de86a9 100644 --- a/bufferevent_ratelim.c +++ b/bufferevent_ratelim.c @@ -438,7 +438,10 @@ bev_refill_callback_(evutil_socket_t fd, short what, void *arg) BEV_UNLOCK(&bev->bev); } -/** Helper: grab a random element from a bufferevent group. */ +/** Helper: grab a random element from a bufferevent group. + * + * Requires that we hold the lock on the group. + */ static struct bufferevent_private * bev_group_random_element_(struct bufferevent_rate_limit_group *group) { @@ -452,7 +455,7 @@ bev_group_random_element_(struct bufferevent_rate_limit_group *group) EVUTIL_ASSERT(! LIST_EMPTY(&group->members)); - which = evutil_weakrand_() % group->n_members; + which = evutil_weakrand_range_(&group->weakrand_seed, group->n_members); bev = LIST_FIRST(&group->members); while (which--) @@ -660,6 +663,9 @@ bufferevent_rate_limit_group_new(struct event_base *base, bufferevent_rate_limit_group_set_min_share(g, 64); + evutil_weakrand_seed_(&g->weakrand_seed, + (ev_uint32_t) ((now.tv_sec + now.tv_usec) + (ev_intptr_t)g)); + return g; } diff --git a/event-internal.h b/event-internal.h index f840058d..78b3fe63 100644 --- a/event-internal.h +++ b/event-internal.h @@ -290,6 +290,10 @@ struct event_base { struct event th_notify; /** A function used to wake up the main thread from another thread. */ int (*th_notify_fn)(struct event_base *base); + + /** Saved seed for weak random number generator. Some backends use + * this to produce fairness among sockets. Protected by th_base_lock. */ + struct evutil_weakrand_state weakrand_seed; }; struct event_config_entry { diff --git a/evutil.c b/evutil.c index abe15604..9307d259 100644 --- a/evutil.c +++ b/evutil.c @@ -2273,14 +2273,52 @@ evutil_getenv_(const char *varname) return getenv(varname); } -long -evutil_weakrand_(void) +ev_uint32_t +evutil_weakrand_seed_(struct evutil_weakrand_state *state, ev_uint32_t seed) { + if (seed == 0) { + struct timeval tv; + evutil_gettimeofday(&tv, NULL); + seed = (ev_uint32_t)tv.tv_sec + (ev_uint32_t)tv.tv_usec; #ifdef _WIN32 - return rand(); + seed += (ev_uint32_t) _getpid(); #else - return random(); + seed += (ev_uint32_t) getpid(); #endif + } + state->seed = seed; + return seed; +} + +ev_int32_t +evutil_weakrand_(struct evutil_weakrand_state *state) +{ + /* This RNG implementation is a linear congruential generator, with + * modulus 2^31, multiplier 1103515245, and addend 12345. It's also + * used by OpenBSD, and by Glibc's TYPE_0 RNG. + * + * The linear congruential generator is not an industrial-strength + * RNG! It's fast, but it can have higher-order patterns. Notably, + * the low bits tend to have periodicity. + */ + state->seed = ((state->seed) * 1103515245 + 12345) & 0x7fffffff; + return (ev_int32_t)(state->seed); +} + +ev_int32_t +evutil_weakrand_range_(struct evutil_weakrand_state *state, ev_int32_t top) +{ + ev_int32_t divisor, result; + + /* We can't just do weakrand() % top, since the low bits of the LCG + * are less random than the high ones. (Specifically, since the LCG + * modulus is 2^N, every 2^m for m= top); + return result; } int diff --git a/poll.c b/poll.c index 744c7141..01144854 100644 --- a/poll.c +++ b/poll.c @@ -93,6 +93,8 @@ poll_init(struct event_base *base) evsig_init_(base); + evutil_weakrand_seed_(&base->weakrand_seed, 0); + return (pollop); } @@ -183,7 +185,7 @@ poll_dispatch(struct event_base *base, struct timeval *tv) if (res == 0 || nfds == 0) return (0); - i = random() % nfds; + i = evutil_weakrand_range_(&base->weakrand_seed, nfds); for (j = 0; j < nfds; j++) { int what; if (++i == nfds) diff --git a/select.c b/select.c index e1d4987c..8ae53cc1 100644 --- a/select.c +++ b/select.c @@ -121,6 +121,8 @@ select_init(struct event_base *base) evsig_init_(base); + evutil_weakrand_seed_(&base->weakrand_seed, 0); + return (sop); } @@ -186,7 +188,7 @@ select_dispatch(struct event_base *base, struct timeval *tv) event_debug(("%s: select reports %d", __func__, res)); check_selectop(sop); - i = random() % nfds; + i = evutil_weakrand_range_(&base->weakrand_seed, nfds); for (j = 0; j < nfds; ++j) { if (++i >= nfds) i = 0; diff --git a/test/regress_buffer.c b/test/regress_buffer.c index dfb680b5..d183b4f9 100644 --- a/test/regress_buffer.c +++ b/test/regress_buffer.c @@ -699,6 +699,7 @@ test_evbuffer_add_file(void *ptr) struct event *rev=NULL, *wev=NULL; struct event_base *base = testdata->base; evutil_socket_t pair[2] = {-1, -1}; + struct evutil_weakrand_state seed = { 123456789U }; /* This test is highly parameterized based on substrings of its * argument. The strings are: */ @@ -757,7 +758,7 @@ test_evbuffer_add_file(void *ptr) data = malloc(1024*512); tt_assert(data); for (i = 0; i < datalen; ++i) - data[i] = (char)evutil_weakrand_(); + data[i] = (char)evutil_weakrand_(&seed); } else { data = strdup("here is a relatively small string."); tt_assert(data); diff --git a/test/regress_util.c b/test/regress_util.c index e7662e0f..6aaeeacf 100644 --- a/test/regress_util.c +++ b/test/regress_util.c @@ -829,6 +829,7 @@ test_evutil_rand(void *arg) char buf2[32]; int counts[256]; int i, j, k, n=0; + struct evutil_weakrand_state seed = { 12346789U }; memset(buf2, 0, sizeof(buf2)); memset(counts, 0, sizeof(counts)); @@ -836,8 +837,8 @@ test_evutil_rand(void *arg) for (k=0;k<32;++k) { /* Try a few different start and end points; try to catch * the various misaligned cases of arc4random_buf */ - int startpoint = evutil_weakrand_() % 4; - int endpoint = 32 - (evutil_weakrand_() % 4); + int startpoint = evutil_weakrand_(&seed) % 4; + int endpoint = 32 - (evutil_weakrand_(&seed) % 4); memset(buf2, 0, sizeof(buf2)); @@ -868,6 +869,13 @@ test_evutil_rand(void *arg) } } + evutil_weakrand_seed_(&seed, 0); + for (i = 0; i < 10000; ++i) { + ev_int32_t r = evutil_weakrand_range_(&seed, 9999); + tt_int_op(0, <=, r); + tt_int_op(r, <, 9999); + } + /* for (i=0;i<256;++i) { printf("%3d %2d\n", i, counts[i]); } */ end: ; diff --git a/util-internal.h b/util-internal.h index 508eae92..5248ab21 100644 --- a/util-internal.h +++ b/util-internal.h @@ -254,7 +254,33 @@ int evutil_resolve_(int family, const char *hostname, struct sockaddr *sa, const char *evutil_getenv_(const char *name); -long evutil_weakrand_(void); +/* Structure to hold the state of our weak random number generator. + */ +struct evutil_weakrand_state { + ev_uint32_t seed; +}; + +#define EVUTIL_WEAKRAND_MAX EV_INT32_MAX + +/* Initialize the state of a week random number generator based on 'seed'. If + * the seed is 0, construct a new seed based on not-very-strong platform + * entropy, like the PID and the time of day. + * + * This function, and the other evutil_weakrand* functions, are meant for + * speed, not security or statistical strength. If you need a RNG which an + * attacker can't predict, or which passes strong statistical tests, use the + * evutil_secure_rng* functions instead. + */ +ev_uint32_t evutil_weakrand_seed_(struct evutil_weakrand_state *state, ev_uint32_t seed); +/* Return a pseudorandom value between 0 and EVUTIL_WEAKRAND_MAX inclusive. + * Updates the state in 'seed' as needed -- this value must be protected by a + * lock. + */ +ev_int32_t evutil_weakrand_(struct evutil_weakrand_state *seed); +/* Return a pseudorandom value x such that 0 <= x < top. top must be no more + * than EVUTIL_WEAKRAND_MAX. Updates the state in 'seed' as needed -- this + * value must be proteced by a lock */ +ev_int32_t evutil_weakrand_range_(struct evutil_weakrand_state *seed, ev_int32_t top); /* Evaluates to the same boolean value as 'p', and hints to the compiler that * we expect this value to be false. */ diff --git a/win32select.c b/win32select.c index 441222f1..7be2389f 100644 --- a/win32select.c +++ b/win32select.c @@ -202,6 +202,8 @@ win32_init(struct event_base *base) if (evsig_init_(base) < 0) winop->signals_are_broken = 1; + evutil_weakrand_seed_(&base->weakrand_seed, 0); + return (winop); err: XFREE(winop->readset_in); @@ -326,7 +328,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv) } if (win32op->readset_out->fd_count) { - i = rand() % win32op->readset_out->fd_count; + i = evutil_weakrand_range_(&base->weakrand_seed, + win32op->readset_out->fd_count); for (j=0; jreadset_out->fd_count; ++j) { if (++i >= win32op->readset_out->fd_count) i = 0; @@ -335,7 +338,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv) } } if (win32op->exset_out->fd_count) { - i = rand() % win32op->exset_out->fd_count; + i = evutil_weakrand_range_(&base->weakrand_seed, + win32op->exset_out->fd_count); for (j=0; jexset_out->fd_count; ++j) { if (++i >= win32op->exset_out->fd_count) i = 0; @@ -345,7 +349,8 @@ win32_dispatch(struct event_base *base, struct timeval *tv) } if (win32op->writeset_out->fd_count) { SOCKET s; - i = rand() % win32op->writeset_out->fd_count; + i = evutil_weakrand_range_(&base->weakrand_seed, + win32op->writeset_out->fd_count); for (j=0; jwriteset_out->fd_count; ++j) { if (++i >= win32op->writeset_out->fd_count) i = 0;