From cfb1839187d6abdb1e89b50f1970e5829bd3c10a Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Wed, 26 Feb 2020 14:12:08 +0100 Subject: [PATCH 1/4] check rdrand and rdseed return values (CR=1), as recommended by Intel https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide detect and disable bad rdrand on AMD ryzen 3000, which returns all-ones https://arstechnica.com/gadgets/2019/10/how-a-months-old-amd-microcode-bug-destroyed-my-weekend/ --- src/native/entropy_cpu_stubs.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/native/entropy_cpu_stubs.c b/src/native/entropy_cpu_stubs.c index 360efe6b..4b5d1425 100644 --- a/src/native/entropy_cpu_stubs.c +++ b/src/native/entropy_cpu_stubs.c @@ -59,17 +59,27 @@ enum cpu_rng_t { static enum cpu_rng_t __cpu_rng = RNG_NONE; +#define RETRIES 10 + static void detect () { #if defined (__x86__) unsigned int sig, eax, ebx, ecx, edx; int max = __get_cpuid_max (0, &sig); + random_t r = 0; if (max < 1) return; if (sig == signature_INTEL_ebx || sig == signature_AMD_ebx) { __cpuid (1, eax, ebx, ecx, edx); - if (ecx & bit_RDRND) __cpu_rng = RNG_RDRAND; + if (ecx & bit_RDRND) { + /* AMD Ryzen 3000 bug where RDRAND always returns 0xFFFFFFFF */ + for (int i = 0; i < RETRIES; i++) + if (_rdrand_step(&r) == 1 && r != (random_t) (-1)) { + __cpu_rng = RNG_RDRAND; + break; + } + } if (max > 7) { __cpuid_count (7, 0, eax, ebx, ecx, edx); if (ebx & bit_RDSEED) __cpu_rng = RNG_RDSEED; @@ -91,12 +101,14 @@ CAMLprim value caml_cycle_counter (value __unused(unit)) { CAMLprim value caml_cpu_random (value __unused(unit)) { #if defined (__x86__) random_t r = 0; - if (__cpu_rng == RNG_RDSEED) { - _rdseed_step (&r); - } else if (__cpu_rng == RNG_RDRAND) { - _rdrand_step (&r); + for (int i = 0; i < RETRIES; i++) { + if (__cpu_rng == RNG_RDSEED) { + if (_rdseed_step (&r) == 1) return Val_long (r); + } else if (__cpu_rng == RNG_RDRAND) { + if (_rdrand_step (&r) == 1) return Val_long (r); + } } - return Val_long (r); /* Zeroed-out if carry == 0. */ + return Val_long (0); #else /* ARM: CPU-assisted randomness here. */ return Val_long (0); From 548dbfb4422e7de45a6c463dcfd80a56d6552749 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Thu, 27 Feb 2020 09:32:18 +0100 Subject: [PATCH 2/4] entropy: test rdseed to not always return -1, as suggested by @cfcs --- src/native/entropy_cpu_stubs.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/native/entropy_cpu_stubs.c b/src/native/entropy_cpu_stubs.c index 4b5d1425..51985440 100644 --- a/src/native/entropy_cpu_stubs.c +++ b/src/native/entropy_cpu_stubs.c @@ -72,17 +72,24 @@ static void detect () { if (sig == signature_INTEL_ebx || sig == signature_AMD_ebx) { __cpuid (1, eax, ebx, ecx, edx); - if (ecx & bit_RDRND) { - /* AMD Ryzen 3000 bug where RDRAND always returns 0xFFFFFFFF */ + if (ecx & bit_RDRND) + /* AMD Ryzen 3000 bug where RDRAND always returns -1 + https://arstechnica.com/gadgets/2019/10/how-a-months-old-amd-microcode-bug-destroyed-my-weekend/ */ for (int i = 0; i < RETRIES; i++) if (_rdrand_step(&r) == 1 && r != (random_t) (-1)) { __cpu_rng = RNG_RDRAND; break; } - } if (max > 7) { __cpuid_count (7, 0, eax, ebx, ecx, edx); - if (ebx & bit_RDSEED) __cpu_rng = RNG_RDSEED; + if (ebx & bit_RDSEED) + /* RDSEED could return -1 as well, thus we test it here as well + https://www.reddit.com/r/Amd/comments/cmza34/agesa_1003_abb_fixes_rdrandrdseed/ */ + for (int i = 0; i < RETRIES; i++) + if (_rdseed_step(&r) == 1 && r != (random_t) (-1)) { + __cpu_rng = RNG_RDSEED; + break; + } } } #endif From 49a5a25a7de124361d06853c82e08708c2c3bc5e Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Thu, 27 Feb 2020 09:54:52 +0100 Subject: [PATCH 3/4] entropy: revert the checking of the return code of rdrand/rdseed for accumulating random bootstrap: use (checked!) rdrand/rdseed multiple times, if not available use whirlwind thrice --- entropy/mirage_crypto_entropy.ml | 28 +++++++++++++------ src/native/entropy_cpu_stubs.c | 47 +++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/entropy/mirage_crypto_entropy.ml b/entropy/mirage_crypto_entropy.ml index 0324eed1..ec75802e 100644 --- a/entropy/mirage_crypto_entropy.ml +++ b/entropy/mirage_crypto_entropy.ml @@ -29,10 +29,11 @@ module Cpu_native = struct - external cycles : unit -> int = "caml_cycle_counter" [@@noalloc] - external random : unit -> int = "caml_cpu_random" [@@noalloc] - external rng_type : unit -> int = "caml_cpu_rng_type" [@@noalloc] - external detect : unit -> unit = "caml_entropy_detect" + external cycles : unit -> int = "caml_cycle_counter" [@@noalloc] + external unchecked_random : unit -> int = "caml_cpu_unchecked_random" [@@noalloc] + external checked_random : unit -> int = "caml_cpu_checked_random" [@@noalloc] + external rng_type : unit -> int = "caml_cpu_rng_type" [@@noalloc] + external detect : unit -> unit = "caml_entropy_detect" let () = detect () @@ -74,7 +75,7 @@ let sources () = * See Whirlwind RNG: * http://www.ieee-security.org/TC/SP2014/papers/Not-So-RandomNumbersinVirtualizedLinuxandtheWhirlwindRNG.pdf *) -let bootstrap f = +let whirlwind_bootstrap f = let outer = 100 and inner_max = 1024 and a = ref 0 @@ -98,7 +99,7 @@ let interrupt_hook () = | Some _ -> let buf = Cstruct.create 12 in fun () -> let a = Cpu_native.cycles () - and b = Cpu_native.random () in + and b = Cpu_native.unchecked_random () in Cstruct.LE.set_uint32 buf 0 (Int32.of_int a) ; Cstruct.LE.set_uint64 buf 4 (Int64.of_int b) ; buf @@ -111,7 +112,18 @@ let interrupt_hook () = * * Compile-time entropy. A function returning it could go into `t.inits`. *) -let bootstrap_functions = [ bootstrap ] + +let checked_rdrand_rdseed f = + let cs = Cstruct.create 8 in + let random = Cpu_native.checked_random () in + Cstruct.LE.set_uint64 cs 0 (Int64.of_int random); + f cs; + Lwt.return_unit + +let bootstrap_functions () = + match Cpu_native.cpu_rng with + | None -> [ whirlwind_bootstrap ; whirlwind_bootstrap ; whirlwind_bootstrap ] + | Some _ -> [ checked_rdrand_rdseed ; checked_rdrand_rdseed ; whirlwind_bootstrap ; checked_rdrand_rdseed ; checked_rdrand_rdseed ] let running = ref false @@ -125,7 +137,7 @@ let initialize (type a) ?g (rng : a Mirage_crypto_rng.generator) = let `Acc handler = Mirage_crypto_rng.accumulate (Some rng) in Lwt_list.iteri_p (fun i boot -> boot (handler ~source:i)) - bootstrap_functions >|= fun () -> + (bootstrap_functions ()) >|= fun () -> let hook = interrupt_hook () in Mirage_runtime.at_enter_iter (fun () -> let e = hook () in diff --git a/src/native/entropy_cpu_stubs.c b/src/native/entropy_cpu_stubs.c index 51985440..f16acd68 100644 --- a/src/native/entropy_cpu_stubs.c +++ b/src/native/entropy_cpu_stubs.c @@ -105,17 +105,50 @@ CAMLprim value caml_cycle_counter (value __unused(unit)) { #endif } -CAMLprim value caml_cpu_random (value __unused(unit)) { +CAMLprim value caml_cpu_checked_random (value __unused(unit)) { #if defined (__x86__) random_t r = 0; - for (int i = 0; i < RETRIES; i++) { - if (__cpu_rng == RNG_RDSEED) { - if (_rdseed_step (&r) == 1) return Val_long (r); - } else if (__cpu_rng == RNG_RDRAND) { - if (_rdrand_step (&r) == 1) return Val_long (r); - } + int ok = 0; + int i = RETRIES; + switch (__cpu_rng) { + case RNG_RDSEED: + do { ok = _rdseed_step (&r); } while ( !(ok | !--i) ); + break; + case RNG_RDRAND: + do { ok = _rdrand_step (&r); } while ( !(ok | !--i) ); + break; + case RNG_NONE: + break; } + return Val_long(r); +#else + /* ARM: CPU-assisted randomness here. */ return Val_long (0); +#endif +} + +CAMLprim value caml_cpu_unchecked_random (value __unused(unit)) { +#if defined (__x86__) + random_t r = 0; + /* rdrand/rdseed may fail (and return CR = 0) if insufficient entropy is + available (or the hardware DRNG is in the middle of reseeding). + + we could handle these by retrying in a loop - which would be + computationally expensive, but since this code is run whenever the Lwt + event loop is entered, and only used to feed entropy into the pool, it is + fine to add not-so-random entropy. + */ + switch (__cpu_rng) { + case RNG_RDSEED: + _rdseed_step (&r); + break; + case RNG_RDRAND: + _rdrand_step (&r); + break; + case RNG_NONE: + break; + } + return Val_long (r); #else /* ARM: CPU-assisted randomness here. */ return Val_long (0); From a3515d19c8dcaa6ee15dad45e9077bf87dcfeec9 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Thu, 27 Feb 2020 10:08:52 +0100 Subject: [PATCH 4/4] entropy: pause after rdseed (as recommended by Intel in 5.3.1) --- src/native/entropy_cpu_stubs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/entropy_cpu_stubs.c b/src/native/entropy_cpu_stubs.c index f16acd68..b6980ff4 100644 --- a/src/native/entropy_cpu_stubs.c +++ b/src/native/entropy_cpu_stubs.c @@ -112,7 +112,7 @@ CAMLprim value caml_cpu_checked_random (value __unused(unit)) { int i = RETRIES; switch (__cpu_rng) { case RNG_RDSEED: - do { ok = _rdseed_step (&r); } while ( !(ok | !--i) ); + do { ok = _rdseed_step (&r); _mm_pause (); } while ( !(ok | !--i) ); break; case RNG_RDRAND: do { ok = _rdrand_step (&r); } while ( !(ok | !--i) );