[master] a9b0449 Adopt the guts of FreeBSD's random(3) implementation for a "testable" random bit supply.
Poul-Henning Kamp
phk at FreeBSD.org
Tue Aug 30 00:15:11 CEST 2016
commit a9b044930ac13ee4b661dc13e2e477f922968bad
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date: Mon Aug 29 22:10:31 2016 +0000
Adopt the guts of FreeBSD's random(3) implementation for a "testable"
random bit supply.
Initialize all usual random sources (random(3), rand48(3) and
VRND_RandomTestable[Double]() with kernel entropy on startup.
Use VRND_RandomCrypto() (= /dev/urandom) for security relevant tasks.
Make the director.random and std.random use VRND_RandomTestable(),
add suitable references and warnings in their docs.
Fix the two testcases which need a testable random generator.
diff --git a/bin/varnishd/cache/cache_main.c b/bin/varnishd/cache/cache_main.c
index 19b519d..6e67968 100644
--- a/bin/varnishd/cache/cache_main.c
+++ b/bin/varnishd/cache/cache_main.c
@@ -168,17 +168,16 @@ cli_debug_xid(struct cli *cli, const char * const *av, void *priv)
* Default to seed=1, this is the only seed value POSIXl guarantees will
* result in a reproducible random number sequence.
*/
-static void
+static void __match_proto__(cli_func_t)
cli_debug_srandom(struct cli *cli, const char * const *av, void *priv)
{
- (void)priv;
unsigned seed = 1;
+ (void)priv;
+ (void)cli;
if (av[2] != NULL)
seed = strtoul(av[2], NULL, 0);
- srandom(seed);
- srand48(random());
- VCLI_Out(cli, "Random(3) seeded with %u", seed);
+ VRND_SeedTestable(seed);
}
static struct cli_proto debug_cmds[] = {
@@ -255,8 +254,8 @@ child_main(void)
BAN_Compile();
- VRND_Seed();
- srand48(random());
+ VRND_SeedAll();
+
CLI_AddFuncs(debug_cmds);
/* Wait for persistent storage to load if asked to */
diff --git a/bin/varnishd/mgt/mgt_cli.c b/bin/varnishd/mgt/mgt_cli.c
index 9edac1d..b16597d 100644
--- a/bin/varnishd/mgt/mgt_cli.c
+++ b/bin/varnishd/mgt/mgt_cli.c
@@ -241,10 +241,13 @@ static void
mgt_cli_challenge(struct cli *cli)
{
int i;
+ uint8_t u;
- AZ(VRND_CryptoQuality(cli->challenge, sizeof cli->challenge - 2));
- for (i = 0; i < sizeof cli->challenge - 2; i++)
- cli->challenge[i] = (cli->challenge[i] % 26) + 'a';
+ AZ(VRND_RandomCrypto(cli->challenge, sizeof cli->challenge - 2));
+ for (i = 0; i < (sizeof cli->challenge) - 2; i++) {
+ AZ(VRND_RandomCrypto(&u, sizeof u));
+ cli->challenge[i] = (u % 26) + 'a';
+ }
cli->challenge[i++] = '\n';
cli->challenge[i] = '\0';
VCLI_Out(cli, "%s", cli->challenge);
diff --git a/bin/varnishd/mgt/mgt_main.c b/bin/varnishd/mgt/mgt_main.c
index 4b33358..97a814f 100644
--- a/bin/varnishd/mgt/mgt_main.c
+++ b/bin/varnishd/mgt/mgt_main.c
@@ -406,7 +406,7 @@ make_secret(const char *dirname)
dirname, strerror(errno));
for (i = 0; i < 256; i++) {
- AZ(VRND_CryptoQuality(&b, 1));
+ AZ(VRND_RandomCrypto(&b, 1));
assert(1 == write(fdo, &b, 1));
}
AZ(close(fdo));
@@ -523,7 +523,7 @@ main(int argc, char * const *argv)
*/
VSUB_closefrom(STDERR_FILENO + 1);
- VRND_Seed();
+ VRND_SeedAll();
mgt_got_fd(STDERR_FILENO);
diff --git a/bin/varnishtest/tests/d00004.vtc b/bin/varnishtest/tests/d00004.vtc
index 82b35b5..32506c4 100644
--- a/bin/varnishtest/tests/d00004.vtc
+++ b/bin/varnishtest/tests/d00004.vtc
@@ -1,7 +1,5 @@
varnishtest "Deeper test of random director"
-random
-
server s1 {
rxreq
txresp -body "1"
@@ -21,15 +19,15 @@ server s2 {
server s3 {
rxreq
txresp -body "333"
-} -start
-
-server s4 {
rxreq
- txresp -body "4444"
+ txresp -body "333"
rxreq
- txresp -body "4444"
+ txresp -body "333"
rxreq
- txresp -body "4444"
+ txresp -body "333"
+} -start
+
+server s4 {
rxreq
txresp -body "4444"
} -start
@@ -54,10 +52,7 @@ varnish v1 -vcl+backend {
}
} -start
-# NB: Do not change the number 1
-# NB: Only srandom(1) is standardized as deterministic.
-
-varnish v1 -cliok "debug.srandom 1"
+varnish v1 -cliok "debug.srandom"
client c1 {
txreq
@@ -65,29 +60,29 @@ client c1 {
expect resp.bodylen == 2
txreq
rxresp
- expect resp.bodylen == 4
+ expect resp.bodylen == 2
txreq
rxresp
- expect resp.bodylen == 4
+ expect resp.bodylen == 1
txreq
rxresp
- expect resp.bodylen == 4
+ expect resp.bodylen == 2
txreq
rxresp
- expect resp.bodylen == 1
+ expect resp.bodylen == 3
txreq
rxresp
expect resp.bodylen == 2
txreq
rxresp
- expect resp.bodylen == 4
+ expect resp.bodylen == 3
txreq
rxresp
- expect resp.bodylen == 2
+ expect resp.bodylen == 4
txreq
rxresp
expect resp.bodylen == 3
txreq
rxresp
- expect resp.bodylen == 2
+ expect resp.bodylen == 3
} -run
diff --git a/bin/varnishtest/tests/m00002.vtc b/bin/varnishtest/tests/m00002.vtc
index 9223f0d..0f5ade5 100644
--- a/bin/varnishtest/tests/m00002.vtc
+++ b/bin/varnishtest/tests/m00002.vtc
@@ -1,8 +1,5 @@
varnishtest "Test std.random()"
-# needs random generator
-random
-
server s1 {
rxreq
txresp
@@ -19,15 +16,13 @@ varnish v1 -vcl+backend {
}
} -start
-# NB: Do not change the number 1
-# NB: Only srandom(1) is standardized as deterministic.
-varnish v1 -cliok "debug.srandom 1"
+varnish v1 -cliok "debug.srandom"
client c1 {
txreq
rxresp
- expect resp.http.rnd1 == 0.388
- expect resp.http.rnd2 == 7.478
- expect resp.http.rnd3 == 9.034
- expect resp.http.rnd4 == 99.502
+ expect resp.http.rnd1 == 0.256
+ expect resp.http.rnd2 == 1.643
+ expect resp.http.rnd3 == 9.011
+ expect resp.http.rnd4 == 99.654
} -run
diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c
index 55fa0f3..11ab41d 100644
--- a/bin/varnishtest/vtc.c
+++ b/bin/varnishtest/vtc.c
@@ -530,57 +530,6 @@ cmd_delay(CMD_ARGS)
VTIM_sleep(f);
}
-/**********************************************************************
- * Check random generator
- */
-
-#define NRNDEXPECT 12
-static const unsigned long random_expect[NRNDEXPECT] = {
- 1804289383, 846930886, 1681692777, 1714636915,
- 1957747793, 424238335, 719885386, 1649760492,
- 596516649, 1189641421, 1025202362, 1350490027
-};
-
-#define RND_NEXT_1K 0x3bdcbe30
-
-/* SECTION: random random
- *
- * Check the random number generator.
- */
-
-static void
-cmd_random(CMD_ARGS)
-{
- uint32_t l;
- int i;
-
- (void)cmd;
- (void)priv;
- if (av == NULL)
- return;
- srandom(1);
- for (i = 0; i < NRNDEXPECT; i++) {
- l = random();
- if (l == random_expect[i])
- continue;
- vtc_log(vl, 4, "random[%d] = 0x%x (expect 0x%lx)",
- i, l, random_expect[i]);
- vtc_log(vl, 1, "SKIPPING test: unknown srandom(1) sequence.");
- vtc_stop = 1;
- break;
- }
- l = 0;
- for (i = 0; i < 1000; i++)
- l += random();
- if (l != RND_NEXT_1K) {
- vtc_log(vl, 4, "sum(random[%d...%d]) = 0x%x (expect 0x%x)",
- NRNDEXPECT, NRNDEXPECT + 1000,
- l, RND_NEXT_1K);
- vtc_log(vl, 1, "SKIPPING test: unknown srandom(1) sequence.");
- vtc_stop = 1;
- }
-}
-
/* SECTION: feature feature
*
* Test that the required feature(s) for a test are available, and skip the test
@@ -685,7 +634,6 @@ static const struct cmds cmds[] = {
CMD(shell),
CMD(err_shell),
CMD(barrier),
- CMD(random),
CMD(feature),
CMD(logexpect),
CMD(process),
diff --git a/bin/varnishtest/vtc_main.c b/bin/varnishtest/vtc_main.c
index 48f2a71..90f5b4f 100644
--- a/bin/varnishtest/vtc_main.c
+++ b/bin/varnishtest/vtc_main.c
@@ -252,7 +252,7 @@ start_test(void)
assert(jp->buf != MAP_FAILED);
memset(jp->buf, 0, jp->bufsiz);
- VRND_Seed();
+ VRND_SeedAll();
bprintf(tmpdir, "%s/vtc.%d.%08x", tmppath, (int)getpid(),
(unsigned)random());
AZ(mkdir(tmpdir, 0711));
diff --git a/include/vrnd.h b/include/vrnd.h
index 81cac18..6a89762 100644
--- a/include/vrnd.h
+++ b/include/vrnd.h
@@ -28,5 +28,9 @@
* Random functions
*/
-int VRND_CryptoQuality(void *, size_t);
-void VRND_Seed(void); /* Seed random(3) properly */
+int VRND_RandomCrypto(void *, size_t);
+long VRND_RandomTestable(void);
+double VRND_RandomTestableDouble(void);
+void VRND_SeedTestable(unsigned int);
+void VRND_SeedAll(void); /* Seed random(3) properly */
+
diff --git a/lib/libvarnish/vrnd.c b/lib/libvarnish/vrnd.c
index fef31de..c55d73e 100644
--- a/lib/libvarnish/vrnd.c
+++ b/lib/libvarnish/vrnd.c
@@ -1,6 +1,7 @@
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2011 Varnish Software AS
+ * Copyright (c) 1983, 1993 The Regents of the University of California.
* All rights reserved.
*
* Author: Dag-Erling Smørgrav <des at des.no>
@@ -25,11 +26,15 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
+ *
+ * Partially from: $FreeBSD: head/lib/libc/stdlib/random.c 303342
+ *
*/
#include "config.h"
#include <fcntl.h>
+#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -37,11 +42,108 @@
#include "vas.h"
#include "vrnd.h"
-#include "vtim.h"
-#include "vsha256.h"
+
+/**********************************************************************
+ * Stripped down random(3) implementation from FreeBSD, to provide
+ * predicatable "random" numbers of testing purposes.
+ */
+
+#define TYPE_3 3 /* x**31 + x**3 + 1 */
+#define DEG_3 31
+#define SEP_3 3
+
+static uint32_t randtbl[DEG_3 + 1] = {
+ TYPE_3,
+ 0x2cf41758, 0x27bb3711, 0x4916d4d1, 0x7b02f59f, 0x9b8e28eb, 0xc0e80269,
+ 0x696f5c16, 0x878f1ff5, 0x52d9c07f, 0x916a06cd, 0xb50b3a20, 0x2776970a,
+ 0xee4eb2a6, 0xe94640ec, 0xb1d65612, 0x9d1ed968, 0x1043f6b7, 0xa3432a76,
+ 0x17eacbb9, 0x3c09e2eb, 0x4f8c2b3, 0x708a1f57, 0xee341814, 0x95d0e4d2,
+ 0xb06f216c, 0x8bd2e72e, 0x8f7c38d7, 0xcfc6a8fc, 0x2a59495, 0xa20d2a69,
+ 0xe29d12d1
+};
+
+static uint32_t *fptr = &randtbl[SEP_3 + 1];
+static uint32_t *rptr = &randtbl[1];
+
+static uint32_t * const state = &randtbl[1];
+static const int rand_deg = DEG_3;
+static const int rand_sep = SEP_3;
+static const uint32_t * const end_ptr = &randtbl[DEG_3 + 1];
+
+static inline uint32_t
+good_rand(uint32_t ctx)
+{
+/*
+ * Compute x = (7^5 * x) mod (2^31 - 1)
+ * wihout overflowing 31 bits:
+ * (2^31 - 1) = 127773 * (7^5) + 2836
+ * From "Random number generators: good ones are hard to find",
+ * Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ * October 1988, p. 1195.
+ */
+ int32_t hi, lo, x;
+
+ /* Transform to [1, 0x7ffffffe] range. */
+ x = (ctx % 0x7ffffffe) + 1;
+ hi = x / 127773;
+ lo = x % 127773;
+ x = 16807 * lo - 2836 * hi;
+ if (x < 0)
+ x += 0x7fffffff;
+ /* Transform to [0, 0x7ffffffd] range. */
+ return (x - 1);
+}
+
+void
+VRND_SeedTestable(unsigned int x)
+{
+ int i, lim;
+
+ state[0] = (uint32_t)x;
+ for (i = 1; i < rand_deg; i++)
+ state[i] = good_rand(state[i - 1]);
+ fptr = &state[rand_sep];
+ rptr = &state[0];
+ lim = 10 * rand_deg;
+ for (i = 0; i < lim; i++)
+ (void)random();
+}
+
+long
+VRND_RandomTestable(void)
+{
+ uint32_t i;
+ uint32_t *f, *r;
+
+ /*
+ * Use local variables rather than static variables for speed.
+ */
+ f = fptr; r = rptr;
+ *f += *r;
+ i = *f >> 1; /* chucking least random bit */
+ if (++f >= end_ptr) {
+ f = state;
+ ++r;
+ }
+ else if (++r >= end_ptr) {
+ r = state;
+ }
+
+ fptr = f; rptr = r;
+ return ((long)i);
+}
+
+double
+VRND_RandomTestableDouble(void)
+{
+ return (
+ ldexp((double)VRND_RandomTestable(), -31) +
+ ldexp((double)VRND_RandomTestable(), -62)
+ );
+}
int
-VRND_CryptoQuality(void *ptr, size_t len)
+VRND_RandomCrypto(void *ptr, size_t len)
{
int fd;
char *p;
@@ -61,10 +163,14 @@ VRND_CryptoQuality(void *ptr, size_t len)
}
void
-VRND_Seed(void)
+VRND_SeedAll(void)
{
unsigned long seed;
- AZ(VRND_CryptoQuality(&seed, sizeof seed));
+ AZ(VRND_RandomCrypto(&seed, sizeof seed));
srandom(seed);
+ AZ(VRND_RandomCrypto(&seed, sizeof seed));
+ VRND_SeedTestable(seed);
+ AZ(VRND_RandomCrypto(&seed, sizeof seed));
+ srand48(seed);
}
diff --git a/lib/libvmod_directors/random.c b/lib/libvmod_directors/random.c
index dee16ff..679fe2e 100644
--- a/lib/libvmod_directors/random.c
+++ b/lib/libvmod_directors/random.c
@@ -34,8 +34,9 @@
#include "cache/cache.h"
#include "cache/cache_director.h"
-#include "vrt.h"
#include "vbm.h"
+#include "vrnd.h"
+#include "vrt.h"
#include "vdir.h"
@@ -69,7 +70,7 @@ vmod_random_resolve(const struct director *dir, struct worker *wrk,
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
CAST_OBJ_NOTNULL(rr, dir->priv, VMOD_DIRECTORS_RANDOM_MAGIC);
- r = scalbn(random(), -31);
+ r = scalbn(VRND_RandomTestable(), -31);
assert(r >= 0 && r < 1.0);
be = vdir_pick_be(rr->vd, r, bo);
return (be);
diff --git a/lib/libvmod_directors/vmod.vcc b/lib/libvmod_directors/vmod.vcc
index 529db05..4adbfba 100644
--- a/lib/libvmod_directors/vmod.vcc
+++ b/lib/libvmod_directors/vmod.vcc
@@ -134,6 +134,8 @@ Description
The random director distributes load over the backends using
a weighted random probability distribution.
+ The "testable" random generator in varnishd is used, which
+ enables deterministic tests to be run (See: d00004.vtc).
Example
new vdir = directors.random();
diff --git a/lib/libvmod_std/vmod.vcc b/lib/libvmod_std/vmod.vcc
index 6b5683a..7808e14 100644
--- a/lib/libvmod_std/vmod.vcc
+++ b/lib/libvmod_std/vmod.vcc
@@ -72,6 +72,9 @@ $Function REAL random(REAL lo, REAL hi)
Description
Returns a random real number between *lo* and *hi*.
+ This function uses the "testable" random generator in varnishd
+ which enables determinstic tests to be run (See m00002.vtc).
+ This function should not be used for cryptographic applications.
Example
set beresp.http.random-number = std.random(1, 100);
diff --git a/lib/libvmod_std/vmod_std.c b/lib/libvmod_std/vmod_std.c
index ed66405..7f0b35d 100644
--- a/lib/libvmod_std/vmod_std.c
+++ b/lib/libvmod_std/vmod_std.c
@@ -37,6 +37,7 @@
#include <stdlib.h>
#include <syslog.h>
+#include "vrnd.h"
#include "vrt.h"
#include "vtcp.h"
#include "vsa.h"
@@ -125,7 +126,7 @@ vmod_random(VRT_CTX, VCL_REAL lo, VCL_REAL hi)
double a;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
- a = drand48();
+ a = VRND_RandomTestableDouble();
a *= hi - lo;
a += lo;
return (a);
More information about the varnish-commit
mailing list