tech-crypto archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: x86 CPU RNG support, take 2



   Date: Fri, 25 Dec 2015 18:27:50 -0500
   From: Thor Lancelot Simon <tls%panix.com@localhost>

   I've cleaned it up a little and hooked it up as a standard entropy source
   per Taylor's comments.  To avoid a pile of largely pointless config glue,
   it is an internal source in kern_rndq.c just like the "callout" source.
   I think this can probably be used for the onboard RNG on other CPUs as well.

This seems to have several functional changes in it:

- Add cpu_rng(9) and its rndsource.
- Implement cpu_rng(9) on x86 using RDRAND/RDSEED for Intel CPUs.
- Convert VIA RNG to cpu_rng(9).

Please split them into separate commits, and separate patches for
review.  Here are some comments on a first round of review pending
that split:

   --- arch/amd64/include/types.h  27 Aug 2015 12:30:50 -0000      1.48
   +++ arch/amd64/include/types.h  19 Dec 2015 21:39:37 -0000
   @@ -93,6 +93,7 @@ typedef       unsigned char           __cpu_simple_lock
    #define        __HAVE_TLS_VARIANT_II
    #define        __HAVE_COMMON___TLS_GET_ADDR
    #define        __HAVE_INTR_CONTROL
   +#define __HAVE_CPU_RNG

KNF: tab, not space, like the rest.

   --- arch/i386/include/types.h   27 Aug 2015 12:30:51 -0000      1.83
   +++ arch/i386/include/types.h   19 Dec 2015 21:40:33 -0000
   @@ -109,6 +109,8 @@ typedef     unsigned char           __cpu_simple_lock
    #define        __HAVE_SYSCALL_INTERN
    #define        __HAVE_MINIMAL_EMUL
    #define        __HAVE_OLD_DISKLABEL
   +#define __HAVE_CPU_RNG

Same.

   --- /dev/null   1 Jan 1970 00:00:00 -0000
   +++ arch/x86/include/cpu_rng.h  25 Dec 2015 22:57:30 -0000
   @@ -0,0 +1,49 @@
   +/* $NetBSD: $ */

$NetBSD$

   +#ifndef _X86_CPURNG_H_
   +#define _X86_CPURNG_H_

File name has an underscore in it.

   +#include <sys/param.h>
   +#include <sys/systm.h>
   +#include <sys/cpu.h>
   +
   +#include <x86/specialreg.h>
   +
   +#include <machine/cpufunc.h>
   +#include <machine/cpuvar.h>

Omit needless includes.  All you need is <sys/types.h> for uint64_t
and size_t.

   +typedef uint64_t cpu_rng_t;
   +
   +void cpu_rng_init(void);
   +size_t cpu_rng(cpu_rng_t *);
   +
   +#endif

