MdePkg/BaseRngLib: Add a smoketest for RDRAND and check CPUID

RDRAND has notoriously been broken many times over its lifespan.
Add a smoketest to RDRAND, in order to better sniff out potential
security concerns.

Also add a proper CPUID test in order to support older CPUs which may
not have it; it was previously being tested but then promptly ignored.

Testing algorithm inspired by linux's arch/x86/kernel/cpu/rdrand.c
:x86_init_rdrand() per commit 049f9ae9..

Many thanks to Jason Donenfeld for relicensing his linux RDRAND detection
code to MIT and the public domain.

>On Tue, Nov 22, 2022 at 2:21 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
  <..>
>    I (re)wrote that function in Linux. I hereby relicense it as MIT, and
>    also place it into public domain. Do with it what you will now.
>
>    Jason

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4163

Signed-off-by: Pedro Falcato <pedro.falcato@gmail.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Pedro Falcato 2022-11-22 22:31:03 +00:00 committed by Mike Beaton
parent 9801a26e6e
commit 6ca9334dc8
1 changed files with 91 additions and 8 deletions

View File

@ -3,6 +3,7 @@
to provide high-quality random numbers.
Copyright (c) 2023, Arm Limited. All rights reserved.<BR>
Copyright (c) 2022, Pedro Falcato. All rights reserved.<BR>
Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
@ -24,6 +25,88 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
STATIC BOOLEAN mRdRandSupported;
//
// Intel SDM says 10 tries is good enough for reliable RDRAND usage.
//
#define RDRAND_RETRIES 10
#define RDRAND_TEST_SAMPLES 8
#define RDRAND_MIN_CHANGE 5
//
// Add a define for native-word RDRAND, just for the test.
//
#ifdef MDE_CPU_X64
#define ASM_RDRAND AsmRdRand64
#else
#define ASM_RDRAND AsmRdRand32
#endif
/**
Tests RDRAND for broken implementations.
@retval TRUE RDRAND is reliable (and hopefully safe).
@retval FALSE RDRAND is unreliable and should be disabled, despite CPUID.
**/
STATIC
BOOLEAN
TestRdRand (
VOID
)
{
//
// Test for notoriously broken rdrand implementations that always return the same
// value, like the Zen 3 uarch (all-1s) or other several AMD families on suspend/resume (also all-1s).
// Note that this should be expanded to extensively test for other sorts of possible errata.
//
//
// Our algorithm samples rdrand $RDRAND_TEST_SAMPLES times and expects
// a different result $RDRAND_MIN_CHANGE times for reliable RDRAND usage.
//
UINTN Prev;
UINT8 Idx;
UINT8 TestIteration;
UINT32 Changed;
Changed = 0;
for (TestIteration = 0; TestIteration < RDRAND_TEST_SAMPLES; TestIteration++) {
UINTN Sample;
//
// Note: We use a retry loop for rdrand. Normal users get this in BaseRng.c
// Any failure to get a random number will assume RDRAND does not work.
//
for (Idx = 0; Idx < RDRAND_RETRIES; Idx++) {
if (ASM_RDRAND (&Sample)) {
break;
}
}
if (Idx == RDRAND_RETRIES) {
DEBUG ((DEBUG_ERROR, "BaseRngLib/x86: CPU BUG: Failed to get an RDRAND random number - disabling\n"));
return FALSE;
}
if (TestIteration != 0) {
Changed += Sample != Prev;
}
Prev = Sample;
}
if (Changed < RDRAND_MIN_CHANGE) {
DEBUG ((DEBUG_ERROR, "BaseRngLib/x86: CPU BUG: RDRAND not reliable - disabling\n"));
return FALSE;
}
return TRUE;
}
#undef ASM_RDRAND
/**
The constructor function checks whether or not RDRAND instruction is supported
by the host hardware.
@ -48,10 +131,13 @@ BaseRngLibConstructor (
// CPUID. A value of 1 indicates that processor support RDRAND instruction.
//
AsmCpuid (1, 0, 0, &RegEcx, 0);
ASSERT ((RegEcx & RDRAND_MASK) == RDRAND_MASK);
mRdRandSupported = ((RegEcx & RDRAND_MASK) == RDRAND_MASK);
if (mRdRandSupported) {
mRdRandSupported = TestRdRand ();
}
return EFI_SUCCESS;
}
@ -70,6 +156,7 @@ ArchGetRandomNumber16 (
OUT UINT16 *Rand
)
{
ASSERT (mRdRandSupported);
return AsmRdRand16 (Rand);
}
@ -88,6 +175,7 @@ ArchGetRandomNumber32 (
OUT UINT32 *Rand
)
{
ASSERT (mRdRandSupported);
return AsmRdRand32 (Rand);
}
@ -106,6 +194,7 @@ ArchGetRandomNumber64 (
OUT UINT64 *Rand
)
{
ASSERT (mRdRandSupported);
return AsmRdRand64 (Rand);
}
@ -122,13 +211,7 @@ ArchIsRngSupported (
VOID
)
{
/*
Existing software depends on this always returning TRUE, so for
now hard-code it.
return mRdRandSupported;
*/
return TRUE;
}
/**