[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