Skip to content

Commit c6e169b

Browse files
committed
random32: add a selftest for the prandom32 code
Given that this code is new, let's add a selftest for it as well. It doesn't rely on fixed sets, instead it picks 1024 numbers and verifies that they're not more correlated than desired. Link: https://lore.kernel.org/netdev/20200808152628.GA27941@SDF.ORG/ Cc: George Spelvin <lkml@sdf.org> Cc: Amit Klein <aksecurity@gmail.com> Cc: Eric Dumazet <edumazet@google.com> Cc: "Jason A. Donenfeld" <Jason@zx2c4.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Kees Cook <keescook@chromium.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: tytso@mit.edu Cc: Florian Westphal <fw@strlen.de> Cc: Marc Plumb <lkml.mplumb@gmail.com> Signed-off-by: Willy Tarreau <w@1wt.eu>
1 parent 3744741 commit c6e169b

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

lib/random32.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <linux/jiffies.h>
3939
#include <linux/random.h>
4040
#include <linux/sched.h>
41+
#include <linux/bitops.h>
4142
#include <asm/unaligned.h>
4243
#include <trace/events/random.h>
4344

@@ -556,6 +557,61 @@ static void prandom_timer_start(struct random_ready_callback *unused)
556557
mod_timer(&seed_timer, jiffies);
557558
}
558559

560+
#ifdef CONFIG_RANDOM32_SELFTEST
561+
/* Principle: True 32-bit random numbers will all have 16 differing bits on
562+
* average. For each 32-bit number, there are 601M numbers differing by 16
563+
* bits, and 89% of the numbers differ by at least 12 bits. Note that more
564+
* than 16 differing bits also implies a correlation with inverted bits. Thus
565+
* we take 1024 random numbers and compare each of them to the other ones,
566+
* counting the deviation of correlated bits to 16. Constants report 32,
567+
* counters 32-log2(TEST_SIZE), and pure randoms, around 6 or lower. With the
568+
* u32 total, TEST_SIZE may be as large as 4096 samples.
569+
*/
570+
#define TEST_SIZE 1024
571+
static int __init prandom32_state_selftest(void)
572+
{
573+
unsigned int x, y, bits, samples;
574+
u32 xor, flip;
575+
u32 total;
576+
u32 *data;
577+
578+
data = kmalloc(sizeof(*data) * TEST_SIZE, GFP_KERNEL);
579+
if (!data)
580+
return 0;
581+
582+
for (samples = 0; samples < TEST_SIZE; samples++)
583+
data[samples] = prandom_u32();
584+
585+
flip = total = 0;
586+
for (x = 0; x < samples; x++) {
587+
for (y = 0; y < samples; y++) {
588+
if (x == y)
589+
continue;
590+
xor = data[x] ^ data[y];
591+
flip |= xor;
592+
bits = hweight32(xor);
593+
total += (bits - 16) * (bits - 16);
594+
}
595+
}
596+
597+
/* We'll return the average deviation as 2*sqrt(corr/samples), which
598+
* is also sqrt(4*corr/samples) which provides a better resolution.
599+
*/
600+
bits = int_sqrt(total / (samples * (samples - 1)) * 4);
601+
if (bits > 6)
602+
pr_warn("prandom32: self test failed (at least %u bits"
603+
" correlated, fixed_mask=%#x fixed_value=%#x\n",
604+
bits, ~flip, data[0] & ~flip);
605+
else
606+
pr_info("prandom32: self test passed (less than %u bits"
607+
" correlated)\n",
608+
bits+1);
609+
kfree(data);
610+
return 0;
611+
}
612+
core_initcall(prandom32_state_selftest);
613+
#endif /* CONFIG_RANDOM32_SELFTEST */
614+
559615
/*
560616
* Start periodic full reseeding as soon as strong
561617
* random numbers are available.

0 commit comments

Comments
 (0)