Note /* _X86_CPU_RNG_H_ */ after #endif.

   --- arch/x86/include/via_padlock.h      13 Apr 2015 16:03:51 -0000      1.8
   +++ arch/x86/include/via_padlock.h      25 Dec 2015 22:10:56 -0000
   @@ -74,8 +69,6 @@ struct via_padlock_softc {
    #define VIAC3_SESSION(sid)     ((sid) & 0x0fffffff)
    #define VIAC3_SID(crd,ses)     (((crd) << 28) | ((ses) & 0x0fffffff))

   -#define VIAC3_RNG_BUFSIZ       16

Should comment somewhere (in the commit message, perhaps) why it is OK
to switch from a 16-byte buffer to 4-byte reads at a time.

   --- /dev/null   1 Jan 1970 00:00:00 -0000
   +++ arch/x86/x86/cpu_rng.c      25 Dec 2015 22:58:39 -0000
   @@ -0,0 +1,166 @@
   +/* $NetBSD: $ */

$NetBSD$

   @@ -0,0 +1,166 @@
   ...
   +static enum { CPU_RNG_NONE = 0,
   +             CPU_RNG_RDRAND,
   +             CPU_RNG_RDSEED,
   +             CPU_RNG_VIA } cpu_rng_mode = CPU_RNG_NONE;

KNF and __read_mostly:

static enum {
	CPU_RNG_NONE = 0,
	CPU_RNG_RDRAND,
	CPU_RNG_RDSEED,
	CPU_RNG_VIA
} cpu_rng_mode __read_mostly = CPU_RNG_NONE;

   +void
   +cpu_rng_init(void)
   +{
   +       if (cpu_feature[5] & CPUID_SEF_RDSEED) {

KNF: blank line between declarations and statements in function body.

   +               cpu_rng_mode = CPU_RNG_RDSEED;
   +               aprint_normal("cpu_rng: RDSEED\n");
   +       } else
   +
   +       if (cpu_feature[1] & CPUID2_RDRAND) {

KNF: no blank line between `else' and `if'.

   +static inline size_t
   +cpu_rng_rdrand(cpu_rng_t *out)
   +{
   +       uint8_t rndsts;
   +#ifndef __x86_64__

Write positive conditionals, not negative ones:

#if defined(__i386__)
...
#elif defined(__x86_64__)
...
#else
#error Unknown x86 variant.
#endif

   +       uint32_t outword[2] = out;
   +       int i;
   +
   +       for (i = 0; i < 2; i++) {
   +               __asm __volatile("rdrand %0; setc %1":"=r"(outword + i),
   +                                "=qm"(rndsts));
   +               if (rndsts != 1) return 0;
   +       }

Did you test-compile this?  The initialization of outword makes no
sense.  I think you meant this:

	uint32_t outword[2];
	unsigned i;

	for (i = 0; i < 2; i++) {
		__asm __volatile("rdrand %0; setc %1" : "=r"(outword[i]),
		    "=qm"(rndsts));
		if (rndsts != 1)
			return 0;
	}

	*out = outword[0] | ((uint64_t)outword[1] << 32);

It is tempting to write

	uint32_t *outword = (uint32_t *)out;

and to avoid copying from the local buffer -- but that is incorrect
because it violates the strict-aliasing rules.

   +#else
   +       __asm __volatile("rdrand %0; setc %1":"=r"(out),
   +                        "=qm"(rndsts));

I assume you meant `*out' instead of `out' here.  Otherwise you write
to the pointer itself, not to the memory that the pointer points to.

This is an example of an implementation bug that a generic statistical
test likely *will* catch, if you end up reading uninitialized garbage
off the stack -- such as the statistical tests that the rndsource code
will automatically apply.  (So this bug is an argument in favour of
not bypassing rndsource.)

KNF: continuation line gets four spaces (or just place on same line --
it's short enough).  I'd also put spaces around the : so it doesn't
get visually eaten by the quoted strings.

   +       if (rndsts != 1) return 0;

KNF: line break after `if (...)'.

   +size_t
   +cpu_rng(cpu_rng_t *out)
   +{
   +       switch (cpu_rng_mode) {
   +           case CPU_RNG_NONE:

KNF: indent the switch, don't indent the case.

   --- arch/x86/x86/identcpu.c     13 Dec 2015 15:02:19 -0000      1.49
   +++ arch/x86/x86/identcpu.c     25 Dec 2015 22:35:37 -0000
   @@ -554,8 +554,15 @@ cpu_probe_c3(struct cpu_info *ci)
                       /* Actually do the enables. */
                       if (rng_enable) {
                           msr = rdmsr(MSR_VIA_RNG);
   -                       wrmsr(MSR_VIA_RNG, msr | MSR_VIA_RNG_ENABLE);
   +                       /* C7 stepping 8 and subsequent CPUs have dual RNG */
   +                       if (model > 0xA || (model == 0xA && stepping > 0x7)) {
   +                               wrmsr(MSR_VIA_RNG, msr | MSR_VIA_RNG_ENABLE |
   +                                                  MSR_VIA_RNG_2NOISE);
   +                       } else {
   +                               wrmsr(MSR_VIA_RNG, msr | MSR_VIA_RNG_ENABLE);
   +                       }
                       }

Simplify:

	msr = readmsr(MSR_VIA_RNG);
	msr |= MSR_VIA_RNG_ENABLE;
	if (model > 0xA || (model == 0xA && stepping > 0x7))
		msr |= MSR_VIA_RNG_2NOISE;
	wrmsr(MSR_VIA_RNG, msr);

What does MSR_VIA_RNG_2NOISE actually do?  Can you add a documentation
citation?

   --- kern/kern_rndq.c    29 Aug 2015 10:00:19 -0000      1.73
   +++ kern/kern_rndq.c    25 Dec 2015 20:04:08 -0000
   @@ -404,6 +408,31 @@ rnd_dv_estimate(krndsource_t *rs, uint32
           return ret;
    }

   +#if defined(__HAVE_CPU_RNG)
   +krndsource_t rnd_cpu_source;
   +
   +static void
   +rnd_cpu_get(size_t bytes, void *priv)
   +{
   +        krndsource_t *cpusrcp = priv;

KNF: Tabs, not spaces, for indentation.

   +       size_t entropy = 0, cnt  = RND_POOLBITS / 2 / NBBY / sizeof(cpu_rng_t);
   +       cpu_rng_t buf[cnt];

This isn't a variable-length array, but it looks like one at first
glance.  I would really prefer to avoid this.  I'd also be a bit more
comfortable writing the divisions as howmany: howmany(RND_POOLBITS/2,
NBBY*sizeof(cpu_rng_t)).  Why /2?  Why RND_POOLBITS and not, e.g., 256
or 512?

   +#if defined(__HAVE_CPU_RNG)
   +       {
   +               cpu_rng_t test;
   +
   +               cpu_rng_init();
   +               if (cpu_rng(&test)) {
   +                       rndsource_setcb(&rnd_cpu_source, rnd_cpu_get,
   +                                       &rnd_cpu_source);
   +                       rnd_attach_source(&rnd_cpu_source, "cpurng",
   +                                         RND_TYPE_RNG,
   +                                         RND_FLAG_COLLECT_VALUE|
   +                                         RND_FLAG_HASCB|RND_FLAG_HASENABLE);

Why attach only if it immediately works?  A transient failure here
seems plausible enough to me, and there's no harm if it fails later.

KNF: four-space continuation lines.


Home | Main Index | Thread Index | Old Index