From 1d4b6d489cb919faa3ad67a3ae53fe26c4cd0a75 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Thu, 20 Jun 2024 10:32:29 -0400 Subject: [PATCH 25/31] MdePkg/BaseRngLib: Add a smoketest for RDRAND and check CPUID RH-Author: Jon Maloy RH-MergeRequest: 77: UINT32 overflow in S3 ResumeCount and Pixiefail fixes RH-Jira: RHEL-21854 RHEL-21856 RHEL-40099 RH-Acked-by: Gerd Hoffmann RH-Commit: [25/31] 11804d6f86a644ae2c3dcad89c633ad63b794d3f JIRA: https://issues.redhat.com/browse/RHEL-21856 Upstream: Merged CVE: CVE-2023-45237 commit c3a8ca7b54a9fd17acdf16c6282a92cc989fa92a Author: Pedro Falcato Date: Tue Nov 22 22:31:03 2022 +0000 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 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 Cc: Michael D Kinney Cc: Liming Gao Cc: Zhiguang Liu Cc: Jason A. Donenfeld Signed-off-by: Jon Maloy --- MdePkg/Library/BaseRngLib/Rand/RdRand.c | 99 +++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 8 deletions(-) diff --git a/MdePkg/Library/BaseRngLib/Rand/RdRand.c b/MdePkg/Library/BaseRngLib/Rand/RdRand.c index aee8ea04e8..7132ab0efd 100644 --- a/MdePkg/Library/BaseRngLib/Rand/RdRand.c +++ b/MdePkg/Library/BaseRngLib/Rand/RdRand.c @@ -3,6 +3,7 @@ to provide high-quality random numbers. Copyright (c) 2023, Arm Limited. All rights reserved.
+Copyright (c) 2022, Pedro Falcato. All rights reserved.
Copyright (c) 2021, NUVIA Inc. All rights reserved.
Copyright (c) 2015, Intel Corporation. All rights reserved.
@@ -25,6 +26,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. @@ -49,10 +132,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; } @@ -71,6 +157,7 @@ ArchGetRandomNumber16 ( OUT UINT16 *Rand ) { + ASSERT (mRdRandSupported); return AsmRdRand16 (Rand); } @@ -89,6 +176,7 @@ ArchGetRandomNumber32 ( OUT UINT32 *Rand ) { + ASSERT (mRdRandSupported); return AsmRdRand32 (Rand); } @@ -107,6 +195,7 @@ ArchGetRandomNumber64 ( OUT UINT64 *Rand ) { + ASSERT (mRdRandSupported); return AsmRdRand64 (Rand); } @@ -123,13 +212,7 @@ ArchIsRngSupported ( VOID ) { - /* - Existing software depends on this always returning TRUE, so for - now hard-code it. - - return mRdRandSupported; - */ - return TRUE; + return mRdRandSupported; } /** -- 2.39.3