Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Change evutil_weakrand_() to avoid platform random()

This change allows us to avoid perturbing the platform's random(), and
to avoid hitting locks on random() in the platform's libc.

evutil_weakrand_() is, well, weak, so we choose here an algorithm that
favors speed over a number of other possibly desirable properties.
We're using a linear congruential generator, and taking our parameters
from those shared by the OpenBSD random() implementation, and
Glibc's fastest random() implementation.

The low bits of a LCG of modulus 2^32 are (notoriously) less random
than the higher bits.  So to generate a random value in a range, using
the % operator is no good; we ought to divide.  We add an
evutil_weakrand_range_() function to do that.

This code also changes the interface of evutil_weakrand_() so that it
now manipulates an explicit seed, rather than having the seed in a
static variable.  This change enables us to use existing locks to
achieve thread-safety, rather than having to rely on an additional lock.

(Patch by Nicholas Marriott; commit message by Nick Mathewson.)
  • Loading branch information...
commit e86af4b7e56ed5b7050cb4f41ae534f54748598c 1 parent d9a5515
@nicm nicm authored Nick Mathewson committed
View
4 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. */
+ ev_uint32_t 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. */
View
2  bufferevent_ratelim.c
@@ -452,7 +452,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--)
View
3  event-internal.h
@@ -290,6 +290,9 @@ 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. */
+ ev_uint32_t weakrand_seed;
};
struct event_config_entry {
View
23 evutil.c
@@ -2273,14 +2273,23 @@ evutil_getenv_(const char *varname)
return getenv(varname);
}
-long
-evutil_weakrand_(void)
+ev_uint32_t
+evutil_weakrand_(ev_uint32_t* seed)
{
-#ifdef _WIN32
- return rand();
-#else
- return random();
-#endif
+ *seed = ((*seed) * 1103515245 + 12345) & 0x7fffffff;
+ return (*seed);
+}
+
+ev_uint32_t
+evutil_weakrand_range_(ev_uint32_t* seed, ev_uint32_t top)
+{
+ ev_uint32_t divisor, result;
+
+ divisor = EV_INT32_MAX / top;
+ do
+ result = evutil_weakrand_(seed) / divisor;
+ while (result > top);
+ return result;
}
int
View
2  poll.c
@@ -183,7 +183,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)
View
2  select.c
@@ -186,7 +186,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;
View
3  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};
+ static ev_uint32_t 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);
View
5 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;
+ static ev_uint32_t 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));
View
3  util-internal.h
@@ -254,7 +254,8 @@ int evutil_resolve_(int family, const char *hostname, struct sockaddr *sa,
const char *evutil_getenv_(const char *name);
-long evutil_weakrand_(void);
+ev_uint32_t evutil_weakrand_(ev_uint32_t* seed);
+ev_uint32_t evutil_weakrand_range_(ev_uint32_t* seed, ev_uint32_t top);
/* Evaluates to the same boolean value as 'p', and hints to the compiler that
* we expect this value to be false. */
View
9 win32select.c
@@ -326,7 +326,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; j<win32op->readset_out->fd_count; ++j) {
if (++i >= win32op->readset_out->fd_count)
i = 0;
@@ -335,7 +336,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; j<win32op->exset_out->fd_count; ++j) {
if (++i >= win32op->exset_out->fd_count)
i = 0;
@@ -345,7 +347,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; j<win32op->writeset_out->fd_count; ++j) {
if (++i >= win32op->writeset_out->fd_count)
i = 0;
Please sign in to comment.
Something went wrong with that request. Please try again.