]> granicus.if.org Git - libevent/commitdiff
Tweak the new evutil_weakrand_() code
authorNick Mathewson <nickm@torproject.org>
Mon, 9 Apr 2012 15:30:46 +0000 (11:30 -0400)
committerNick Mathewson <nickm@torproject.org>
Mon, 9 Apr 2012 15:30:46 +0000 (11:30 -0400)
Make its state actually get seeded.

Document it more thoroughly.

Turn its state into a structure.

Fix a bug in evutil_weakrand_range_() where it could return the top of
the range.

Change its return type to ev_int32_t.

Add a quick unit test to make sure that the value of
evutil_weakrand_range_() is in range.

bufferevent-internal.h
bufferevent_ratelim.c
event-internal.h
evutil.c
poll.c
select.c
test/regress_buffer.c
test/regress_util.c
util-internal.h
win32select.c

index ad2c25c75df7c2b2ea3fe4370c0a8fd1ca809284..461c46e76ca3a95b00dbbb04615969acd7072d95 100644 (file)
@@ -105,8 +105,8 @@ struct bufferevent_rate_limit_group {
         * to refill. */
        struct event master_refill_event;
 
-       /** Seed for weak random number generator. */
-       ev_uint32_t weakrand_seed;
+       /** 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
index 4272c0da9e77e2814aac57af53501496527e1ad0..f7de86a9309ece739cfca1007562a7e75dad9630 100644 (file)
@@ -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)
 {
@@ -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;
 }
 
index 4c8820568423fd9686e1f926c100702e7994810f..78b3fe63acfa2c28f0f8a0e350ecbd1233a793bb 100644 (file)
@@ -291,8 +291,9 @@ struct event_base {
        /** 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;
+       /** 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 {
index a87b524621fd762f31ac90f44ce5821506aa7d06..9307d259d310cc52e5fff0c2bb8f5cff4ad9052a 100644 (file)
--- a/evutil.c
+++ b/evutil.c
@@ -2274,21 +2274,50 @@ evutil_getenv_(const char *varname)
 }
 
 ev_uint32_t
-evutil_weakrand_(ev_uint32_t* seed)
+evutil_weakrand_seed_(struct evutil_weakrand_state *state, ev_uint32_t seed)
 {
-       *seed = ((*seed) * 1103515245 + 12345) & 0x7fffffff;
-       return (*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
+               seed += (ev_uint32_t) _getpid();
+#else
+               seed += (ev_uint32_t) getpid();
+#endif
+       }
+       state->seed = seed;
+       return seed;
 }
 
-ev_uint32_t
-evutil_weakrand_range_(ev_uint32_t* seed, ev_uint32_t top)
+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_uint32_t divisor, result;
+       ev_int32_t divisor, result;
 
-       divisor = EV_INT32_MAX / top;
-       do
-               result = evutil_weakrand_(seed) / divisor;
-       while (result > top);
+       /* 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<N will divide the modulus, and so
+        * therefore the low m bits of the LCG will have period 2^m.) */
+       divisor = EVUTIL_WEAKRAND_MAX / top;
+       do {
+               result = evutil_weakrand_(state) / divisor;
+       } while (result >= top);
        return result;
 }
 
diff --git a/poll.c b/poll.c
index 3ac427acbd92663e93d175e39095536a6836aedb..0114485481044d910544345f16c080db21e1732e 100644 (file)
--- 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);
 }
 
index f9a0c20658a7396a3c690f603d6a6a44f234ce48..8ae53cc11ec56afa7a81dee8310c17e47350fed1 100644 (file)
--- 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);
 }
 
index 2e4ef176a29b858f125fde1b3e65beb9d1baeeb0..d183b4f9db75568fe4332a24543f7f5493eb7832 100644 (file)
@@ -699,7 +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;
+       struct evutil_weakrand_state seed = { 123456789U };
 
        /* This test is highly parameterized based on substrings of its
         * argument.  The strings are: */
index 2d53ba8dd1b31fd6ccb752453753c89bcc242c73..6aaeeacfd7a3f62f1c4fbcd2508de8c76e3851e5 100644 (file)
@@ -829,7 +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;
+       struct evutil_weakrand_state seed = { 12346789U };
 
        memset(buf2, 0, sizeof(buf2));
        memset(counts, 0, sizeof(counts));
@@ -869,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:
        ;
index 291feb8a51ff7085b10e1e3a0c2f8d1322afa89e..5248ab21dd1520552a8f51c68e07771480b316af 100644 (file)
@@ -254,8 +254,33 @@ int evutil_resolve_(int family, const char *hostname, struct sockaddr *sa,
 
 const char *evutil_getenv_(const char *name);
 
-ev_uint32_t evutil_weakrand_(ev_uint32_t* seed);
-ev_uint32_t evutil_weakrand_range_(ev_uint32_t* seed, ev_uint32_t top);
+/* 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. */
index ce2d525f21eb34f100846a9f27ade3d065fa9f99..7be2389f38d7381abf97bd81ea41109178c6146f 100644 (file)
@@ -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);